请先看几行源码:
public NioServerSocketChannelFactory( Executor bossExecutor, Executor workerExecutor) { this(bossExecutor, workerExecutor, SelectorUtil.DEFAULT_IO_THREADS); } public NioServerSocketChannelFactory( Executor bossExecutor, Executor workerExecutor, int workerCount) { if (bossExecutor == null) { throw new NullPointerException("bossExecutor"); } if (workerExecutor == null) { throw new NullPointerException("workerExecutor"); } if (workerCount <= 0) { throw new IllegalArgumentException( "workerCount (" + workerCount + ") " + "must be a positive integer."); } this.bossExecutor = bossExecutor; this.workerExecutor = workerExecutor; sink = new NioServerSocketPipelineSink(workerExecutor, workerCount); } NioServerSocketPipelineSink(Executor workerExecutor, int workerCount) { workers = new NioWorker[workerCount]; for (int i = 0; i < workers.length; i ++) { workers[i] = new NioWorker(id, i + 1, workerExecutor); } }
其中,在构造NioServerSocketChannelFactory方法时,如未指定workcount,系统会默认指定为cup * 2 。 (我的cup是双核)我在这里的workcount的值是4。
在我的测试中,通过观察,只看到最多4个线程在工作。
到这里有了些疑问:
1、如果我在客户端一次创建10个线程,这里只有4个在工作,剩余线程是由workExecutor来缓存,还是由boosExecutor缓存?
2、在构造NioSErverSocketPipelineSink类时,只传递了workExecutor和workcount两个参数,通过观察源码没看到workExecutor有执行execute的地方 !?
3、boosExecutor和workExecutor在Netty中的用途是什么?
4、java api中对Executors.newCachedThreadPool()解释是:
-
创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。对于执行很多短期异步任务的程序而言,这些线程池通常可提高程序性能。调用 execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资源。
-
api中提到长时间保持空闲的线程池不会使用任何资源,但他会在池中没有空闲线程时,给来访者创建新的线程,那么这时,假设我对这个服务发起1000、2000..个并发线程,那么netty在他的用户手册中提到“不再因过快、过慢或超负载连接导致 OutOfMemoryError”,那么netty是如何控制的 ?
5、在我构造ServerBootStrap方法时:
final ServerBootstrap server=new ServerBootstrap( new NioServerSocketChannelFactory( Executors.newCachedThreadPool(), Executors.newCachedThreadPool()) );
把Executors.newCachedThreadPool 替换为:Executors.newFixedThreadPool(n) 会对netty有什么影响?由于不了解boosExecutor和workExecutor的实际用意,所以也产生了低第5个问题 。
请各位朋友帮忙解答一下啊,非常感谢!
呵呵,不知道是问题太简单,还是没人关心这些!!! 不过我想应该有很多人同我有一样的我问题。
至于Executors这个类,我知道他的实际用途,但到了netty这里实在让我不能理解他的用意。
引用来自“xinglu”的答案
难怪了,我用Executors.newFixedThreadPool(n)来替代第二个参数后,实际并发的线程与workcount的关系就复杂了。。
这里的handler你说他是非线程安全的 ?
引用来自“xinglu”的答案
哈哈,谢谢xinglu ,netty对我的业务还蛮实用的,我们之前使用nio的方式实现的 。 在经过测试对比后,netty在I/o方面的性能相当稳定,cpu使用率比nio/bio都平缓很多 。
引用来自“jason5186”的答案
引用来自“xinglu”的答案
哈哈,谢谢xinglu ,netty对我的业务还蛮实用的,我们之前使用nio的方式实现的 。 在经过测试对比后,netty在I/o方面的性能相当稳定,cpu使用率比nio/bio都平缓很多 。
引用来自“xinglu”的答案
引用来自“jason5186”的答案
引用来自“xinglu”的答案
哈哈,谢谢xinglu ,netty对我的业务还蛮实用的,我们之前使用nio的方式实现的 。 在经过测试对比后,netty在I/o方面的性能相当稳定,cpu使用率比nio/bio都平缓很多 。
是公司内部研发的有针对性的测试工具,不过你可以试用一下jprofiler ,这是一个非常不错的工具。
另外,我的测试结果只是针对我的应用,根据应用中的线程经过的路径不同、逻辑不同等等,所以结果也会有大不同。
刚接触这个框架的时候,太急着使用,弄得很慌张,API都没查全就来弄这个了。。。
经过一段的实际应用、java源码、netty源码查了几遍,终于可以操控他了。
我个人认为对于workExecutor没有必要改变他,netty默认会使用CPU * 2来设置,基本都是够用,设置多了也是增加了线程上下文的频繁切换,反而会使线程的执行效率下降。
workExecutor这个线程池是用于处理用户请求的,我们写demo时通常都会由该线程直接调用handler,在执行相应的业务操作/DB操作等等,而实际应用中,基本不会这么做,如果执行的业务比较耗时,且线程的数量只有这几个可以支配,那么无法处理的请求将只能等待。
所以,基于这种情况,我们在配置ChannelPipelineFactory时,加入一个ExecutionHandler(ThreadPools)类,用于分发请求,让workExecutor线程更专注的去处理请求,而非调用业务处理器;而如何分发和分发请求的策略依赖于你的ThreadPools的实现。
当然,netty有已经实现的ThreadPools可以供你可以直接拿来使用,也你也可以自定义一个适合你的业务场景的ThreadPools。
所以,要构造一个ServerBootstrap默认的基本可以满足大部分的通讯场景,更多的需要你去关注的还是ExecutionHandler类。
另外,netty有些版本存在一些小的BUG,如资源未能正确释放,链路检测函数校验不严格等等,需要留意官方的版本修复日志,尽早发现并打上补丁。
结贴吧!