Netty连接处理那些事
编者注:Netty是Java领域有名的开源网络库,特点是高性能和高扩展性,因此很多流行的框架都是基于它来构建的,比如我们熟知的Dubbo、Rocketmq、Hadoop等,针对高性能RPC,一般都是基于Netty来构建,比如soft-bolt。总之一句话,Java小伙伴们需要且有必要学会使用Netty并理解其实现原理。
关于Netty的入门讲解可参考:Netty 入门,这一篇文章就够了
Netty的连接处理就是IO事件的处理,IO事件包括读事件、ACCEPT事件、写事件和OP_CONNECT事件。
IO事件的处理是结合ChanelPipeline来做的,一个IO事件到来,首先进行数据的读写操作,然后交给ChannelPipeline进行后续处理,ChannelPipeline中包含了channelHandler链(head + 自定义channelHandler + tail)。
使用channelPipeline和channelHandler机制,起到了解耦和可扩展的作用。一个IO事件的处理,包含了多个处理流程,这些处理流程正好对应channelPipeline中的channelHandler。如果对数据处理有新的需求,那么就新增channelHandler添加到channelPipeline中,这样实现很6,以后自己写代码可以参考。
说到这里,一般为了满足扩展性要求,常用2种模式:
- 方法模板模式:模板中定义了各个主流程,并且留下对应hook方法,便于扩展。
- 责任链模式:串行模式,可以动态添加链数量和对应回调方法。
netty的channelHandler
的channelPipeline
可以理解成就是责任链模式,通过动态增加channelHandler可达到复用和高扩展性目的。
了解netty连接处理机制之前需要了解下NioEventLoop模型,其中处理连接事件的架构图如下:
对应的处理逻辑源码为:
// 处理各种IO事件
private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
try {
int readyOps = k.readyOps();
if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
// OP_CONNECT事件,client连接上客户端时触发的事件
int ops = k.interestOps();
ops &= ~SelectionKey.OP_CONNECT;
k.interestOps(ops);
unsafe.finishConnect();
}
if ((readyOps & SelectionKey.OP_WRITE) != 0) {
ch.unsafe().forceFlush();
}
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
// 注意,这里读事件和ACCEPT事件对应的unsafe实例是不一样的
// 读事件 -> NioByteUnsafe, ACCEPT事件 -> NioMessageUnsafe
unsafe.read();
}
} catch (CancelledKeyException ignored) {
unsafe.close(unsafe.voidPromise());
}
}
从上面代码来看,事件主要分为3种,分别是OP_CONNECT事件、写事件和读事件(也包括ACCEPT事件)。下面分为3部分展开:
ACCEPT事件
// NioMessageUnsafe
public void read() {
assert eventLoop().inEventLoop();
final ChannelConfig config = config();
final ChannelPipeline pipeline = pipeline();
final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
allocHandle.reset(config);
boolean closed = false;
Throwable exception = null;
try {
do {
// 调用java socket的accept方法,接收请求
int localRead = doReadMessages(readBuf);
// 增加统计计数
allocHandle.incMessagesRead(localRead);
} while (allocHandle.continueReading());
} catch (Throwable t) {
exception = t;
}
// readBuf中存的是NioChannel
int size = readBuf.size();
for (int i = 0; i < size; i ++) {
readPending = false;
// 触发fireChannelRead
pipeline.fireChannelRead(readBuf.get(i));
}
readBuf.clear();
allocHandle.readComplete();
pipeline.fireChannelReadComplete();
}
连接建立好之后就该连接的channel注册到workGroup中某个NIOEventLoop的selector中,注册操作是在fireChannelRead中完成的,这一块逻辑就在ServerBootstrapAcceptor.channelRead中。
// ServerBootstrapAcceptor
public void channelRead(ChannelHandlerContext ctx, Object msg) {
final Channel child = (Channel) msg;
// 设置channel的pipeline handler,及channel属性
child.pipeline().addLast(childHandler);
setChannelOptions(child, childOptions, logger);
for (Entry<AttributeKey<?>, Object> e: childAttrs) {
child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
}
try {
// 将channel注册到childGroup中的Selector上
childGroup.register(child).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
forceClose(child, future.cause());
}
}
});
} catch (Throwable t) {
forceClose(child, t);
}
}
READ事件
// NioByteUnsafe
public final void read() {
final ChannelConfig config = config();
final ChannelPipeline pipeline = pipeline();
final ByteBufAllocator allocator = config.getAllocator();
final RecvByteBufAllocator.Handle allocHandle = recvBufAllocHandle();
allocHandle.reset(config);
ByteBuf byteBuf = null;
boolean close = false;
try {
do {
byteBuf = allocHandle.allocate(allocator);
// 从channel中读取数据,存放到byteBuf中
allocHandle.lastBytesRead(doReadBytes(byteBuf));
allocHandle.incMessagesRead(1);
readPending = false;
// 触发fireChannelRead
pipeline.fireChannelRead(byteBuf);
byteBuf = null;
} while (allocHandle.continueReading());
// 触发fireChannelReadComplete,如果在fireChannelReadComplete中执行了ChannelHandlerContext.flush,则响应结果返回给客户端
allocHandle.readComplete();
// 触发fireChannelReadComplete
pipeline.fireChannelReadComplete();
if (close) {
closeOnRead(pipeline);
}
} catch (Throwable t) {
if (!readPending && !config.isAutoRead()) {
removeReadOp();
}
}
}
写事件
正常情况下一般是不会注册写事件的,如果Socket发送缓冲区中没有空闲内存时,再写入会导致阻塞,此时可以注册写事件,当有空闲内存(或者可用字节数大于等于其低水位标记)时,再响应写事件,并触发对应回调。
if ((readyOps & SelectionKey.OP_WRITE) != 0) {
// 写事件,从flush操作来看,虽然之前没有向socket缓冲区写数据,但是已经写入到
// 了chnanel的outboundBuffer中,flush操作是将数据从outboundBuffer写入到
// socket缓冲区
ch.unsafe().forceFlush();
}
CONNECT事件
该事件是client触发的,由主动建立连接这一侧触发的。
if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
// OP_CONNECT事件,client连接上客户端时触发的事件
int ops = k.interestOps();
ops &= ~SelectionKey.OP_CONNECT;
k.interestOps(ops);
// 触发finishConnect事件,其中就包括fireChannelActive事件,如果有自定义的handler有channelActive方法,则会触发
unsafe.finishConnect();
}
推荐阅读
- Netty 入门,这一篇文章就够了
- Java常见几种动态代理的对比
- 程序员必看| mockito原理浅析
- Eureka 原理分析
- MQ初窥门径【面试必看的Kafka和RocketMQ存储区别】
- java lambda 深入浅出
欢迎小伙伴关注【TopCoder】阅读更多精彩好文。
Netty连接处理那些事的更多相关文章
- 聊聊iOS中网络编程长连接的那些事
1.长连接在iOS开发中的应用 常见的短连接应用场景:一般的App的网络请求都是基于Http1.0进行的,使用的是NSURLConnection.NSURLSession或者是AFNetworking ...
- Netty的那些”锁”事
Netty锁事的五个关键点: ① 在意锁的对象和范围 --> 减少粒度 ② 注意锁的对象本身大小 --> 减少空间占用 ③ 注意锁的速度 --> 提高速度 ④不同场景选择不同 ...
- SQL SERVER 无法正常连接的那些事
1.确保sqlserver服务正常运行. >一般可以从两个地方控制服务,一是系统自带的服务管理器,最快捷的方式是运行“services.msc”,二是使用sqlserver自带的“SQL Ser ...
- Netty自带连接池的使用
一.类介绍1.ChannelPool——连接池接口 2.SimpleChannelPool——实现ChannelPool接口,简单的连接池实现 3.FixedChannelPool——继承Simple ...
- 新手入门:目前为止最透彻的的Netty高性能原理和框架架构解析
1.引言 Netty 是一个广受欢迎的异步事件驱动的Java开源网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端. 本文基于 Netty 4.1 展开介绍相关理论模型,使用场景,基本组件 ...
- Netty 源码 NioEventLoop(三)执行流程
Netty 源码 NioEventLoop(三)执行流程 Netty 系列目录(https://www.cnblogs.com/binarylei/p/10117436.html) 上文提到在启动 N ...
- Netty In Action中文版 - 第三章:Netty核心概念
在这一章我们将讨论Netty的10个核心类.清楚了解他们的结构对使用Netty非常实用.可能有一些不会再工作中用到.可是也有一些非经常常使用也非常核心,你会遇到. Bootstrap ...
- netty的调优-及-献上写过注释的源码工程
Netty能干什么? Http服务器 使用Netty可以编写一个 Http服务器, 就像tomcat那样,能接受用户发送的http请求, , 只不过没有实现Servelt规范, 但是它也能解析携带的参 ...
- Netty高性能原理和框架架构解析
1.引言 Netty 是一个广受欢迎的异步事件驱动的Java开源网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端. 本文基于 Netty 4.1 展开介绍相关理论模型,使用场景,基本组件 ...
随机推荐
- 你真的懂Spring Java Config 吗?Full @Configuration vs lite @Bean mode
Full @Configuration和lite @Bean mode 是 Spring Java Config 中两个非常有意思的概念. 先来看一下官方文档关于这两者的相关内容: The @Bean ...
- 前端之CSS基础及使用方法
CSS介绍 CSS(Cascading Style Sheet,层叠样式表)定义如何显示HTML元素. 当浏览器读到一个样式表,它就会按照这个样式表来对文档进行格式化(渲染). CSS语法 CSS实例 ...
- Windows 10 删除C盘的用户,恢复技巧
起因 入域不成功,然后强制删除了C盘 Users里面 账户 然后再进行入域的时候就入域不成功了,如果再进行登录就会提示 无法登录到你的账户 通常可以通过从你的账户注销,然后重新登录解决此问题 如果不立 ...
- SQL SERVER 日志如何进行收缩?
最近经历了一次服务器SQL SERVER 数据库服务器端事务日志爆满,导致服务器数据库写入不进数据的宕机事件,经过此次事件的发生,奉劝各位同仁一句,如果没有绝对的充足存储空间,数据库事务日志文 ...
- requests模块(post)请求篇
'''利用parse模块模拟post请求分析百度词典分析步骤:1. 打开F122. 尝试输入单词girl,发现每敲一个字母后都有请求3. 请求地址是 http://fanyi.baidu.com/su ...
- Python之string模块(详细讲述string常见的所有方法)
相信不少学习python的程序员都接触过string模块 string模块主要包含关于字符串的处理函数 多说无益,初学python的小伙伴还不赶紧码起来 接下来将会讲到字符串的大小写.判断函数. 以及 ...
- 算法学习之剑指offer(一)
题目一: 题目描述 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. 思路1:遍历 ...
- 初识AutoIt
1.定义 AutoIt 目前最新是v3版本,这是一个使用类似BASIC脚本语言的免费软件,它设计用于Windows GUI(图形用户界面)中进行自动化操作.它利用模拟键盘按键,鼠标移动和窗口/控件的组 ...
- Flink实战| Flink+Redis实时防刷接口作弊
随着人口红利的慢慢削减,互联网产品的厮杀愈加激烈,大家开始看好下沉市场的潜力,拼多多,趣头条等厂商通过拉新奖励,购物优惠等政策率先抢占用户,壮大起来.其他各厂商也紧随其后,纷纷推出自己产品的极速版,如 ...
- oracle中创建用户、角色、权限简单使用
Oracle关于用户.权限.角色简单使用 创建数据库用户(在system用户下)create user 用户名 identified by 密码; 授权grant 权限名 to 用户名; 查看当前用户 ...