Closed cjqCN closed 6 years ago
@cjqCN
并不是线程数越大越好,线程数越多带来的上下文切换也是非常消耗效率的。
一般 IO
性质的任务确实应当要比 CPU密集
性任务要的线程数更多,但 2*CPU
也是业界比较认可的一个配置。
@crossoverJie
是的,确实不是线程数越大越好。但是,也要考虑到业务逻辑中同步IO操作,这里的线程切换带来的损耗远小于work线程阻塞带来的性能下降。
你可以测试一下,在业务逻辑中模拟一些例如数据库读写的IO耗时操作,
线程休眠Thread.sleep(1000L)
就是一个很简单、快捷的模拟方法,然后进行压测。
你会发现在一定的范围内,并发数、吞吐量与work线程数成正比,同时也仅仅受限于work线程数。
tomcat
nio的默认线程数是200,通常也大于2*CPU
,并且是可配置的,也是有一定的道理的。
@cjqCN
但其实大多数使用 Tomcat
性能不够时很少会去调整它的核心线程数,要么是优化业务代码,要么就是横向水平扩容。
Tomcat
是基于 servlet 的容器,同时还要支持多个应用的运行,和Cicada
的定位完全是不一样的。
而在 IO
线程耗时操作的业务我觉得有两种方式解决:
正是因为是一个通用的框架,所以才应该做成可配置的。
换一种说法来说,
HttpDispatcher
占用的是worker的线程资源,也就是占用了IO线程。
HttpDispatcher
中的业务逻辑处理出了阻塞,会影响整个框架的IO读写。极端点来说,如果前面数个请求将worker线程同时都占用的话,并且无法及时释放的话,cicada
将无法处理接受新的连接 (无法解码、路由等)。
如果要将HttpDispatcher
独立于worker线程池的话,应如下配置:
public class CicadaInitializer extends ChannelInitializer<Channel> {
private static EventLoopGroup dispatcher;
@Override
protected void initChannel(Channel ch) throws Exception {
ch.pipeline()
.addLast(new HttpRequestDecoder())
.addLast(new HttpResponseEncoder())
.addLast(dispatcher, new HttpDispatcher())
.addLast("logging", new LoggingHandler(LogLevel.INFO));
}
}
或者单独将HttpDispatcher
中的action.execute(CicadaContext.getContext(),paramMap)
用新的线程池去处理,以减少业务对框架的影响。你觉得呢?
@cjqCN 这就是我上面说的两种方案:
而在 IO 线程耗时操作的业务我觉得有两种方式解决:
- 使用者自行根据业务选择新开线程池的方式,而不是依赖框架。因为一个通用的框架并不知道你想要的合适线程数是多少。
- 框架支持纯异步的请求,其实也是在内部新开线程池的方式。
我不建议自定义 IO
线程的原因有几个:
cicada
对使用者应当是一个黑盒,不需要关系其内部的实现。2 * CPU
已经是一种比较合理的配置,对于没有经验的开发者来说盲目的增大或调小这个配置甚至还会降低性能。Action
中自己配置线程池。因为不同的业务需要的线程数配置都不一样,交由业务自己判断是比较合理的(PS:只要业务不阻塞请求,这样也不会阻塞 IO 线程。)。
不打开keepalive的情况下,像http这种短连接协议,很大程度上是没有用到nio的特性的。也就是说,io线程数决定并发数,在netty中也就是work线程,NioEventLoopGroup默认的线程是cpu*2,对于HTTP服务器来说是远远不够的。将这个值做成用户可配置的是否会更好?