欢迎关注公众号:【 爱编程】
若是有须要后台回复 2019赠送 1T的学习资料哦!!
本文是基于Netty4.1.36进行分析java
Netty服务端的启动代码基本都是以下:编程
private void start() throws Exception { final EchoServerHandler serverHandler = new EchoServerHandler(); /** * NioEventLoop并非一个纯粹的I/O线程,它除了负责I/O的读写以外 * 建立了两个NioEventLoopGroup, * 它们实际是两个独立的Reactor线程池。 * 一个用于接收客户端的TCP链接, * 另外一个用于处理I/O相关的读写操做,或者执行系统Task、定时任务Task等。 */ EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup childGroup = new NioEventLoopGroup(); try { //ServerBootstrap负责初始化netty服务器,而且开始监听端口的socket请求 ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, childGroup) .channel(NioServerSocketChannel.class) .localAddress(new InetSocketAddress(port)) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { // 为监听客户端read/write事件的Channel添加用户自定义的ChannelHandler socketChannel.pipeline().addLast(serverHandler); } }); ChannelFuture f = b.bind().sync(); f.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); } finally { bossGroup.shutdownGracefully().sync(); childGroup.shutdownGracefully().sync(); } }
从上图的代码能够总结为如下几个步骤:缓存
一、建立ServerBootStrap实例
二、设置并绑定Reactor线程池:EventLoopGroup,EventLoop就是处理全部注册到本线程的Selector上面的Channel
三、设置并绑定服务端的channel
四、五、建立处理网络事件的ChannelPipeline和handler,网络时间以流的形式在其中流转,handler完成多数的功能定制:好比编解码 SSl安全认证
六、绑定并启动监听端口
七、当轮训到准备就绪的channel后,由Reactor线程:NioEventLoop执行pipline中的方法,最终调度并执行channelHandler安全
它就是主要引导启动服务端,工做包括如下:服务器
流程:
首先从用户代码的bind()其实就是AbstractBootstrap.bind(),而后经过反射工厂将用户经过b.channel(NioServerSocketChannel.class)传入的NioServerSocketChannel经过调用底层的jdk的SelectorProvider建立channel,同时也接着建立好对应的ChannelPipeline。
详情能够参考下图,本身去查看一下源码:网络
主要工做以下:异步
1)设置的option缓存到NioServerSocketChannelConfig里
2)设置的attr设置到channel里
3)保存配置的childOptions,配置的childAttrs 到ServerBootstrapAcceptor里
4)往NioSocketChannel的pipeline中添加一个ServerBootstrapAcceptorsocket
主要的核心源码以下:ide
@Override void init(Channel channel) throws Exception { final Map<ChannelOption<?>, Object> options = options0(); synchronized (options) { setChannelOptions(channel, options, logger); } final Map<AttributeKey<?>, Object> attrs = attrs0(); synchronized (attrs) { for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) { @SuppressWarnings("unchecked") AttributeKey<Object> key = (AttributeKey<Object>) e.getKey(); channel.attr(key).set(e.getValue()); } } ChannelPipeline p = channel.pipeline(); final EventLoopGroup currentChildGroup = childGroup; final ChannelHandler currentChildHandler = childHandler; final Entry<ChannelOption<?>, Object>[] currentChildOptions; final Entry<AttributeKey<?>, Object>[] currentChildAttrs; synchronized (childOptions) { currentChildOptions = childOptions.entrySet().toArray(newOptionArray(0)); } synchronized (childAttrs) { currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(0)); } p.addLast(new ChannelInitializer<Channel>() { @Override public void initChannel(final Channel ch) throws Exception { final ChannelPipeline pipeline = ch.pipeline(); ChannelHandler handler = config.handler(); if (handler != null) { pipeline.addLast(handler); } ch.eventLoop().execute(new Runnable() { @Override public void run() { pipeline.addLast(new ServerBootstrapAcceptor( ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs)); } }); } }); }
小结:
整体如上面工做流程所述。
特别地建议:查看ServerBootstrapAcceptor源码,你能够发现ServerBootstrapAcceptor在channelRead事件触发的时候(也就有客户端链接的时候),把childHandler加到childChannel Pipeline的末尾,设置childHandler的options和attrs,最后把childHandler注册进childGroupoop
注册过程以下图
小结:
Channel 注册过程所作的工做就是将 Channel 与对应的 EventLoop 关联。
1).每一个 Channel 都会关联一个特定的 EventLoop, 而且这个 Channel 中的全部 IO 操做都是在这个 EventLoop 中执行的;
2).当关联好 Channel 和 EventLoop 后, 会继续调用底层的 Java NIO SocketChannel 的 register 方法, 将底层的 Java NIO SocketChannel 注册到指定的 selector 中.
经过这两步, 就完成了 Netty Channel 的注册过程.
端口绑定的源码流程基本以下图,详情能够仍是你本身读一下源码比较好点。
小结:
其实netty端口绑定是调用 jdk的javaChannel().bind(localAddress, config.getBacklog());进行绑定,而后TCP链路创建成功,Channel激活事件,经过channelPipeline进行传播。
客户端启动的常规代码以下:
private void start() throws Exception { /** * Netty用于接收客户端请求的线程池职责以下。 * (1)接收客户端TCP链接,初始化Channel参数; * (2)将链路状态变动事件通知给ChannelPipeline */ EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); b.group(group) .channel(NioSocketChannel.class) .remoteAddress(new InetSocketAddress(host,port)) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { socketChannel.pipeline().addLast(new EchoClientHandler()); } }); //绑定端口 ChannelFuture f = b.connect().sync(); f.channel().closeFuture().sync(); } catch (Exception e) { group.shutdownGracefully().sync(); } }
1.用户线程建立Bootstrap实例,经过API设置建立客户端相关的参数,异步发起客户端链接。
2.建立处理客户端链接、I/O读写的Reactor线程组NioEventLoopGroup,默认为CPU内核数的2倍。
3.经过Bootstrap的ChannelFactory和用户指定的Channel类型建立用于客户端NioSocketChannel,它的功能相似于JDK NIO类库提供的SocketChannel
4.建立默认的Channel Handler Pipeline,用于调度和执行网路事件。
5.异步发起TCP链接,判断链接是否成功。若是成功,则直接将NioSocketChannel注册到多路复用器上,监听读操做位,用于数据包读取和消息发送,若是没有当即链接成功,则注册链接监听为到多路复用器,等待链接结果。
6.注册对应的网络监听状态为到多路复用器。
7.由多路复用器在I/O现场中轮询个Channel,处理链接结果。
8.若是链接成功,设置Future结果,发送链接成功事件,触发ChannelPipeline执行。
9.由ChannelPipeline调度执行系统和用户的ChannelHandler,执行逻辑。
小结:
客户端是如何发起 TCP 链接的?
以下图:
特别提醒:
在AbstractChannelHandlerContext.connect()#findContextOutbound这步操做是返回的结果next实际上是头节点,也就是说在下一步next.invokeConnect()这里的next就是头节点,因此最终是调用HeadContext .connect()
本文主要讲述netty服务端和客户端的简单工做流程。
具体服务端与客户端如何通讯,以及内存管理等方面的知识下一次再写。
若是对 Java、大数据感兴趣请长按二维码关注一波,我会努力带给大家价值。以为对你哪怕有一丁点帮助的请帮忙点个赞或者转发哦。
关注公众号【爱编码】,回复2019有相关资料哦。