背景
netty 是一个异步事件驱动的网络通信层框架,其官方文档的解释为
Netty is a NIO client server framework which enables quick and easy development of network applications such as protocol servers and clients. It greatly simplifies and streamlines network programming such as TCP and UDP socket server.
我们在新美大消息推送系统sailfish(日均推送消息量50亿),新美大移动端代理优化系统shark(日均吞吐量30亿)中,均选择了netty作为底层网络通信框架。
既然两大如此重要的系统底层都使用到了netty,所以必然要对netty的机制,甚至源码了若指掌,于是,便催生了netty源码系列文章。后面,我会通过一系列的主题把我从netty源码里所学到的毫无保留地介绍给你,源码基于4.1.6.Final
为什么使用netty
netty底层基于jdk的NIO,我们为什么不直接基于jdk的nio或者其他nio框架?下面是我总结出来的原因
1.使用jdk自带的nio需要了解太多的概念,编程复杂
2.netty底层IO模型随意切换,而这一切只需要做微小的改动
3.netty自带的拆包解包,异常检测等机制让你从nio的繁重细节中脱离出来,让你只需要关心业务逻辑
4.netty解决了jdk的很多包括空轮训在内的bug
5.netty底层对线程,selector做了很多细小的优化,精心设计的reactor线程做到非常高效的并发处理
6.自带各种协议栈让你处理任何一种通用协议都几乎不用亲自动手
7.netty社区活跃,遇到问题随时邮件列表或者issue
8.netty已经历各大rpc框架,消息中间件,分布式通信中间件线上的广泛验证,健壮性无比强大
解析netty
了解了这么多,今天我们就从一个例子出来,开始我们的netty源码之旅。
本篇主要讲述的是netty是如何绑定端口,启动服务。启动服务的过程中,你将会了解到netty各大核心组件,我先不会细讲这些组件,而是会告诉你各大组件是怎么串起来组成netty的核心
example
下面是一个非常简单的服务端启动代码
public final class SimpleServer {
public static void main(String[] args) throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new SimpleServerHandler())
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
}
});
ChannelFuture f = b.bind(8888).sync();
f.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
private static class SimpleServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("channelActive");
}
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
System.out.println("channelRegistered");
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
System.out.println("handlerAdded");
}
}
}
简单的几行代码就能开启一个服务端,端口绑定在8888,使用nio模式,下面讲下每一个步骤的处理细节
EventLoopGroup
说白了,就是一个死循环,不停地检测IO事件,处理IO事件,执行任务
ServerBootstrap
是服务端的一个启动辅助类,通过给他设置一系列参数来绑定端口启动服务
group(bossGroup, workerGroup)
我们需要两种类型的人干活,一个是老板,一个是工人,老板负责从外面接活,接到的活分配给工人干,放到这里,bossGroup
的作用就是不断地accept到新的连接,将新的连接丢给workerGroup
来处理
.channel(NioServerSocketChannel.class)
表示服务端启动的是nio相关的channel,channel在netty里面是一大核心概念,可以理解为一条channel就是一个连接或者一个服务端bind动作,后面会细说
.handler(new SimpleServerHandler()
表示服务器启动过程中,需要经过哪些流程,这里SimpleServerHandler
最终的顶层接口为ChannelHander
,是netty的一大核心概念,表示数据流经过的处理器,可以理解为流水线上的每一道关卡
childHandler(new ChannelInitializer<SocketChannel>)...
表示一条新的连接进来之后,该怎么处理,也就是上面所说的,老板如何给工人配活
ChannelFuture f = b.bind(8888).sync();
这里就是真正的启动过程了,绑定8888端口,等待服务器启动完毕,才会进入下行代码
f.channel().closeFuture().sync();
等待服务端关闭socket
bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully();
关闭两组死循环
上述代码可以很轻松地再本地跑起来,最终控制台的输出为:
handlerAdded
channelRegistered
channelActive
总结
netty启动一个服务所经过的流程
1.设置启动类参数,最重要的就是设置channel
2.创建server对应的channel,创建各大组件,包括ChannelConfig,ChannelId,ChannelPipeline,ChannelHandler,Unsafe等
3.初始化server对应的channel,设置一些attr,option,以及设置子channel的attr,option,给server的channel添加新channel接入器,并出发addHandler,register等事件
4.调用到jdk底层做端口绑定,并触发active事件,active触发的时候,真正做服务费端口绑定