最近发现系统中出现了很多 IOException: Connection reset by peer 与 ClosedChannelException: null

深入看了看代码, 做了些测试, 发现 Connection reset 会在客户端不知道 channel 被关闭的情况下, 触发了 eventloop 的 unsafe.read() 操作抛出

而 ClosedChannelException 一般是由 Netty 主动抛出的, 在 AbstractChannel 以及 SSLHandler 里都可以看到 ClosedChannel 相关的代码

AbstractChannel

static final ClosedChannelException CLOSED_CHANNEL_EXCEPTION = new ClosedChannelException();...static {CLOSED_CHANNEL_EXCEPTION.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);NOT_YET_CONNECTED_EXCEPTION.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);}...@Overridepublic void write(Object msg, ChannelPromise promise) {ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;if (outboundBuffer == null) {// If the outboundBuffer is null we know the channel was closed and so// need to fail the future right away. If it is not null the handling of the rest// will be done in flush0()// See https://github.com/netty/netty/issues/2362
                safeSetFailure(promise, CLOSED_CHANNEL_EXCEPTION);// release message now to prevent resource-leak
                ReferenceCountUtil.release(msg);return;}outboundBuffer.addMessage(msg, promise);}

在代码的许多部分, 都会有这个 ClosedChannelException, 大概的意思是说在 channel close 以后, 如果还调用了 write 方法, 则会将 write 的 future 设置为 failure, 并将 cause 设置为 ClosedChannelException, 同样 SSLHandler 中也类似

-----------------

回到 Connection reset by peer, 要模拟这个情况比较简单, 就是在 server 端设置一个在 channelActive 的时候就 close channel 的 handler. 而在 client 端则写一个 Connect 成功后立即发送请求数据的 listener. 如下

client

    public static void main(String[] args) throws IOException, InterruptedException {Bootstrap b = new Bootstrap();b.group(new NioEventLoopGroup()).channel(NioSocketChannel.class).handler(new ChannelInitializer<NioSocketChannel>() {@Overrideprotected void initChannel(NioSocketChannel ch) throws Exception {}});b.connect("localhost", 8090).addListener(new ChannelFutureListener() {@Overridepublic void operationComplete(ChannelFuture future) throws Exception {if (future.isSuccess()) {future.channel().write(Unpooled.buffer().writeBytes("123".getBytes()));future.channel().flush();}}});

server

public class SimpleServer {public static void main(String[] args) throws Exception {EventLoopGroup bossGroup = new NioEventLoopGroup(1);EventLoopGroup workerGroup = new NioEventLoopGroup();ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_REUSEADDR, true).childHandler(new ChannelInitializer<NioSocketChannel>() {@Overrideprotected void initChannel(NioSocketChannel ch) throws Exception {ch.pipeline().addLast(new SimpleServerHandler());}});b.bind(8090).sync().channel().closeFuture().sync();}
}public class SimpleServerHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {ctx.channel().close().sync();}@Overridepublic void channelRead(ChannelHandlerContext ctx, final Object msg) throws Exception {System.out.println(123);}@Overridepublic void channelInactive(ChannelHandlerContext ctx) throws Exception {System.out.println("inactive");}
}

这种情况之所以能触发 connection reset by peer 异常, 是因为 connect 成功以后, client 段先会触发 connect 成功的 listener, 这个时候 server 段虽然断开了 channel, 也触发 channel 断开的事件 (它会触发一个客户端 read 事件, 但是这个 read 会返回 -1, -1 代表 channel 关闭, client 的 channelInactive 跟 channel  active 状态的改变都是在这时发生的), 但是这个事件是在 connect 成功的 listener 之后执行, 所以这个时候 listener 里的 channel 并不知道自己已经断开, 它还是会继续进行 write 跟 flush 操作, 在调用 flush 后, eventloop 会进入 OP_READ 事件里, 这时候 unsafe.read() 就会抛出 connection reset 异常. eventloop 代码如下

NioEventLoop

