Netty学习之耗时业务处理逻辑设计

我们在学习和使用Netty的过程中会按照我们以往的BIO方式来进行代码设计,这是惯性思维,虽然我们知道Netty是基于NIO设计的异步非阻塞框架,但是并不是仅仅通过Boss线程池和Worker线程池配置好其他的我们就不管了,Netty就替我们全做了。Netty只是替我们做好了业务接收和调度,并没有直接提供业务处理的相关线程,我们可以直接将业务写在Netty的IO线程池中,但是这样会影响线程的读写,直接降低系统的吞吐量及响应性能,所以我们需要将业务提交到实际的业务线程池中,我们可以自己定义自己的业务线程池,也可以使用Netty提供的线程池机制。自己定义的线程池实现方式这里就不做太多说明了,因为线程池的资料有很多,稍微用点心就能学会,其中boss用来监控tcp链接,worker用来处理io事件. 具体的说,boss执行 server.accept()操作 .worker处理事件的读写到业务逻辑处理等后续操作.NioEventLoopGroup 和NioEventLoop 都可以.但是前者使用的是线程池. 其实bossgroup如果服务端开启的是一个端口(大部分都是一个),单线程即可.worker大部分情况需要多线程处理了 .因为 一个eventloop绑定了一个selector,事件都是通过selector轮询处理的. 一万个请求让一个select处理和让100个selector处理肯定是多线程效率要高一些(因为有io),今天我们还是来说说Netty的实现方式。

static final EventExecutorGroup group = new DefaultEventExecutorGroup(16);
... ChannelPipeline pipeline = ch.pipeline(); 
// 简单非阻塞业务,可以使用I/O线程执行 
pipeline.addLast("decoder", new MyProtocolDecoder()); 
pipeline.addLast("encoder", new MyProtocolEncoder()); 
// 复杂耗时业务,使用新的线程池 
pipeline.addLast(group, "handler", new MyBusinessLogicHandler());
public class DataServerInitializer extends ChannelInitializer { 
private static final DataServerHandler SHARED = new DataServerHandler(); 
@Override 
public void initChannel(Channel channel) { 
channel.pipeline().addLast("handler", SHARED); 
} 
}

多个ChannelPipeline只有一个实例,所以该Handler要求无状态。
上述示例中,DataServerHandler的事件处理方法中,不能使用或改变本身的私有变量,
因为ChannelHandler是非线程安全的,使用私有变量会造成线程竞争而产生错误结果。

在Netty中,netty对线程模型进行了重新封装,它们分别是EventExecutorGroup和EventExecutor.每个EventExecutor是一个单独的线程,可以执行Runnable任务。EventExecutorGroup相当于一个线程组,用于管理和分配一组EventExecutor.我们知道Netty是基于事件驱动的。这样的封装给我们的开发带来了非常好的顺序。而且它还封装了定时任务,更加方便我们在一个线程中执行一些定时任务。

netty中默认实现了一个DefaultEventExecutorGroup和DefaultEventExecutor。DefaultEventexecutorGroup继承于MultithreadEventExecutorGroup,MultilthreadEventExecutorGroup是线程管理的核心类

Netty作为一个异步非阻塞式的框架,是不允许在ChannelHandler中长时间处理事务(比如数据库的操作),阻塞I/O的读写处理的。I/O线程是不允许被阻塞的,也就是不能在ChannelHandler中进行任何阻塞式的处理,但是对此我们也有相应的解决方法.就是在把ChannelHanders添加到ChannelPipeline的时候,指定一个EventExecutorGroup,ChannelHandler中所有的方法都将会在这个指定的EventExecutorGroup中运行。而这个EVentExecutorGroup运行的线程与I/O线程不同,达到不阻塞I/O的目的。 每次DefaultEventExcutorGroup线程池中的线程不能被重用,每次都会生成一个新的线程,然后在新的线程中调用ChannelHandler, 在visualvm可以看到线程数量直线增长。解决的方法是:不能使用局部变量形式DefaultEventExecutorGroup。而使用类静态成员变量:static final EventExecutor e1 = new DefaultEventExecutorGroup(16);

你可能感兴趣的:(java)