常见并发服务器实现方案分析

首先,我们要清楚,什么是并发?什么是多线程?为什么需要多线程?为什么需要并发服务器?

为什么要使用多线程技术

为什么多线程会提高响应速度?

我们知道,线程一般分为:I/O线程(这里特指网络I/O,每个reactor就是一个IO线程);计算线程,==》耗费cpu;第三方库所用线程,如logging,又比如database//这些操作不应该放在计算线程中执行。

例如记录日志,多个线程写日志,由于文件操作比较慢,服务线程会等在IO上,让CPU空闲,增加响应时间。

解决办法:单独用一个logging线程负责收集日志消息,并写入磁盘文件,通过BlockingQueue提供对外接口,别的线程要写日志时,往队列中塞就行,这样服务线程的计算和logging线程的磁盘IO就可以重叠。将耗时的IO操作单独作为一个线程,不去影响其他计算线程的运行,提高其他线程的响应时间。如果异步io成熟的话,可以使用protator模式,(还可以使单个线程io和计算重叠)

多线程能提高并发度吗?

如果指的是“并发连接数”,不能。假如单纯采用 thread per connection 的模型,那么并发连接数大约300,这远远低于基于事件的单线程程序所能轻松达到的并发连接数(几千上万,甚至几万)。所谓“基于事件”,指的是用 IO multiplexing event loop 的编程模型,又称 Reactor 模式。

多线程能提高吞吐量吗?

对于计算密集型服务,不能。如果要在一个8核的机器上压缩100个1G的文本文件,每个core的处理能力为200MB/s,那么“每次起8个进程,一个进程压缩一个文件”与“只启动一个进程(8个线程并发压缩一个文件)”,这两种方式总耗时相当,但是第二种方式能较快的拿到第一个压缩完的文件。

实现方案:

一、循环式/迭代式服务器实现

特点:

每处理完一个请求就关闭连接(短连接),循环式服务器只能使用短连接 (使用长连接不能及时 响应多客户端请求)。此类服务器为单线程应用程序,不能充分利用多核CPU性能。

二、并发式(concurrent)服务器

特点:

1、one connection per process/one connection per thread  一个进程/线程 一个连接 可以处理多个客户端请求

2、适合执行时间比较长的服务

三、预先创建线程/进程 prefork or pre threaded

特点:

1、预先创建若干个子进程,在循环中创建连接。子进程负责与客户端连接。

2、与并发服务器相比,减少了创建线程/进程的开销,提高响应速度

3、但需要解决:客户端连接过来,多个进程都处于accept状态,但只有一个进程返回值正确,其他都失败----惊群

四、反应式(reactive)服务器(reactor模式)

特点:

1、并发处理多个请求。实际上是在一个线程中完成。无法充分利用多核CPU 。

2、不适合执行时间比较长的服务,为了让客户端感觉是在“并发”处理而不是“循环”处理,每个请求必须在相对较短时间内执行。

3、客户端请求过来,首先将监听套接字加入,关注它的可读事件,有可读事件时,acceptor接受连接,接受连接后,返回已连接套接字,加入reactor中,由dispatch分派事件,-->读取-解包-计算-打包-发送。 所有处理都是单线程轮询,在一个线程中完成,不能充分利用多核CPU

五、reactor + thread per request

在方案4基础上,每个轮询过来创建一个线程,这样就可以充分利用多CPU性能。

六、reactor + worker thread

每个连接在一个工作中的线程完成     ---能够充分利用多CPU。但是没有方案2并发式服务器效率高,因为多了reactor事件循环。

七、reactor + thread pool

针对第5种模式reactor + thread per request的改进--能适应密集计算

这里线程池大小的选择:

如果池中执行任务时,密集计算所占时间比重为P(0<P<=1),而系统一共有C个CPU,为了让C个CPU跑满而不过载,线程池大小的经验公式T=C/P,即T*P=C(让CPU刚好跑满 )

假设C=8,P=1.0,线程池的任务完全密集计算,只要8个活动线程就能让CPU饱和

假设C=8,P=0.5,线程池的任务有一半是计算,一半是IO,那么T=16,也就是16个“50%繁忙的线程”能让8个CPU忙个不停。

(当P < 0.2时这个公式不再适合)

八、multiple reactors(能适应更大的突发IO) 

特点:

1、reactors in threads(one loop per thread)   事件循环Event Loop 。

2、reactors in processes 每个reactor都是一个进程或者线程。也可以看做一个线程池,线程池数目根据千兆网卡个数确定

3、(轮叫方式round robin)mainReactor将监听套接字加入,每当客户端连接到来,监听套接字产生可读事件,acceptor返回已连接套接字,将已连接套接字加入subReactor中,用其处理客户端连接 再来一个连接,重新分配一个subReactor ,这些连接均匀分配到这些reactor中,每个连接只能在一个reactor线程中处理,一个Reactor一般可以适应一个千兆网口。

九、multiple reactors+thread pool (one loop per thread + threadpool )应对突发IO与密集计算

特点:

1、两个线程池:一个IO线程池、一个处理线程池

2、多个subReactor共享一个线程池 每个subreactor把处理耗时操作放至线程池中处理

十、proactor服务器(proactor模式,基于异步I/O)==》前面的服务器全是同步IO 。

理论上proactor比reactor效率要高一些 。

特点:

1、异步IO能够让IO操作与计算重叠。充分利用了硬件的DMA特性-直接存储访问,不需要CPU干预 。

2、linux异步IO实现方式:     

        glibc aio(aio_*),有bug     

        kernel native aio (io_*),也不完美。目前仅支持O_DIRECT方式对磁盘读写,跳过系统缓存,有自己实现缓存,难度不小。 。

3、boost asio实现的proactor,实际上不是真正意义上的异步IO,底层是用epoll来实现的,模拟异步IO的

4、异步IO与同步IO的非阻塞模式的区别:异步IO是不阻塞的,同步IO的非阻塞模式 设置描述符属性:O_NONBLOCK,调用read函数来接收,(如果数据没有准备好,立刻返回,不阻塞),需要一直read -- 》  会处于忙等待状态。

5、异步IO与IO复用区别: IO复用仅仅能够得到通知--内核中有数据了,得到通知后,数据仍然在内核缓冲区中,IO并没有完成,没有到用户空间中,仍然需要调用read函数将数据拉到应用程序缓冲区中。

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值