private static void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {final NioUnsafe unsafe = ch.unsafe();if (!k.isValid()) {// close the channel if the key is not valid anymore
            unsafe.close(unsafe.voidPromise());return;}try {int readyOps = k.readyOps();// Also check for readOps of 0 to workaround possible JDK bug which may otherwise lead// to a spin loopif ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {unsafe.read();if (!ch.isOpen()) {// Connection already closed - no need to handle write.
                    return;}}if ((readyOps & SelectionKey.OP_WRITE) != 0) {// Call forceFlush which will also take care of clear the OP_WRITE once there is nothing left to write
                ch.unsafe().forceFlush();}if ((readyOps & SelectionKey.OP_CONNECT) != 0) {// remove OP_CONNECT as otherwise Selector.select(..) will always return without blocking// See https://github.com/netty/netty/issues/924int ops = k.interestOps();ops &= ~SelectionKey.OP_CONNECT;k.interestOps(ops);unsafe.finishConnect();}} catch (CancelledKeyException e) {unsafe.close(unsafe.voidPromise());}}

这就是 connection reset by peer 产生的原因

------------------

再来看 ClosedChannelException 如何产生, 要复现他也很简单. 首先要明确, 并没有客户端主动关闭才会出现 ClosedChannelException 这么一说. 下面来看两种出现 ClosedChannelException 的客户端写法

client 1, 主动关闭 channel

public class SimpleClient {private static final Logger logger = LoggerFactory.getLogger(SimpleClient.class);public static void main(String[] args) throws IOException, InterruptedException {Bootstrap b = new Bootstrap();b.group(new NioEventLoopGroup()).channel(NioSocketChannel.class).handler(new ChannelInitializer<NioSocketChannel>() {@Overrideprotected void initChannel(NioSocketChannel ch) throws Exception {}});b.connect("localhost", 8090).addListener(new ChannelFutureListener() {@Overridepublic void operationComplete(ChannelFuture future) throws Exception {if (future.isSuccess()) { future.channel().close();future.channel().write(Unpooled.buffer().writeBytes("123".getBytes())).addListener(new ChannelFutureListener() {@Overridepublic void operationComplete(ChannelFuture future) throws Exception {if (!future.isSuccess()) {logger.error("Error", future.cause());}}});future.channel().flush();}}});}
}

只要在 write 之前主动调用了 close, 那么 write 必然会知道 close 是 close 状态, 最后 write 就会失败, 并且 future 里的 cause 就是 ClosedChannelException

--------------------

client 2. 由服务端造成的 ClosedChannelException

public class SimpleClient {private static final Logger logger = LoggerFactory.getLogger(SimpleClient.class);public static void main(String[] args) throws IOException, InterruptedException {Bootstrap b = new Bootstrap();b.group(new NioEventLoopGroup()).channel(NioSocketChannel.class).handler(new ChannelInitializer<NioSocketChannel>() {@Overrideprotected void initChannel(NioSocketChannel ch) throws Exception {}});Channel channel = b.connect("localhost", 8090).sync().channel();Thread.sleep(3000);channel.writeAndFlush(Unpooled.buffer().writeBytes("123".getBytes())).addListener(new ChannelFutureListener() {@Overridepublic void operationComplete(ChannelFuture future) throws Exception {if (!future.isSuccess()) {logger.error("error", future.cause());}}});}
}

服务端

public class SimpleServer {public static void main(String[] args) throws Exception {EventLoopGroup bossGroup = new NioEventLoopGroup(1);EventLoopGroup workerGroup = new NioEventLoopGroup();ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_REUSEADDR, true).childHandler(new ChannelInitializer<NioSocketChannel>() {@Overrideprotected void initChannel(NioSocketChannel ch) throws Exception {ch.pipeline().addLast(new SimpleServerHandler());}});b.bind(8090).sync().channel().closeFuture().sync();}
}

这种情况下,  服务端将 channel 关闭, 客户端先 sleep, 这期间 client 的 eventLoop 会处理客户端关闭的时间, 也就是 eventLoop 的 processKey 方法会进入 OP_READ, 然后 read 出来一个 -1, 最后触发 client channelInactive 事件, 当 sleep 醒来以后, 客户端调用 writeAndFlush, 这时候客户端 channel 的状态已经变为了 inactive, 所以 write 失败, cause 为 ClosedChannelException

转载于:https://www.cnblogs.com/zemliu/p/3864131.html

