在nio编程中,select和bind可以不按顺序调用,也可以不在同一个线程中。netty中这是在boss线程中做的事情,可能会出现先select再绑定端口的情况。 这样做的好处就是统一化select逻辑,但是因为要支持tcp,udp,sctp等传输协议,每种协议都是不同类型的channel,所以将注册分离开已达到最大的代码重用。所以, select逻辑都在NioEventLoop的run方法里,而不同协议支持的注册和bind端口由不同类型的channel实现。netty在注册的时候先注册了个0,表示不对任何事件感兴趣,在绑定的时候,才又注册了accept事件。 这就是boss线程和worker线程分离开的原因。
epoll触发一个对断关闭然后在jvm层被包装成了一个读事件。因为 epoll收到退出事件的时候要触发一个读操作,读到-1认为退出,所以java从实际操作角度认为epoll的退出事件也是读。所以 简化了java层处理的事件数。 但这个时候用channel.read()方法读的时候,会报java.io.IOException: 远程主机强迫关闭了一个现有的连接。如果是主动关闭可以在触发读事件第一件事是判断是否有效吧,比如先读一个字节 看看是不是-1,如果是-1就停止。 如果异常是reset by peer,则表示被动关闭,一个流氓方法是 所有和链接相关的异常都catch,然后关闭这个链接,没有更好的做法了,netty自己也是这样做的。
在配置中对Async和Oneway的情况是有并发限制的,同时在处理的任务数有限制,如果有超出,则会抛出RemotingTooMuchRequestException; 其中Server流控配置中:Async是64,Oneway是256。 不能通过System.property更改,只能通过调用NettyServerConfig.setXxx方法更改。
Netty client端流控配置:
这个设置得比Server的大小限制得大的太多了,默认值根本没什么意义。其中Async和Oneway都是65535,可以通过System Properties来更改。
这些默认值都是一些经验值,所以比较合理。
举个例子,你有一个队列,有个队列的consumer,如果consumer从队列拿了一个东西,处理完后,告诉队列:你可以扔了,这大致等价于LT模式。 如果consumer拿完了东西,队列自己就直接扔了,这大概等价于ET模式。一般来说,ET模式下事件触发次数比LT要少很多,所以ET模式效率更高。但是哪个”高速“,现实当中应该用哪个,不得看consumer的处理速度,看业务需求。 场景:
本公众号后面的文章中会对上面的一些细节进行实例讲解,敬请关注。