一切从ServerBootstrap开始

ServerBootstrap负责初始话netty服务器,并且开始监听端口的socket请求。

bootstrap bootstrap =newServerBootstrap(

newNioServerSocketChannelFactory(
Executors.newCachedThreadPool(),//boss线程池
Executors.newCachedThreadPool()//worker线程池
)
);
bootstrap.setPipelineFactory(newHttpChannelPipelineFactory());
bootstrap.setOption("child.tcpNoDelay",true);
bootstrap.setOption("child.keepAlive",true);
bootstrap.bind(newInetSocketAddress(httpPort));//端口开始监听
ServerBootstrap用一个ServerSocketChannelFactory 来实例化。ServerSocketChannelFactory 有两种选择,一种是NioServerSocketChannelFactory,一种是OioServerSocketChannelFactory。前者使用NIO,后则使用普通的阻塞式IO。它们都需要两个线程池实例作为参数来初始化,一个是boss线程池,一个是worker线程池。

ServerBootstrap.bind(int)负责绑定端口,当这个方法执行后,ServerBootstrap就可以接受指定端口上的socket连接了。一个ServerBootstrap可以绑定多个端口。

boss线程和worker线程

可以这么说,ServerBootstrap监听的一个端口对应一个boss线程,它们一一对应。比如你需要netty监听80和443端口,那么就会有两个boss线程分别负责处理来自两个端口的socket请求。在boss线程接受了socket连接求后,会产生一个channel(一个打开的socket对应一个打开的channel),并把这个channel交给ServerBootstrap初始化时指定的ServerSocketChannelFactory来处理,boss线程则继续处理socket的请求。

ServerSocketChannelFactory则会从worker线程池中找出一个worker线程来继续处理这个请求。
如果是OioServerSocketChannelFactory的话,那个这个channel上所有的socket消息消息,从开始到channel(socket)关闭,都只由这个特定的worker来处理,也就是说一个打开的socket对应一个指定的worker线程,这个worker线程在socket没有关闭的情况下,也只能为这个socket处理消息,无法服务器他socket。

如果是NioServerSocketChannelFactory的话则不然,每个worker可以服务不同的socket或者说channel,worker线程和channel不再有一一对应的关系。
显然,NioServerSocketChannelFactory只需要少量活动的worker线程及能很好的处理众多的channel,而OioServerSocketChannelFactory则需要与打开channel等量的worker线程来服务。

线程是一种资源,所以当netty服务器需要处理长连接的时候,最好选择NioServerSocketChannelFactory,这样可以避免创建大量的worker线程。在用作http服务器的时候,也最好选择NioServerSocketChannelFactory,因为现代浏览器都会使用http keepalive功能(可以让浏览器的不同http请求共享一个信道),这也是一种长连接。

worker线程的生命周期(life circle)

当某个channel有消息到达或者有消息需要写入socket的时候,worker线程就会从线程池中取出一个。在worker线程中,消息会经过设定好的ChannelPipeline处理。ChannelPipeline就是一堆有顺序的filter,它分为两部分:UpstreamHandler和DownStreamHandler。本文着重介绍netty的线程模型,所以关于pipeline的内容从简说明。

客户端送入的消息会首先由许多UpstreamHandler依次处理,处理得到的数据送入应用的业务逻辑handler,通常用SimpleChannelUpstreamHandler来实现这一部分。

publicclassSimpleChannelUpstreamHandler{

publicvoid messageReceived(ChannelHandlerContext c,MessageEvent e)throwsException{
//业务逻辑开始掺入
}
}
对于Nio当messageReceived()方法执行后,如果没有产生异常,worker线程就执行完毕了,它会被线程池回收。业务逻辑hanlder会通过一些方法,把返回的数据交给指定好顺序的DownStreamHandler处理,处理后的数据如果需要,会被写入channel,进而通过绑定的socket发送给客户端。这个过程是由另外一个线程池中的worker线程来完成的。

