jvisualvm jconsole 你好 netty! tomcat-NIO2是阻塞写, 如果遇到写繁忙, 则会注册OP_WRITE事件,并且当前线程阻塞select至操作系统返回OP_WRITE事件. 2020/1/3 现象:压测时,客户端一直处于收不到服务端数据的状态。 复现: 并发高的时候,不在eventLoop上执行的时候, DefaultHttpResponse response = new DefaultHttpResponse(); response.set('content-length',50); write(response); write(new DefaultHttpContent(ByteBuff(50))); write(LastHttpContent.EMPTY); 会出现如果存在设置响应体长度,LastHttpContent 正两个 这时候当处理完DefaultHttpResponse,和DefaultHttpContent, 客户端就会进行下一次请求。 DefaultHttpResponse response = new DefaultHttpResponse(); response.set('content-length',50); write(response); write(new DefaultHttpContent(ByteBuff(50))); 与此同时,write(LastHttpContent.EMPTY); 这个写任务还堆积在EventLoop的taskQueue中, 导致HttpObjectEncoder的state属性没有变回ST_INIT。 这时候服务端收到了客户端的读事件,并且没来及处理上一次的写任务 write(LastHttpContent.EMPTY), 就会导致HttpObjectEncoder抛出 unexpected message type: 因为抛出了异常,导致本次的DefaultHttpResponse没有返回给客户端,这时客户端会一直等待服务端回应并阻塞直至超时。 * 在这里可以进行优化操作 * @author wangzihao * 2018/8/25/025 * * 1.优化记录 (2018年8月25日 22:16:13), * 发现问题 : rpc调用不论设置超时时间多长, 总是会超时. * 而且不会因为客户端的连接数增大而改善,反而降低性能, 1个线程=400qps, 100个线程=100-250qps * 而且服务端只要不发生gc,执行时间都是在3纳秒以内 * 超时时间=10秒, 100个客户端线程, 108750次调用, 80次超时 = 0.027%的超时几率, 100-250的qps * * 猜测1 : 估计是客户端的原因, 客户端的执行方法, 有堵塞的调用, 而且堵塞范围过广, 可以从缩小堵塞的影响范围入手 * 猜测2 : 网络丢包, 因为请求后, 没有发现有响应对应的请求id * * 猜测3 : id生成问题, 因为测出以下结果, ID(63604)还未请求 就已经先响应 2018-08-26 00:35:55.359 ERROR 6688 --- [rkerGroup@1-4-1] c.g.n.s.impl.RemoteSessionServiceImpl : 1 save requestTimeout : maxTimeout is [10000] 2018-08-26 00:36:09.878 ERROR 6688 --- [rkerGroup@3-5-1] c.g.n.c.rpc.RpcClient$RpcClientHandler : 超时的响应[] : null :requestId: 63604 status: 200 message: "ok" encode: 1 2018-08-26 00:36:09.879 ERROR 6688 --- [rkerGroup@3-5-1] c.g.n.c.rpc.RpcClient$RpcClientHandler : 超时的响应[] : null :requestId: 63605 status: 200 message: "ok" encode: 1 2018-08-26 00:36:19.878 ERROR 6688 --- [rkerGroup@1-4-2] c.g.n.s.impl.RemoteSessionServiceImpl : 超时的请求 : requestId: 63604 requestMappingName: "/_inner/db" methodName: "get" data: "[\"dc177356b505447cbf3338537a90f27a\"]" 2018-08-26 00:36:19.878 ERROR 6688 --- [rkerGroup@1-4-2] c.g.n.s.impl.RemoteSessionServiceImpl : 2 get requestTimeout : maxTimeout is [10000] 2018-08-26 00:36:19.880 ERROR 6688 --- [rkerGroup@1-4-1] c.g.n.s.impl.RemoteSessionServiceImpl : 超时的请求 : requestId: 63605 requestMappingName: "/_inner/db" methodName: "get" data: "[\"d7cc6c7a8f7f4c9792c56ee8b472ec6d\"]" 猜测4 : 思路 : 1.如果客户端不发送请求, 服务端是不可能响应的 2.从发送请求的地方寻找线索, 3.客户端判断服务端超时的依据是 : 服务端响应后, 如果获取不到该请求的锁, 则判定为超时 4.那么会不会是因为客户端的锁还没放进去, 服务端就响应了 5.于是调换执行顺序, 先放锁, 再发送请求 6.验证成功! 就是这个原因 ( : 看来有时rpc的执行速度比map.put要快) 解决问题 (已解决超时情况): 原因1. : 旧: RpcLock lock = new RpcLock(); getSocketChannel().writeAndFlush(rpcRequest); 这里发送早了,锁还没放进去, 就响应了, 导致响应拿不到锁 requestLockMap.put(requestId,lock); 新: RpcLock lock = new RpcLock(); requestLockMap.put(requestId,lock); 先放锁, 再请求, 问题解决 getSocketChannel().writeAndFlush(rpcRequest); 原因2 : session服务端的jvm内存不足, 导致大量gc, 参数改为后 : -Xms600m -Xmn600m -Xmx1000m 验证 : 测试结果 第(100)次统计, 时间 = 500001毫秒[8分20秒], 总调用次数 = 1435015, 总超时次数 = 0, 成功率 = 100 第(387)次统计, 时间 = 1935001毫秒[32分15秒], 总调用次数 = 4314961, 总超时次数 = 8, 成功率 = 100.00 (注 : 这个是因为session服务端的内存满了) session服务端gc情况 : [Full GC (Allocation Failure) [Tenured: 409600K->409599K(409600K), 1.7072878 secs] 962559K->620546K(962560K), [Metaspace: 9959K->9959K(10624K)], 1.7075061 secs] [Times: user=1.70 sys=0.00, real=1.71 secs] 30分钟, 143万次请求, 431万次rpc调用, 8次rpc超时, 服务端保存143万个会话 配置参数 : getClientEventLoopWorkerCount = 3 getServerEventLoopWorkerCount = 2 getServerEventLoopIoRatio=100 getClientEventLoopIoRatio=100 getSessionClientSocketChannelCount=1 isSessionClientEnableAutoReconnect=true isEnableExecuteHold=false isEnableLog=true RpcDBService.timeout=1000毫秒 新的问题 : qps 还是一直在1000左右, 下面是测试结果 15:07:30.720 [QpsPrintThread] INFO QpsRunningTest$PrintThread - 第(1)次统计, 时间 = 5002毫秒[0分5秒], 成功 = 2843, 失败 = 0, qps = 568.37 15:07:35.720 [QpsPrintThread] INFO QpsRunningTest$PrintThread - 第(2)次统计, 时间 = 10022毫秒[0分10秒], 成功 = 9421, 失败 = 0, qps = 940.03 15:07:40.721 [QpsPrintThread] INFO QpsRunningTest$PrintThread - 第(3)次统计, 时间 = 14723毫秒[0分14秒], 成功 = 15178, 失败 = 0, qps = 1030.90 15:07:45.761 [QpsPrintThread] INFO QpsRunningTest$PrintThread - 第(4)次统计, 时间 = 19463毫秒[0分19秒], 成功 = 21329, 失败 = 0, qps = 1095.87 15:07:55.762 [QpsPrintThread] INFO QpsRunningTest$PrintThread - 第(6)次统计, 时间 = 29164毫秒[0分29秒], 成功 = 32479, 失败 = 0, qps = 1113.67 15:08:00.762 [QpsPrintThread] INFO QpsRunningTest$PrintThread - 第(7)次统计, 时间 = 34164毫秒[0分34秒], 成功 = 39302, 失败 = 0, qps = 1150.39 15:08:20.765 [QpsPrintThread] INFO QpsRunningTest$PrintThread - 第(11)次统计, 时间 = 53267毫秒[0分53秒], 成功 = 62640, 失败 = 0, qps = 1175.96 15:08:25.765 [QpsPrintThread] INFO QpsRunningTest$PrintThread - 第(12)次统计, 时间 = 58267毫秒[0分58秒], 成功 = 67813, 失败 = 0, qps = 1163.83 15:08:55.768 [QpsPrintThread] INFO QpsRunningTest$PrintThread - 第(18)次统计, 时间 = 87370毫秒[1分27秒], 成功 = 98933, 失败 = 0, qps = 1132.35 15:09:40.772 [QpsPrintThread] INFO QpsRunningTest$PrintThread - 第(27)次统计, 时间 = 130574毫秒[2分10秒], 成功 = 151317, 失败 = 0, qps = 1158.86 15:11:30.787 [QpsPrintThread] INFO QpsRunningTest$PrintThread - 第(49)次统计, 时间 = 237289毫秒[3分57秒], 成功 = 264122, 失败 = 0, qps = 1113.08 15:14:10.828 [QpsPrintThread] INFO QpsRunningTest$PrintThread - 第(81)次统计, 时间 = 392830毫秒[6分32秒], 成功 = 414227, 失败 = 0, qps = 1054.47 15:15:20.836 [QpsPrintThread] INFO QpsRunningTest$PrintThread - 第(95)次统计, 时间 = 460738毫秒[7分40秒], 成功 = 462517, 失败 = 0, qps = 1003.86 15:15:40.838 [QpsPrintThread] INFO QpsRunningTest$PrintThread - 第(99)次统计, 时间 = 480140毫秒[8分0秒], 成功 = 479827, 失败 = 0, qps = 999.35 15:17:40.849 [QpsPrintThread] INFO QpsRunningTest$PrintThread - 第(123)次统计, 时间 = 596551毫秒[9分56秒], 成功 = 562187, 失败 = 0, qps = 942.40 15:18:40.856 [QpsPrintThread] INFO QpsRunningTest$PrintThread - 第(135)次统计, 时间 = 654758毫秒[10分54秒], 成功 = 611696, 失败 = 0, qps = 934.23 15:21:45.884 [QpsPrintThread] INFO QpsRunningTest$PrintThread - 第(172)次统计, 时间 = 834686毫秒[13分54秒], 成功 = 762595, 失败 = 0, qps = 913.63 15:38:18.034 [QpsPrintThread] INFO QpsRunningTest$PrintThread - 第(350)次统计, 时间 = 1801035毫秒[30分1秒], 成功 = 1438254, 失败 = 0, qps = 798.57 Exception in thread "vert.x-eventloop-thread-1" java.lang.OutOfMemoryError: Java heap space java.lang.OutOfMemoryError: Java heap space java.lang.OutOfMemoryError: Java heap space java.lang.OutOfMemoryError: Java heap space java.lang.OutOfMemoryError: Java heap space * * * 2.优化记录(2018年8月26日 15:34:04) * 发现问题 : 希望可以将qps优化到5000+, 目前是1000,而且还不稳定 像本地方法调用一样快速(本地方法qps是5000+) * 入手点1 : 优化RpcLock.lock方法 -> 原因 : 通过jvisualvm工具发现 RpcLock.lock方法居然占用并堵塞唯一2条线程60%的工作时间 * * 实现方式1 : 在lock的实现方法中,增加了自旋, 如果自旋后没有获取到响应, 再进行堵塞 (不过几乎看不到效果,) * 测试结果 : 第75次统计, 时间=6分15秒, 调用数=1030483, 自旋成功数=138, 自旋成功率=0.01 *