Netty 中 IOException: Connection reset by peer 与 java.nio.channels.ClosedChannelException: null相关推荐

  1. Netty : java.io.IOException: Connection reset by peer

    文章目录 1.背景 1.背景 netty聊天室程序,聊天页面如下: 点击上传图片开始报错: 最大上传限制 后来报错: java.io.IOException: Connection reset by ...

  2. Spark:java.io.IOException: Connection reset by peer

    1.背景 # 2.问题2 java.io.IOException: Connection reset by peerat sun.nio.ch.FileDispatcherImpl.read0(Nat ...

  3. [记录] ---阿里云java.io.IOException: Connection reset by peer的问题

    项目部署到阿里云,突然报错,频繁的打印堆栈信息,一开始是把堆内存打满导致服务一直重启,调大堆内存后就不影响正常服务了,但还是一直打堆栈,虽说日志会自动清理,但一直打这个信息着实不好看. 最终在阿里的e ...

  4. ES报错:Connection reset by peer 解决经历

    http://nicethemes.cn/news/txtlist_i28391v.html 这次来分享一下ES报错:java.io.IOException: Connection reset by ...

  5. Tomcat:Connection reset by peer: socket write error

    Connection reset by peer: socket write error错误分析及解决 Connection reset by peer: socket write error错误分析 ...

  6. Connection reset by peer (秒懂)

    疯狂创客圈 经典图书 : <Netty Zookeeper Redis 高并发实战> 面试必备 + 面试必备 + 面试必备 [博客园总入口 ] 疯狂创客圈 经典图书 : <Sprin ...

  7. connection reset by peer问题总结及解决方案

    找遍了 中英文网站,翻遍了能找的角落,发现了出现故障的原因和原理,及改如何处理,这里记录下,希望能帮助到有需要的小伙伴,少走点弯路, 以上就整理内容: connection reset by peer ...

  8. Connection reset by peer原理解析

    "Connection reset by peer"代表什么? "Connection reset by peer"表示当前服务器接受到了通信对端发送的TCP ...

  9. 分析connection reset by peer, socket write error错误原因

    上次写<connection reset by peer, socket write error问题排查>已经过去大半年,当时把问题"敷衍"过去了. 但是此后每隔一段时 ...

最新文章

  1. Python——阶段总结(一)
  2. Emacs 24.3 配置JDEE(http://blog.csdn.net/csfreebird/article/details/19033939)
  3. java虚拟机 什么语言_什么是Java虚拟机?为什么Java被称为平台无关的编程语言...
  4. python tensorflow tf.Session().run()函数(运行操作并评估“fetches”中的张量)
  5. MFC UI按钮多线程
  6. 初学HTML5系列二:HTML5新增的事件属性
  7. sort(()={return Math.random()-0.5)}乱序数组不准确
  8. 这篇 Linux 总结得很棒啊!
  9. c语言答辩题目,中学数学《线的认识》答辩题目与解析
  10. UVa11542 - Square(gauss)
  11. KEIL MDK access violation at 0x40021000 : no ‘read‘ permission的一种解决方法
  12. UFT11.5如何复用QTP9.2的脚本
  13. JBPM工作流(八)——流程实例(PI)Process Instance
  14. libiec61850 1.5.1 新版本
  15. 基于正态过程搜索和差分进化算法的改进樽海鞘群算法
  16. Java全栈开发---Java ERP系统开发:商业ERP(十三)CXF框架,物流BOS系统开发
  17. PC端/电脑端有没有识别二维码并分类的工具
  18. matlab中面板数据格式,MATLAB空间面板数据模型操作介绍
  19. Android开发高级进阶之Android开发艺术探索笔记重要知识点
  20. 从天津滨海新区大爆炸、危化品监管聊聊 IT人背负的社会责任感

热门文章

  1. linux下gdb常用的调试命令
  2. [html] 如何构建“弱网络环境”友好的项目?
  3. [vue] vue组件里写的原生addEventListeners监听事件,要手动去销毁吗?为什么?
  4. [css] 说说浏览器解析CSS选择器的过程?
  5. [css] 如何做图片预览,如何放大一个图片?
  6. 前端学习(2684):重读vue电商网站5之登录页面总结如何进行表单验证
  7. 前端学习(1973)vue之电商管理系统电商系统之完成修改的操作
  8. “睡服”面试官系列第十七篇之Reflect(建议收藏学习)
  9. 前端学习(1560):ng-class颜色切换
  10. 前端学习(892):bom概述