对于Oio来说,从始到终,都是由一个指定的worker来处理。

减少worker线程的处理占用时间

worker线程是由netty内部管理,统一调配的一种资源,所以最好应该尽快的把让worker线程执行完毕,返回给线程池回收利用。worker线程的大部分时间消耗在在ChannelPipeline的各种handler中,而在这些handler中,一般是负责应用程序业务逻辑掺入的那个handler最占时间,它通常是排在最后的UpstreamHandler。所以通过把这部分处理内容交给另外一个线程来处理,可以有效的减少worker线程的周期循环时间。一般有两种方法:

messageReceived()方法中开启一个新的线程来处理业务逻辑

publicvoid messageReceived(ChannelHandlerContext ctx,MessageEvent e)throwsException{

...
newThread(...).start();
}

在messageReceived()中开启一个新线程来继续处理业务逻辑,而worker线程在执行完messageReceived()就会结束了。更加优雅的方法是另外构造一个线程池来提交业务逻辑处理任务。

利用netty框架自带的ExecutionHandler

基本使用方法

publicclassDatabaseGatewayPipelineFactoryimplementsChannelPipelineFactory{

 
privatefinalExecutionHandler executionHandler;
 
publicDatabaseGatewayPipelineFactory(ExecutionHandler executionHandler){
this.executionHandler= executionHandler;
}
 
publicChannelPipeline getPipeline(){
returnChannels.pipeline(
newDatabaseGatewayProtocolEncoder(),
newDatabaseGatewayProtocolDecoder(),
executionHandler,// 多个pipeline之间必须共享同一个ExecutionHandler
newDatabaseQueryingHandler());//业务逻辑handler,IO密集
}
}
把共享的ExecutionHandler实例放在业务逻辑handler之前即可,注意ExecutionHandler一定要在不同的pipeline之间共享。它的作用是自动从ExecutionHandler自己管理的一个线程池中拿出一个线程来处理排在它后面的业务逻辑handler。而worker线程在经过ExecutionHandler后就结束了,它会被ChannelFactory的worker线程池所回收。

它的构造方法是ExecutionHandler(Executor executor) ,很显然executor就是ExecutionHandler内部管理的线程池了。netty额外给我们提供了两种线程池:
MemoryAwareThreadPoolExecutor和OrderedMemoryAwareThreadPoolExecutor,它们都在org.jboss.netty.handler.execution 包下。
MemoryAwareThreadPoolExecutor确保jvm不会因为过多的线程而导致内存溢出错误,OrderedMemoryAwareThreadPoolExecutor是前一个线程池的子类,除了保证没有内存溢出之外,还可以保证channel event的处理次序。具体可以查看API文档,上面有详细说明。

Netty服务器线程模型概览的更多相关文章

  1. 面试官:Netty的线程模型可不只是主从多Reactor这么简单

    笔者看来Netty的内核主要包括如下图三个部分: 其各个核心模块主要的职责如下: 内存管理 主要提高高效的内存管理,包含内存分配,内存回收. 网通通道 复制网络通信,例如实现对NIO.OIO等底层JA ...

  2. Netty IO线程模型学习总结

    Netty框架的 主要线程是IO线程.线程模型的好坏直接决定了系统的吞吐量.并发性和安全性. Netty的线程模型遵循了Reactor的基础线程模型.以下我们先一起看下该模型 Reactor线程模型 ...

  3. Netty Reactor 线程模型笔记

    引用: https://www.cnblogs.com/TomSnail/p/6158249.html https://www.cnblogs.com/heavenhome/articles/6554 ...

  4. netty之==线程模型

    1.1 netty线程模型本质遵循了Reactor的基础线程模型,所以得先介绍Reactor模型  1.2  Reactor模型 无论是C++还是Java编写的网络框架,大多数都是基于Reactor模 ...

  5. netty源码解解析(4.0)-4 线程模型-概览

    netty线程体系概览 netty的高并发能力很大程度上由它的线程模型决定的,netty定义了两种类型的线程: I/O线程: EventLoop, EventLoopGroup.一个EventLoop ...

  6. netty reactor线程模型分析

    netty4线程模型 ServerBootstrap http示例 // Configure the server. EventLoopGroup bossGroup = new EpollEvent ...

  7. Tomcat 7服务器线程模型

    Tomcat 7服务器网络处理主要由NioEndpoint,其处理客户端连接的主要流程如图所示图中Acceptor及Worker分别是以线程池形式存在,Poller是一个单线程.注意,与BIO的实现一 ...

  8. 【Netty源码分析】Reactor线程模型

    1. 背景 1.1. Java线程模型的演进 1.1.1. 单线程 时间回到十几年前,那时主流的CPU都还是单核(除了商用高性能的小机),CPU的核心频率是机器最重要的指标之一. 在Java领域当时比 ...

  9. Netty系列之Netty线程模型

    Reference: http://www.infoq.com/cn/articles/netty-threading-model 1. 背景 1.1. Java线程模型的演进 1.1.1. 单线程 ...

