Netty服务器线程模型概览
2015-09-10 11:08
453 查看
写的不错,转来学习。可以让思维很清晰。
一切从ServerBootstrap开始
ServerBootstrap 负责初始话netty服务器,并且开始监听端口的socket请求。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来实现这一部分。
对于Oio来说,从始到终,都是由一个指定的worker来处理。
减少worker线程的处理占用时间
worker 线程是由netty内部管理,统一调配的一种资源,所以最好应该尽快的把让worker线程执行完毕,返回给线程池回收利用。worker线程的大部分时 间消耗在在ChannelPipeline的各种handler中,而在这些handler中,一般是负责应用程序业务逻辑掺入的那个handler最占 时间,它通常是排在最后的UpstreamHandler。所以通过把这部分处理内容交给另外一个线程来处理,可以有效的减少worker线程的周期循环 时间。一般有两种方法:messageReceived()方法中开启一个新的线程来处理业务逻辑
利用netty框架自带的ExecutionHandler
基本使用方法:它的构造方法是ExecutionHandler(Executor executor) ,很显然executor就是ExecutionHandler内部管理的线程池了。netty额外给我们提供了两种线程池:
MemoryAwareThreadPoolExecutor和OrderedMemoryAwareThreadPoolExecutor,它们都在org.jboss.netty.handler.execution 包下。
MemoryAwareThreadPoolExecutor 确保jvm不会因为过多的线程而导致内存溢出错误,OrderedMemoryAwareThreadPoolExecutor是前一个线程池的子类,除 了保证没有内存溢出之外,还可以保证channel event的处理次序。具体可以查看API文档,上面有详细说明。
Netty服务端启动步骤:
代码:
NIO处理方式:
1.Netty用一个BOSS线程去处理客户端的接入,创建Channel
2.从WORK线程池(WORK线程数量默认为cpu cores的2倍)拿出一个WORK线程交给BOSS创建好的Channel实例(Channel实例持有java网络对象)
3.WORK线程进行数据读入(读到ChannelBuffer)
4.接着触发相应的事件传递给ChannelPipeline进行业务处理(ChannelPipeline中包含一系列用户自定义的ChannelHandler组成的链)
有 一点要注意的是,执行整个ChannelHandler链这个过程是串行的,如果业务逻辑(比如DB操作)比较耗时,会导致WORK线程长时间被占用得不 到释放,最终影响整个服务端的并发处理能力,所以一般我们通过ExecutionHandler线程池来异步处理ChannelHandler调用链,使 得WORK线程经过ExecutionHandler时得到释放。
要解决这个问题增加下面代码即可:
1) MemoryAwareThreadPoolExecutor
通过对线程池内存的使用控制,可控制Executor中待处理任务的上限(超过上限时,后续进来的任务将被阻塞),并可控制单个Channel待处理任务的上限,防止内存溢出错误;
2) OrderedMemoryAwareThreadPoolExecutor
是 1)的子类。除了MemoryAwareThreadPoolExecutor 的功能之外,它还可以保证同一Channel中处理的事件流的顺序性,这主要是控制事件在异步处理模式下可能出现的错误的事件顺序,但它并不保证同一 Channel中的事件都在一个线程中执行,也没必要保证这个。
相关文章推荐
- PHP整型溢出
- Linux中与内核通信的Netlink机制(实例)
- Python编程在Windows下使用Eclipse和PyDev搭建编程环境
- Android使用ORM思想封装数据库
- JS中replace()用法举例
- curl http_build_query踩过的坑
- 日历代码
- 15_09_10 英语学习
- XCode快捷键
- 安装spm时phantomjs安装失败的解决方案
- DB2 版本发布历史
- 使用volley实现android的三级缓存
- RequireJS API
- 译:Android Platform Guide(Android 平台指南)
- [转]Dialog
- 多控制器和导航控制器简单介绍
- 以工作表的方式合并excel
- C++编译器符号表有哪些内容?
- [Linux][ubuntu][Perl]bad interpreter: No such file or directory
- Button 图片和标题位置居中问题