Channel ChannelPipeline EventLoop

Channel

channel是通讯的载体,对应通讯的一端,在BIO中对应Socket,Nio中 对应SocketChannel, Netty中对应NioSocketChannel,ServerSocket同理
channelHandler是通道的处理器,一个channel往往有多个handler
channelpipeline是handler的容器,装载并管理handler的顺序(本质是双向链表)
1:channel创建时,会对应创建一个channelpipeline,pipeline首先会记录一个头部的处理器handler,当pipeline进行分发的时候,先分发给头部,然后依次执行,执行handler全部执行完成
2:channel创建时,会注册到EventLoop之中,EventLoop会监听事件的发生,不同的事件调用handler不同的处理方法,让流程运转起来

四种状态Channel

1:ChannelUnregistered 已创建但还未注册到监听器中
2;ChannelRegistered 已注册到监听器中EventLoop中
3:ChannelActive :连接完成处于活跃状态,此时可以接收和发送数据
4;ChannelInactive 非活跃状态 代表连接未建立或者已断开

Channelhandler生命周期

1:handlerAdded 把handler添加到pipeline之中
2;handlerRemoved 从pipeline中移除
3;execptionCaught 在处理过程中错误产生

Channel创建时机

在启动对象调用bind()或者connect方法时,会创建channel本质上通过反射,使用工厂的反射实现类创建对应的实例,此时对象的类型通过channel的参数来进行设定

ChannelPipeline

pipeline 中维护了入栈和出栈链路,两条链路的执行顺序。
handler值负责自身的业务逻辑,对通道而言,他是无状态的,通道的信息会保存到handlerContext处理器上下文中,他是连接pipeline和handler之间的中间角色
pileline管理的是由handlerContext包裹的handler,也就是说,当添加handler时,先将其转为handlerContext。然后添加到pipeline的双向链表中,头节点叫做HeadContext,尾节点叫做TailContext

pipeline优点:

1: 解耦,让处理器逻辑独立,可以被多个channel共享
2 :channel 相关信息,交给context维护
3: 具有极大灵活性,使用处理器可以方便的添加或者删除,或者更改它们的顺序

EventLoop

EventLoop里面封装了监听器,Event Loop是一种程序结构,在程序中完成两大功能,一是:负责程序本身的运行,二是:负责与其他线程的通信,被称为 Event Loop线程 (可以理解为“消息线程”)
$--------------------------------------------------------------------------------------------------------------------------
Event Loop事件循环,监听IO事件,内部封装了线程,EventLoopGroup事件循环组,是对EventLoop的管理,封装了线程池,当新建channel时,group会分配一个EvenLoop,封装了NIO中Selector,监听通道中所有事件,一个通道的生命周期,所有操作都由相同的EventLoop所封装的线程处理。同时,多个通道可以由一个EventLoop进行处理一对多的关系

NioEventLoop

EventLoopGroup(其实是MultithreadEventExecutorGroup)内部维护了一个类型为EventExecutor childrean数组 其大小是aThreads,这样就构成了一个线程池,如果我们在实例化NioEventLoopGroup时,指定线程池大小,则nThreads就是指定的值,反之是处理器核心数 * 2
NioEventLoop属性:
SelectorProvider provider属性:NioEventLoopGroup构造器中通过SelectProvider.provider()获取一个SelectProvider
Selector selector 属性:NioEventLoop 构造器中通过调用通过 selector = provider.openSelector()获取一个 selector 对象。
NioEventLoop 继承于SingleThreadEventLoop, 而SingleThreadEventLoop又继承SingleThreadEventLoop,而SingleThreadEventLoop 又继承与SingleThreadEventExecutor,
SingleThreadEventExecutor是netty中对本地线程的抽象,它内部又一个Thread thread属性,存储了一个本地java线程,因此我们可以认为一个NioEventLoop其实和一个特定的线程绑定,并且在其生命周期内,绑定的线程都不会在改变

Bootstrap

