TogetherOS / cicada

🚀 Fast lightweight HTTP service framework.
https://crossoverjie.top/categories/cicada/
Apache License 2.0
953 stars 212 forks source link

并发问题 #19

Closed cjqCN closed 6 years ago

cjqCN commented 6 years ago

不打开keepalive的情况下,像http这种短连接协议,很大程度上是没有用到nio的特性的。也就是说,io线程数决定并发数,在netty中也就是work线程,NioEventLoopGroup默认的线程是cpu*2,对于HTTP服务器来说是远远不够的。将这个值做成用户可配置的是否会更好?

crossoverJie commented 6 years ago

@cjqCN

并不是线程数越大越好,线程数越多带来的上下文切换也是非常消耗效率的。

一般 IO 性质的任务确实应当要比 CPU密集 性任务要的线程数更多,但 2*CPU 也是业界比较认可的一个配置。

cjqCN commented 6 years ago

@crossoverJie 是的,确实不是线程数越大越好。但是,也要考虑到业务逻辑中同步IO操作,这里的线程切换带来的损耗远小于work线程阻塞带来的性能下降。 你可以测试一下,在业务逻辑中模拟一些例如数据库读写的IO耗时操作, 线程休眠Thread.sleep(1000L) 就是一个很简单、快捷的模拟方法,然后进行压测。 你会发现在一定的范围内,并发数、吞吐量与work线程数成正比,同时也仅仅受限于work线程数。 tomcat nio的默认线程数是200,通常也大于2*CPU,并且是可配置的,也是有一定的道理的。

crossoverJie commented 6 years ago

@cjqCN

但其实大多数使用 Tomcat 性能不够时很少会去调整它的核心线程数,要么是优化业务代码,要么就是横向水平扩容。

Tomcat 是基于 servlet 的容器,同时还要支持多个应用的运行,和 Cicada 的定位完全是不一样的。

而在 IO 线程耗时操作的业务我觉得有两种方式解决:

cjqCN commented 6 years ago

正是因为是一个通用的框架,所以才应该做成可配置的。 换一种说法来说, 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)用新的线程池去处理,以减少业务对框架的影响。你觉得呢?

crossoverJie commented 6 years ago

@cjqCN 这就是我上面说的两种方案:

而在 IO 线程耗时操作的业务我觉得有两种方式解决:

  • 使用者自行根据业务选择新开线程池的方式,而不是依赖框架。因为一个通用的框架并不知道你想要的合适线程数是多少。
  • 框架支持纯异步的请求,其实也是在内部新开线程池的方式。

我不建议自定义 IO 线程的原因有几个: