欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

一张图说明netty的结构及源码分析后的总结

程序员文章站 2022-07-14 07:54:59
...
    好久没写技术文章了,根据往深层次学习的大方向,学习了netty的源码,后来女儿出生,太忙也没总结。最近抽空总结一下。

   源码看的是netty3.10版本,担心太新的看不明白。一张图说明netty的结构及源码分析后的总结
            
    
    博客分类: java netty nio java 

    简单点都截图了,欢迎交流。

一、netty在我们项目中的位置

查看全图
一张图说明netty的结构及源码分析后的总结
            
    
    博客分类: java netty nio java 

二、netty的结构图

查看全图
一张图说明netty的结构及源码分析后的总结
            
    
    博客分类: java netty nio java 


三、说明

1、两大工作

    上图中,主要分左边的chanel部分与右边的pool部分,下面简要分析一下pool中的Boss与Worker的工作。

Boss run(){
1.	可能的epoll bug,重建selector
2.	Sink产生的bind任务
Run(){
		用nio的ServerSocket.bind
		Fire bound状态事件
}
3.处理selector的accept
			找一个worker,用RoundRobin方式依次找。
			把新产生的socketChannel包装一下,new一个registe任务,扔到worker的队列中处理
}


Worker run(){
1.	可能的epoll bug,重建selector
2.	Boss扔过来的,放在queue中channel的registe任务
Run(){
		SocketChannel注册read到selector
		Fire connect状态事件
}
3.处理selector的read
			先用nio的byteBuf,再根据配置的bufFactory产生ChannelBuf
			Fire msg reveive消息事件
}



2. Netty使用与启动例子(org.jboss.netty.example.echo。EchoServer)

        ServerBootstrap bootstrap = new ServerBootstrap(
                new NioServerSocketChannelFactory(
                        Executors.newCachedThreadPool(),
                        Executors.newCachedThreadPool()));
        bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
            public ChannelPipeline getPipeline() {
                ChannelPipeline p = Channels.pipeline();
                if (sslCtx != null) {
                    p.addLast("ssl", sslCtx.newHandler());
                }
                p.addLast("echo", new EchoServerHandler());
                return p;
            }
        });
        bootstrap.setOption("child.tcpNoDelay", true);
        bootstrap.setOption("child.receiveBufferSize", 1048576);
        bootstrap.setOption("child.sendBufferSize", 1048576);
        // Bind and start to accept incoming connections.
        bootstrap.bind(new InetSocketAddress(PORT));


 ServerBootstrap是一个仓库,放置channel工厂与pipeline工厂,还有很多setOption
 New NioServerSocketChannelFactory的时候,就会产生bosspool workerpool以及boss.worker等对象,并且线程已经动起来了。结构图上右侧❶❷就已经好了。在处理channelk前,你们更基础的模块必须组装好,并运转起来,才能开始接生意。
 bootstrap.bind的时候,开始❸new NioServerSocketChannel,配置好它的pipeline,当其包含的jdk的ServerSocketChannek.open后,将fireChannelOpen。在pipleline上handler传递处理
 具体由serverbootsrap的内部类binder处理,它会sendUpstream后,进行channel.bind。这其实是产生一个downStream的bound事件进行处理,最后是pipeline的sink进行处理
 Sink会产生一个bind任务,把真正的jdknio中的操作放boss的queue这里,由boss去做。Boss早就启动好了,就开始在端口监听了。



3.一些要点
1.ChannelHandler的状态管理:
   看多了责任链模式,首先想到ChannelHandler为啥不是单例呢?api中有:
   State management:A ChannelHandler often needs to store some stateful information.The simplest and recommended approach is to use member variables
   api中还有具体的例子可以看。

2.Boss与每一个worker都有自己对应的selector,只关注自己扔进去的channel的特定事件,返回也是这些有变化的channel,这样都是一个单线程中有序运行。多线程中也是组织好的,也不能是无序的,这样线程安全,稳定性,可控性,高性能达到完美。不过selector中的taskQueue用的是ConcurrentLinkedQueue,貌似没看到并发的情况么???放的时候都由boss放,取的时候都在一个线程里。

3.通常我们用底层引用的类,一般包装一下,这样中间过程就有了自己的逻辑了。而selector注册,只能用jdk的channel,这时把包装后的nettychannel当att传进去。这对底层设计或者使用底层,提供了一个范本。

4.在pipeline上产生链式反应时,外部对象调用是fire(channel, event/msg),内部的channelHandler完成工作后,如果调用时是(handlercontext,event/msg)。
   由于pipeline中使用链表,而不是常见的数组,这样context包装handler后就相互之间手拉手了。Context作为内部类对象可以找到外部pipeline对象,再传递到下一个处理。所以类似于j2ee中的filter(filterChain,req/res),要回到链条上来。
   为何用链表呢?因为有up/down两个方向,方便是前,或者后插入。因为handler是交netty用户的。我在druid中看到的是数组放filter,j2ee中的filter是先配置的过滤器先执行,猜测是数组。

5.Jdk nio中是byteBuf对象作为数据缓冲区,netty使用channelBuf,它分两种,一种是直接包装这个byteBuf的。一种是用这个buf产生heap中的对象保存数据。

6.New worker时,它把自己包装成另一个runable,并用静态类方法,使用exactor线程池来启动自己,中间就可以加入一些额外逻辑了。

7. netty中异步处理非常多,比如一个bind,一个注册,都作为任务扔到线程池中处理,异步调用时,产生一个future对象也传过去,作为结果的存放,异步的桥梁。这些操作印象中都会在channel的pipeline上传递一遍,这样的处理方式统一,也方便开发人员扩展功能,也方便用户随便插入中间的处理类。

8. 平时用内部静态类,而这里的DefaultChannelHandlerContext是pipeline的普通一个内部类,实现了一个接口。DefaultChannelHandlerContext当然从属于pipeline,并且它的实例指向pipeline的实例。如果handler处理后回调,就是传过来的ChannelHandler的对象。

9.当boss pool在init()的时候,想要知道内部的boss是不是启动了,就要同步等待。这时候用到一个countDownLatch,虽然实际上一个,按多个boss情况,每一个都要了解,所以countDownLatch属于boss,并且boss正常启动就countDown一下,外部等待的主线程就知道了。这样的设计在druid中也看到了。一般在构建复杂初始化时,主线程需要各自上报自己启动情况,可以这样设计。

10. 我也喜欢在开发一个功能时,把所有用的类,象是单独的机器一样,都预先配置在一个厂房里,如同bootstrap类,然后把相关的类对象都组合好,最后一个init启动起来。
  • 一张图说明netty的结构及源码分析后的总结
            
    
    博客分类: java netty nio java 
  • 大小: 84.8 KB
  • 一张图说明netty的结构及源码分析后的总结
            
    
    博客分类: java netty nio java 
  • 大小: 303.9 KB
相关标签: netty nio java