netty可靠性
Netty的可靠性
首先,我们要从Netty的主要用途来分析它的可靠性,Netty目前的主流用法有三种:
1) 构建RPC调用的基础通信组件,提供跨节点的远程服务调用能力;
2) NIO通信框架,用于跨节点的数据交换;
3) 其它应用协议栈的基础通信组件,例如HTTP协议以及其它基于Netty开发的应用层协议栈。
以阿里的分布式服务框架Dubbo为例,Netty是Dubbo RPC框架的核心。它的服务调用示例图如下:
图1-1 Dubbo的节点角色说明图
其中,服务提供者和服务调用者之间可以通过Dubbo协议进行RPC调用,消息的收发默认通过Netty完成。
通过对Netty主流应用场景的分析,我们发现Netty面临的可靠性问题大致分为三类:
1) 传统的网络I/O故障,例如网络闪断、防火墙Hang住连接、网络超时等;
2) NIO特有的故障,例如NIO类库特有的BUG、读写半包处理异常、Reactor线程跑飞等等;
3) 编解码相关的异常。
在大多数的业务应用场景中,一旦因为某些故障导致Netty不能正常工作,业务往往会陷入瘫痪。所以,从业务诉求来看,对Netty框架的可靠性要求是非常的高。作为当前业界最流行的一款NIO框架,Netty在不同行业和领域都得到了广泛的应用,它的高可靠性已经得到了成百上千的生产系统检验。
Netty是如何支持系统高可靠性的?下面,我们就从几个不同维度出发一探究竟。
2. Netty高可靠性之道
2.1. 网络通信类故障
2.1.1. 客户端连接超时
在传统的同步阻塞编程模式下,客户端Socket发起网络连接,往往需要指定连接超时时间,这样做的目的主要有两个:
1) 在同步阻塞I/O模型中,连接操作是同步阻塞的,如果不设置超时时间,客户端I/O线程可能会被长时间阻塞,这会导致系统可用I/O线程数的减少;
2) 业务层需要:大多数系统都会对业务流程执行时间有限制,例如WEB交互类的响应时间要小于3S。客户端设置连接超时时间是为了实现业务层的超时。
JDK原生的Socket连接接口定义如下:
图2-1 JDK Socket连接超时接口
对于NIO的SocketChannel,在非阻塞模式下,它会直接返回连接结果,如果没有连接成功,也没有发生IO异常,则需要将SocketChannel注册到Selector上监听连接结果。所以,异步连接的超时无法在API层面直接设置,而是需要通过定时器来主动监测。
下面我们首先看下JDK NIO类库的SocketChannel连接接口定义:
图2-2 JDK NIO 类库SocketChannel连接接口
从上面的接口定义可以看出,NIO类库并没有现成的连接超时接口供用户直接使用,如果要在NIO编程中支持连接超时,往往需要NIO框架或者用户自己封装实现。
下面我们看下Netty是如何支持连接超时的,首先,在创建NIO客户端的时候,可以配置连接超时参数:
图2-3 Netty客户端创建支持设置连接超时参数
设置完连接超时之后,Netty在发起连接的时候,会根据超时时间创建ScheduledFuture挂载在Reactor线程上,用于定时监测是否发生连接超时,相关代码如下:
图2-4 根据连接超时创建超时监测定时任务
创建连接超时定时任务之后,会由NioEventLoop负责执行。如果已经连接超时,但是服务端仍然没有返回TCP握手应答,则关闭连接,代码如上图所示。
如果在超时期限内处理完成连接操作,则取消连接超时定时任务,相关代码如下:
图2-5 取消连接超时定时任务
Netty的客户端连接超时参数与其它常用的TCP参数一起配置,使用起来非常方便,上层用户不用关心底层的超时实现机制。这既满足了用户的个性化需求,又实现了故障的分层隔离。
2.1.2. 通信对端强制关闭连接
在客户端和服务端正常通信过程中,如果发生网络闪断、对方进程突然宕机或者其它非正常关闭链路事件时,TCP链路就会发生异常。由于TCP是全双工的,通信双方都需要关闭和释放Socket句柄才不会发生句柄的泄漏。
在实际的NIO编程过程中,我们经常会发现由于句柄没有被及时关闭导致的功能和可靠性问题。究其原因总结如下:
1) IO的读写等操作并非仅仅集中在Reactor线程内部,用户上层的一些定制行为可能会导致IO操作的外逸,例如业务自定义心跳机制。这些定制行为加大了统一异常处理的难度,IO操作越发散,故障发生的概率就越大;
2) 一些异常分支没有考虑到,由于外部环境诱因导致程序进入这些分支,就会引起故障。
下面我们通过故障模拟,看Netty是如何处理对端链路强制关闭异常的。首先启动Netty服务端和客户端,TCP链路建立成功之后,双方维持该链路,查看链路状态,结果如下:
图2-6 Netty服务端和客户端TCP链路状态正常
强制关闭客户端,模拟客户端宕机,服务端控制台打印如下异常:
图2-7 模拟TCP链路故障
从堆栈信息可以判断,服务端已经监控到客户端强制关闭了连接,下面我们看下服务端是否已经释放了连接句柄,再次执行netstat命令,执行结果如下:
图2-8 查看故障链路状态
从执行结果可以看出,服务端已经关闭了和客户端的TCP连接,句柄资源正常释放。由此可以得出结论,Netty底层已经自动对该故障进行了处理。
下面我们一起看下Netty是如何感知到链路关闭异常并进行正确处理的,查看AbstractByteBuf的writeBytes方法,它负责将指定Channel的缓冲区数据写入到ByteBuf中,详细代码如下:
图2-9 AbstractByteBuf的writeBytes方法
在调用SocketChannel的read方法时发生了IOException,代码如下:
图2-10 读取缓冲区数据发生IO异常
为了保证IO异常被统一处理,该异常向上抛,由AbstractNioByteChannel进行统一异常处理,代码如下:
图2-11 链路异常退出异常处理
为了能够对异常策略进行统一,也为了方便维护,防止处理不当导致的句柄泄漏等问题,句柄的关闭,统一调用AbstractChannel的close方法,代码如下:
图2-12 统一的Socket句柄关闭接口
2.1.3. 正常的连接关闭
对于短连接协议,例如HTTP协议,通信双方数据交互完成之后,通常按照双方的约定由服务端关闭连接,客户端获得TCP连接关闭请求之后,关闭自身的Socket连接,双方正式断开连接。
在实际的NIO编程过程中,经常存在一种误区:认为只要是对方关闭连接,就会发生IO异常,捕获IO异常之后再关闭连接即可。实际上,连接的合法关闭不会发生IO异常,它是一种正常场景,如果遗漏了该场景的判断和处理就会导致连接句柄泄漏。
下面我们一起模拟故障,看Netty是如何处理的。测试场景设计如下:改造下Netty客户端,双发链路建立成功之后,等待120S,客户端正常关闭链路。看服务端是否能够感知并释放句柄资源。
首先启动Netty客户端和服务端,双方TCP链路连接正常:
图2-13 TCP连接状态正常
120S之后,客户端关闭连接,进程退出,为了能够看到整个处理过程,我们在服务端的Reactor线程处设置断点,先不做处理,此时链路状态如下:
图2-14 TCP连接句柄等待释放
从上图可以看出,此时服务端并没有关闭Socket连接,链路处于CLOSE_WAIT状态,放开代码让服务端执行完,结果如下:
图2-15 TCP连接句柄正常释放
下面我们一起看下服务端是如何判断出客户端关闭连接的,当连接被对方合法关闭后,被关闭的SocketChannel会处于就绪状态,SocketChannel的read操作返回值为-1,说明连接已经被关闭,代码如下:
图2-16 需要对读取的字节数进行判断
如果SocketChannel被设置为非阻塞,则它的read操作可能返回三个值:
1) 大于0,表示读取到了字节数;
2) 等于0,没有读取到消息,可能TCP处于Keep-Alive状态,接收到的是TCP握手消息;
3) -1,连接已经被对方合法关闭。
通过调试,我们发现,NIO类库的返回值确实为-1:
图2-17 链路正常关闭,返回值为-1
得知连接关闭之后,Netty将关闭操作位设置为true,关闭句柄,代码如下:
图2-18 连接正常关闭,释放资源
2.1.4. 故障定制
在大多数场景下,当底层网络发生故障的时候,应该由底层的NIO框架负责释放资源,处理异常等。上层的业务应用不需要关心底层的处理细节。但是,在一些特殊的场景下,用户可能需要感知这些异常,并针对这些异常进行定制处理,例如:
1) 客户端的断连重连机制;
2) 消息的缓存重发;
3) 接口日志中详细记录故障细节;
4) 运维相关功能,例如告警、触发邮件/短信等
Netty的处理策略是发生IO异常,底层的资源由它负责释放,同时将异常堆栈信息以事件的形式通知给上层用户,由用户对异常进行定制。这种处理机制既保证了异常处理的安全性,也向上层提供了灵活的定制能力。
具体接口定义以及默认实现如下:
图2-19 故障定制接口
用户可以覆盖该接口,进行个性化的异常定制。例如发起重连等。
netty可靠性的更多相关文章
- Netty系列之Netty可靠性分析
作者 李林锋 发布于 2014年6月19日 | 29 讨论 分享到:微博微信FacebookTwitter有道云笔记邮件分享 稍后阅读 我的阅读清单 1. 背景 1.1. 宕机的代价 1.1. ...
- 【转】Netty系列之Netty可靠性分析
http://www.infoq.com/cn/articles/netty-reliability 首先,我们要从Netty的主要用途来分析它的可靠性,Netty目前的主流用法有三种: 1) 构建R ...
- Netty系列之Netty可靠性分析--转载
原文地址:http://www.infoq.com/cn/articles/netty-reliability 1. 背景 1.1. 宕机的代价 1.1.1. 电信行业 毕马威国际(KPMG Inte ...
- Netty学习笔记
一些类与方法说明 1)ByteBuf ByteBuf的API说明: Creation of a buffer It is recommended to create a new buffer usin ...
- Netty系列学习
Netty系列之Netty高性能之道 Netty系列之Netty线程模型 Netty系列之Netty 服务端创建 Netty系列之Netty编解码框架分析 Netty系列之Netty百万级推送服务设计 ...
- Netty构建分布式消息队列(AvatarMQ)设计指南之架构篇
目前业界流行的分布式消息队列系统(或者可以叫做消息中间件)种类繁多,比如,基于Erlang的RabbitMQ.基于Java的ActiveMQ/Apache Kafka.基于C/C++的ZeroMQ等等 ...
- Netty构建分布式消息队列实现原理浅析
在本人的上一篇博客文章:Netty构建分布式消息队列(AvatarMQ)设计指南之架构篇 中,重点向大家介绍了AvatarMQ主要构成模块以及目前存在的优缺点.最后以一个生产者.消费者传递消息的例子, ...
- Netty简介
Netty简介 Netty是由JBOSS提供的一个Java开源框架.Netty提供异步的.事件驱动的网络应用程序框架和工具,用以快速开发高性能.高可靠性的网络服务器和客户端程序.和传统BIO不同,NI ...
- Netty:数据处理流程
Netty作为异步的.事件驱动一个网络通信框架,使用它可以帮助我们快速开发高性能高可靠性的网络服务. 为了更好的使用Netty来解决开发中的问题,学习Netty是很有必要的. Netty现在主流有三个 ...
随机推荐
- 浅析CLR的异常处理模型
文章目录: 异常概述 CLR中的异常处理机制 CLR中异常的核心类System.Exception类 异常处理的设计规范和最佳实践 异常处理的性能问题 其他拓展 1.异常概述 异常我们通常指的是行动成 ...
- 第5章分布式系统模式 在 .NET 中使用 DataSet 实现 Data Transfer Object
要在 .NET Framework 中实现分布式应用程序.客户端应用程序需要显示一个窗体,该窗体要求对 ASP.NET Web Service 进行多个调用以满足单个用户请求.基于性能方面的考虑,我们 ...
- React+webpack
webPack + React 步骤: 1. 创建文件夹 src 源代码目录 main.js 打包的入口文件 App.js 项目的根组件 import React,{Component} from ' ...
- 解决Android单个dex文件不能超过65535个方法问题
一.找坑:谷歌规定单个dex文件中的方法不能超过65536的限制 我们编写项目过程中在工程的lib文件夹下引用的第三方插件jar包太多或者项目过大,编译运行时就有可能报出com.android.dex ...
- SQL数据库链接代码的解释
SqlConnection conn = new SqlConnection(); conn.ConnectionString = "Data Source=(local);Initial ...
- RedHat/CentOS 大文件拆分及合并与md5验证
[root@tdh55 mnt]# cd /opt/[root@tdh55 opt]# ll -h-rw-r--r--. 1 root root 7.5G May 12 11:19 TDH-Image ...
- 解决启动httpd报: apr_sockaddr_info_get() failed for错误
我测试库里 service httpd start 时报 下面错误 httpd: apr_sockaddr_info_get() failed for fengxin.wzjzt.centoshttp ...
- MongoDB_可视化工具Robo 3T
Robo 3T可以对MongoDB进行可视化操作. Robo 3T安装 官网下载地址:https://robomongo.org/ 进入官网,点击下载,Studio 3T功能更全面,基础功能是免费的, ...
- Flask框架函数
title: flask学习笔记 subtitle: 1. flask框架函数 date: 2018-12-14 10:17:28 --- Flask学习 学习Miguel Grinberg的2017 ...
- 训练1-H
小明今年3岁了, 现在他已经能够认识100以内的非负整数, 并且能够进行100以内的非负整数的加法计算. 对于大于等于100的整数, 小明仅保留该数的最后两位进行计算, 如果计算结果大于等于100, ...