引导,对应用程序进行配置
1:配置
必选参数:group ,channel ,handler(服务端–childHandler)
group():指定一到两个reactor
channel():指定channel工厂,反射的方式创建channel
handler指定reactor处理器,其中childHandler指定的是,服务端所接受到的客户端channel使用的处理器,而服务端主reactor(bootGroup),已经默认添加了acceptor处理器,所以可以不指定
option():指定TCP相关参数,以及netty中自定义参数
以上配置参数的过程,称之为初始化

    //用端口来进行异步连接
        try {
            //这里返回的是channelFuture
            ChannelFuture future=bootstrap.bind(2100).sync();
            //这里阻塞最后finally的关闭   这里是等待closeFuture执行,并用sync()方法进行等待closeFuture执行的结果,同步等待异步的结果
            future.channel().closeFuture().sync();
        }catch (Exception e){
            e.printStackTrace();
        }finally{
            //进行关闭线程池
            boss.shutdownGracefully();
            work.shutdownGracefully();
        }
------------------------------------------------------
bind(),将服务器的channel绑定到端口号中,然后接收客户端的连接,让整个netty运行。

sync() 因为绑定事件是异步的,所以使用sync同步等待结果,换句话说,bind只出发了绑定端口的操作,需要使用sync等待事件执行的结果

future.channel().closeFuture.sync(),含义为,当通道被关闭的时候,才执行后续的操作,sync使当前线程执行到此处阻塞,让netty正常运行工作,以确保不指定后面的shutdown方法

Future和Promise

Future代表的是,一个还未完成的异步任务的执行结果,可以通过addListener方法,监听执行结果后进行相应的处理,此时它的状态可以分为未完成,已完成(成功,失败,主动取消)等
对Future来说,状态只能读取,不能进行修改,又出现了Promise,是Future的子类,但是Promise只能修改一次,
参照生活中定额发票 (Future)和机打发票(只能修改一次)Promise

心跳检测

IdleStateHandler,是netty提供的处理器
1:超长多长时间木有读readerIdleTime
2: 超长多长时间木有写writeIdleTime
3:超长多长时间木有读和写 allIdleTime
底层实现检测的是IdleStateEvent事件,通过管道传递给下一个handler处理,处理方法是userEventTriggered

Tcp粘包和粘包

TCP是“流协议”,基于字节流,木有界限,只会根据TCP缓冲区的情况进行拆分,所以业务上完整的包,可能被拆分成多个进行发送。
UDP是基于报文的,并且首部使用16bit来指示数据报文的长度,所以不会发生拆分现象。
发送了两个数据包,服务端只收到了一个,发生了粘包
即使收到了两个,如果包不完整,发生拆包。
-#----------------------------------------------------------------------------------------------------------------------
UDP不会发生拆包和粘包,UDP是基于报文的,在UDP的首部使用16bit存储报文的长度,因此不会发生
TCP发生粘包和拆包的根本原因 :要发送的数据先经过TCP的缓冲区,还限制了最大报文长度 A :如果要发送的数据 >Tcp剩余的缓冲区大小,发生拆包 B:如果要发送的数据 >最大报文长度,发生拆包 C:如果要发送的数据 << TCP剩余的缓冲区大小,发生粘包 D:接收数据的应用层,木有即时读取缓冲区数据,也会发生粘包

解决方法;

A;设置出消息的长度
B:设置出消息的边界–分隔符
----------------------------------------------------------------------------------------------#------------------
A; —》基于长度的编解码器,在包头部设置出数据的长度(类似于UDP的头部处理)
LengthFieldBasedFrameDecoder 自定义长度的处理方式
FixedLengthFrameDecoder 固定长度的处理方式
B:—》基于分隔符的解码器
DelimiterBasedFrameDecoder 自定义分隔符的处理方式
LineBaseFrameDecoder 行尾(“\n 或者 “”\r\n”)分隔符的处理方式

Demo总结:

1:第一种方式,传输的过程数据单位是字节流ByteBuf,需要自行处理分隔符以及数据的长度,此时会出现粘包粘包的问题
2:第二种方式,使用LineBasedFrameDecoder,配合StringDecoder使用,传输的数据单位变成字符串,可以直接处理,保证业务逻辑的包和真正传输的包是一致的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值