程序员之Reactor高性能IO模式
针对典型的Client/Server模式,服务端接收Client的请求,Server响应Client的请求,具体模式如图:
C/S模式
1.最原始粗暴的Server设计方案
while(true) {
SocketMessage sm = getAccept();
// 从端口获取是否有新的套接字连接请求
if(sm ! = null) {
handle(sm);
}
}
该方法的问题:
a.无法并发访问,效率太低;
b.请求拥塞,前面的没有处理完,后面的请求只能拥塞等待。
2.针对上面的方案改进出了多线程方案,即
while(true) {
SocketMessage sm = getAccept();
// 从端口获取是否有新的套接字连接请求
if(sm ! = null) {
new Thread() {
run() {
handle(sm);
}
}.start();
}
}
早期的tomcat版本确实是这样实现的,这样提高了系统的吞吐量,避免了拥塞。但是该方法对Server系统要求较高,创建线程需要的资源比较多,如果创建的线程太多,系统就会崩溃,而且创建和销毁线程也需要消耗资源。
3.针对上面的方案进行线程池优化
CustomThreadFactory threadFactory = new CustomThreadFactory("ExecutorPool");
BlockingQueue<Runnable> blockingQueue =
new LinkedBlockingQueue<>(2000);
ExecutorService service = new ThreadPoolExecutor(10,
10, 10, TimeUnit.SECONDS, blockingQueue,
threadFactory, new ThreadPoolExecutor.CallerRunsPolicy());
while(true) {
SocketMessage sm = getAccept();
// 从端口获取是否有新的套接字连接请求
if(sm ! = null) {
Thread t = new Thread() {
run() {
handle(sm);
}
};
service.execute(t);
}
}
该方案提出了线程池,线程都在指定参数的线程池中执行,确实比之前好了很多,但是该方案的线程力度太大,每个线程负责一次请求的所有事情,也限制了吞吐量。
4.Reactor模式
为解决上述问题,Reactor模式将线程拆分成不同的Handler,每一种Handler处理一种Event,通过全局的Selector将不同的event分配到不同的handler上,
Server的多线程模式
这种模式一个Handler几乎处理了read->handle->response等所有处理,因此在client连接,Socket input/output 都会进入拥塞,导致性能下降十分快。
Reactor基于事件驱动模式,当event触发时,才会调用对应的handler进行处理.
Reactor负责处理IO事件,当检测到有Event事件进来时,将event发送到Handler去处理,然后处理器处理。
Handler负责非拥塞的行为,标示系统管理的资源,同时将event与handler绑定。
该方案Reactor为单线程,不但处理accept连接,还要讲event发送到handler进行处理,所以对处理器要求较高。
4.1 业务处理多线程方案
业务处理多线程方案将CPU执行的处理放入到线程池中进行处理,但Reactor任然是单线程。
4.2 多CPU多线程方案
4.1方案将业务处理改成多线程执行,但是Reactor任然是单线程,不但处理accept连接,还要讲event发送到handler进行处理。一次可以将Reactor分成两个部分,方案如下:
多CPU多线程方案.jpgmainReactor负责监听连接,accept将连接发送给subReactor处理,这样subReactor就从连接中解脱出来(比如TCP三次连接,慢启动等)。