MAX_CONCURRENT_STREAMS
这个坑排查时间最长。苹果官方文档上没有关于settings的说明,但测试和生产环境中服务器返回的settings是
若本地不限制stream的并发数,则会报错:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class H2Channel extends NioSocketChannel { private val streamCounter = new AtomicInteger(0) override def writeAndFlush(msg: AnyRef): ChannelFuture = { streamCounter.incrementAndGet super.writeAndFlush(msg) } def isAvailable: Boolean = { streamCounter.get < 500 } def decr() = { streamCounter.decrementAndGet } } |
Connection reset by peer
报错信息就是
解决方法有2种,1是代码实现中添加ping frame,在connection reset之前ping一次。示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
@throws(classOf[Exception]) override def userEventTriggered(ctx: ChannelHandlerContext, evt: AnyRef) { evt match { case _: IdleStateEvent => val pingDataBuffer: ByteBuf = ctx.alloc.ioBuffer(8, 8) pingDataBuffer.writeLong(System.currentTimeMillis()) this.encoder.writePing(ctx, false, pingDataBuffer, ctx.newPromise) .addListener(new GenericFutureListener[ChannelFuture]() { @throws(classOf[Exception]) def operationComplete(future: ChannelFuture) { if (future.isSuccess) { pingTimeoutFuture = future.channel.eventLoop.schedule(new Runnable() { def run() { error.error("Closing channel due to ping timeout.") future.channel.close } }, 30, TimeUnit.SECONDS) } else { error.error(s"Failed to write PING frame. ${future.cause().getStackTraceString}") future.channel.close } } }) ctx.flush case _ => } super.userEventTriggered(ctx, evt) } |
第二种方法是修改系统的
1 |
sudo sh -c 'echo 60 >/proc/sys/net/ipv4/tcp_keepalive_time' |
openssl版本过低,不支持alpn
若服务器无法升级到openssl 1.0.2及以上的版本就会出现
发送速度慢,channel频繁超时
建立连接的时候若采用域名,则所有channel的remoteaddress都是同一个ip,会导致发送速度慢,channel频繁超时。可以将连接分散到不同的remoteadress上去:
1 2 3 4 5 |
private val addArray: Array[InetAddress] = InetAddress.getAllByName(PRODUCTION_HOST) private def getAddress: String = { addArray(Random.nextInt(addArray.length - 1)).getHostAddress } |
实测这样做以后,发送速度由2000/s不到提升到最大1.3w/s,且基本上不会出现channel超时的情况。
channel是否要保持不关闭
根据苹果官方文档上所说的,最好保持连接不要关闭,除非是一天发一次这样的频率。
Keep your connections with APNs open across multiple notifications; don’t repeatedly open and close connections. APNs treats rapid connection and disconnection as a denial-of-service attack. You should leave a connection open unless you know it will be idle for an extended period of time—for example, if you only send notifications to your users once a day it is ok to use a new connection each day.
然而实际测试中发现根据域名获取到的最新ip往往发送速度较快。猜测苹果有算法能选出相对比较空闲的服务器。所以这种情况下如果保持连接不断开,下次发送依旧使用这些连接很有可能让请求发送到一台特别繁忙的服务器上去,从而导致发送速度低下。
同样最近在搞apns的http2接口,最近发现有个奇怪的现象
每个进程在传输35w条消息后,效率会大大降低,就像是苹果服务器已经不堪重负,博主有遇到这种情况吗
测过百万级别的消息,没遇到你说的情况。我这边测试的结果是payload越大发送越慢,channel保持时间越长发送越慢。
最近发消息时频繁出现苹果服务器设置max_cocurrent_stream为1的情况,导致单个连接发送变慢
Received settings from APNs gateway: {HEADER_TABLE_SIZE=4096, MAX_CONCURRENT_STREAMS=1, INITIAL_WINDOW_SIZE=65535, MAX_FRAME_SIZE=16384, MAX_HEADER_LIST_SIZE=8000}
很奇怪,之前没遇到这情况,博主没有遇到过?
并且一直收到苹果服务器的goaway frame
最近发送速度降的特别厉害,而且大量的connection reset by peer。我和一些开发者交流后大家都有这个现象,最后觉得应该是奥运期间苹果服务器压力太大导致的。
看了下日志,确实很多MAX_CONCURRENT_STREAMS=1
是的,经你这么一说很有道理,我也是速度下降非常明显,同时大量的goaway
顺带博主可以加个好友吗,我给你邮箱发过邮件的,不过你没回