随机推荐

  1. django之创建第8个项目-数据库配置及同步研究

    1.sqlitestudio-2.1.5数据库可视化工具--百度云盘下载 2.编写C:\djangoweb\helloworld\blog\models.py文件 # Create your mode ...

  2. VS2010 lib和dll导出路径设置

    创建库文件工程时往往需要设置.lib文件和.dll文件的路径. 假设一个solution对应了多个工程,然而他们共用一些库,就可以在solution文件夹下分别添加两个文件夹lib和bin(其实放在一 ...

  3. 使用u盘安装os x系统

    从 App Store 下载 OS X 安装器 请执行以下步骤下载要安装的 OS X 版本. 1. 从 Apple 菜单中,选取“App Store”. 2. 按住 Option 键并点按“已购项目” ...

  4. Apk反编译助手

    Android是一个让人很纠结的产品,它开源开放,但是也有很多蛋疼坑爹的地方,还是那句话,谁开发谁知道! Apk反编译助手,懒人和对command line无爱的同学们可下载看看,至少需要.net 2 ...

  5. CSS的子选择器与后代选择器的区别

    来源于:http://www.jianshu.com/p/599654ba5f4a 子选择器: 一个比较有用的选择器子选择器,即大于符号(>),用于选择指定标签元素的第一代子元素.如右侧代码编辑 ...

  6. 航信电子发票开发(servlet请求方式)

    在系统用户交费后,需要打印发票,可以选择普票或者机打票(票据信息在系统中自定义设置的),也可以打印电子发票,这里对接的是航信的电子发票,请求方式非web服务,而是使用servlet通过HTTP请求的方 ...

  7. 大道至简的C语言内存管理

    C语言内存的开辟和释放需要程序员自己来实现. 而C语言的内存开辟和释放只在stdlib.h里面提供了四个函数,这么简洁的设计就足以完成一切工作. C++里面各种类型的指针漫天飞舞,显得啰嗦冗余. ca ...

  8. 简单的Java串口通讯应答示例

    java串口通讯第一次使用,找的资料都比较麻烦,一时没有理出头绪,自己在示例的基础上整理了一个简单的应答示例,比较简陋,但演示了java串口通讯的基本过程. package com.garfield. ...

  9. java struts2入门学习---中文验证、对错误消息的分离、结果(result)类型细节配置

    一.需求 利用struts2实现中文验证并对错误消息的抽离. 详细需求:用户登录-->不填写用户名-->页面跳转到用户登录页面,提示用户名必填(以英文和中文两种方式提示)-->填写英 ...

  10. linux下yum命令出现Loaded plugins: fastestmirror Determining fastest mirrors

    今天yum install的时候出问题了,找了半天才找到一个可行的解决办法 fastestmirror是yum的一个加速插件,这里是插件提示信息是插件不能用了. 不能用就先别用呗,禁用掉,先yum了再 ...