Netty 的线程模型

要想开发一个高性能的TCP服务器,熟悉所使用框架的线程模型非常重要。Netty本身都是高性能的网络框架,如果再搭配上高效率的代码,才能实现一个高大上的服务器。但是如果不了解它们的线程模型,就很难写出高性能的代码。框架本身效率再高,程序写的太差,那么服务器整体的性能也不会太高。就像一个电脑,CPU再好,内存小硬盘慢散热差,整体的性能也不会太高。

玩过Android开发的同学会知道,在Android应用中有一个非常重要线程:UI线程(即主线程)。UI线程是负责一个Android的界面显示以及和用户交互。Activity的一些方法,例如onCreate、onStop、onDestroy都是运行在UI线程中的。但是在编写Activity代码的时候有一点需要非常注意,就是绝对不能把阻塞的或者耗时的任务写在这些方法中,如果写在这些方法中,则会阻塞UI线程,导致用户操作的界面反应迟钝,体验很差。所以在Android开发中,耗时或者阻塞的任务会另外开线程去做。

同样在 Netty 中,也有一个非常重要的线程:IO线程。

传统的BIO实现的TCP服务器,特别对于TCP长连接,通常都要为每个连接开启一个线程,线程也是操作系统的一种资源,所以很难实现高性能高并发。而异步IO实现的TCP服务器,由于IO操作都是异步的,可以用一个线程或者少量线程来处理大量连接的IO操作,所以只需要少量的IO线程就可以实现高并发的服务器。

在网络编程过程中,通常有一些业务逻辑是比较耗时、阻塞的,例如数据库操作,如果网络不好,加上数据库性能差,SQL不够优化,数据量大,一条SQL可能会执行很久。由于IO线程本身数量就不多,通常只有一个或几个,而如果这种耗时阻塞的代码在IO线程中运行的话,IO线程的其他事情,例如网络read和write,就无法进行了,会影响IO性能以及整个服务器的性能。

所以,使用 Netty,如果有耗时的任务,就绝对不能在IO线程中运行,而是要另外开启线程来处理。

Netty的TCP服务器启动时,会创建两个NioEventLoopGroup,一个boss,一个worker:

 

EventLoopGroup bossGroup = new NioEventLoopGroup();  
EventLoopGroup workerGroup = new NioEventLoopGroup();

 

NioEventLoopGroup实际上是一个线程组,可以通过构造方法设置线程数量,默认为CPU核心数*2。boss用于服务器接收新的TCP连接,boss线程接收到新的连接后将连接注册到worker线程。worker线程用于处理IO操作,例如read、write。

Netty中的boss线程类似于MINA的Acceptor thread,work线程和MINA的I/O processor thread类似。不同的一点是MINA的Acceptor thread是单个线程,而Netty的boss是一个线程组。实际上Netty的ServerBootstrap可以监听多个端口号,如果只监听一个端口号,那么只需要一个boss线程即可,推荐将bossGroup的线程数量设置成1。

EventLoopGroup bossGroup = new NioEventLoopGroup(1);

当有新的TCP客户端连接到服务器,将由boss线程来接收连接,然后将连接注册到worker线程,当客户端发送数据到服务器,worker线程负责接收数据,并执行ChannelPipeline中的ChannelHandler。

和MINA的I/O processor thread 类似,Netty的worker线程本身数量不多,而且要实时处理IO事件,如果有耗时的业务逻辑阻塞住worker线程,例如在channelRead中执行一个耗时的数据库查询,会导致IO操作无法进行,服务器整体性能就会下降。

public class TcpServer {

 public static void main(String[] args) throws InterruptedException {
   EventLoopGroup bossGroup = new NioEventLoopGroup(1); // 服务器监听一个端口号,boss线程数建议设置成1
   EventLoopGroup workerGroup = new NioEventLoopGroup(4); // worker线程数设置成4
   try {
     ServerBootstrap b = new ServerBootstrap();
     b.group(bossGroup, workerGroup)
         .channel(NioServerSocketChannel.class)
         .childHandler(new ChannelInitializer<SocketChannel>() {

           // 创建一个16个线程的线程组来处理耗时的业务逻辑
           private EventExecutorGroup group = new DefaultEventExecutorGroup(16);

           @Override
           public void initChannel(SocketChannel ch) throws Exception {
             ChannelPipeline pipeline = ch.pipeline();
             pipeline.addLast(new LineBasedFrameDecoder(80));
             pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));

             // 将TcpServerHandler中的业务逻辑放到EventExecutorGroup线程组中执行
             pipeline.addLast(group, new TcpServerHandler());
           }
         });
     ChannelFuture f = b.bind(8080).sync();
     f.channel().closeFuture().sync();
   } finally {
     workerGroup.shutdownGracefully();
     bossGroup.shutdownGracefully();
   }
 }

}

class TcpServerHandler extends ChannelInboundHandlerAdapter {

 @Override
 public void channelRead(ChannelHandlerContext ctx, Object msg) throws InterruptedException {

   // 假设这里有个变态的SQL要执行3秒
   Thread.sleep(3000);

 }
}

以上就是Netty的线程模型,当然了,如果想要深入了解它的具体实现,还需要看它的源代码,最后大家也可以关注我的公众号:互联网架构师修炼之道,会有更多的精华文章等着您呢!

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ningshuaichen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值