TCP并发模型设计

         简单总结一下常见的TCP并发模型,主要加强和巩固,能够在各类应用中更好的实践。

         其中ise实现了6、7、9、10

         Memcached并发模型为9,一个libevnet负责监听,其他处理socket的读写。

         GreenSQL并发模型为6,只是做一个代理。

1及时创建型多进程模型

         这是传统的 Unix 网络编程并发模型。每当接收到一个客户端的连接后,便立即 fork 出一个新的子进程来为这个客户端服务。服务完毕后,子进程退出。所以,这种模型也叫“process per connection”。这种模型并不适合并发量较大的场合,因为 fork 的开销太大。

2即时创建型多线程并发模型

         与即时创建进程相比,创建一个线程的开销要小得多。但因为受到操作系统对单个进程内线程数量的限制,该模型仍然无法适用于大并发量的情况。另外,线程数量太多,会给操作系统带来严重的线程切换开销。

3预创建型多进程并发模型

这是对模型1的优化。为了避免不断地创建和销毁进程,该模型在程序初始化时预先创建好了一批子进程。Apache httpd 使用了这种模型。

4预创建型多线程并发模型

         这是对模型2的优化。为了避免不断地创建和销毁线程,该模型在程序初始化时预先创建好了一批线程(线程池、任务调度)。Apache httpd 除了使用模型3外,也使用了这种模型。

5基于select的小规模IO多路复用并发模型

         IO多路复用(IOmultiplexing)是为了避免进程或线程阻塞于某个特定的IO系统调用而产生的技术。IO多路复用技术使得单个进程或线程可以同时关注多个socket的变化。Linux 平台下的相应机制有select、poll、epoll,以及更高层的封装libevent;Windows 平台下则有 select、Overlapped IO、IOCP 等。该模型使用 select 作为 IO 多路复用的基础。它之所以被冠以“小规模”,是因为 select 调用能同时关注的 socket 的数量受到操作系统的限制:在 Linux 平台下,这个限制一般是 1024;而在 Windows 平台下则更小,一般是 64。该模型由于受到 select 的限制,所以并不实用,也需根据实践情况考虑问题。

6基于IO多路复用的单线程并发模型

除模型5外,其它模型都不使用select,而是采用伸缩性更佳的Linux::EPoll 或 Windows::IOCP。在这种模型中,只有一个线程,既负责 IO,又负责业务逻辑计算,虽然能同时应对很多并发连接,但较难发挥多核威力。所以,这种模型适合IO密集型应用,而不太适合CPU密集型应用。

7基于IO多路复用的单线程IO加工作线程池并发模型

在这个模型中,一个线程负责IO,另增加一个固定大小的线程池(根据计算密集程度和CPU 数量确定线程池的大小),用于业务逻辑计算。IO 线程接收到客户端的任务后,将任务转移至工作线程池,线程池完成任务后再交由 IO 线程完成最后的对客户端的应答。

这种模型很好地弥补了模型6的不足。既能利用IO多路复用处理并发连接,又能借助工作线程池充分发挥多核优势。在 IO 压力不大时,此模型能很好地满足一般需求。当 IO 压力大时,可考虑采用“多事件循环”方案,见模型9和模型10。

8基于IO多路复用的单事件循环多线程并发模型

在Reactor/Proactor模式中,程序执行点由多路事件选择器(demultiplexor)开始,运行至事件处理器,再返回至多路事件选择器,这个循环称为“事件循环(event loop)”。在具体实现中,这个循环中可能有事件发生,也可能没有。当没有事件发生时,实际上是一个什么也没做的空循环。事件循环的运转要由线程(或进程)来驱动。一个事件循环可以由一个线程来驱动,也可以由多个线程来驱动(事件发生时,多路事件选择器只将事件传递给一个线程)。反过来,一个线程无法同时驱动多个事件循环。

在这种模型中,只使用单个事件循环(即只有一个多路事件选择器),但是有多个线程在驱动。这是典型的Windows::IOCP 的做法。在IOCP中,一般只创建一个“完成端口句柄”,同时会根据CPU核数创建若干个线程。这些线程都参与事件循环(调用 API: GetQueuedCompletionStatus )。这种模型与Windows::IOCP非常匹配,由于Windows 操作系统内置了高效的异步机制,在即使只有一个“完成端口句柄”的情况下仍能高效地工作,而多线程可以充分利用 CPU 资源。

在多数场合下,这种模型能很好地工作。但由于单事件循环的特性 (即单个事件循环中管理着所有并 发连接),使得应用层很难区分不同连接的优先级;另外,单个连接上的不同请求会被分配到不同的线程,使得请求处理的顺序性很难得到保证。

9基于IO多路复用的多事件循环多线程并发模型

基于目前操作系统的性能,对于一个网络程序,一个事件循环 1就足以应付千兆以太网的网络IO。在一个程序使用多个事件循环更多的是基于其它方面的考虑,比如利用多事件循环来区别对待不同优先级的连接。这种模型就是这样,它使用了多个事件循环(即多个多路事件选择器),并为每个事件循环只分配一个线程。事件循环数量与线程数量相等,数量大小一般由 CPU 核数确定。

   这种模型中,多线程模式保证了对 CPU 资源的充分利用;并发 IO 请求由多个事件循环共同承担,避免突发IO导致单个事件循环的负载饱和;另外,一个连接被分配到一个事件循环后,完全由一个线程管理,保证了连接上请求处理的顺序性。与模型7相比,由于此模型使用了多线程IO,所以在处理突发IO时更能应对自如,虽然少了工作线程池,但小规模计算仍可以在当前IO线程中进行。此外,由于使用了多个事件循环,能够处理连接的优先级,可以将优先级高的连接分配到单独的事件循环中。

10基于IO多路复用的多事件循环多进程并发模型

         与模型9相比,这个模型将线程换成了进程。Nginx 采用的正是这个模型。

11基于IO多路复用的多事件循环多线程加工作线程池并发模型

         在模型9在基础上增加工作线程池,就形成了这种并发模型。这种模型适用于既有大规模IO并发请求,又存在大量的计算任务的情形。如同模型7的做法一样,IO线程并不参与计算,而是把计算任务转移到工作线程池中。

### Python 中 TCP 并发模型的实现方式 #### 多线程并发模型 在多线程模式下,每个新的客户端连接都会创建一个新的线程来处理通信。这种方法简单直观,适合于CPU密集型任务较少的应用场景。 对于Python来说,`threading`模块提供了简单的接口用于启动新线程: ```python import socket from threading import Thread def handle_client(client_socket): request = client_socket.recv(1024) print(f"Received: {request}") client_socket.send(b"ACK") client_socket.close() server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(('localhost', 9999)) server.listen(5) while True: client_sock, addr = server.accept() thread = Thread(target=handle_client, args=(client_sock,)) thread.start() ``` 此代码片段展示了如何使用多线程处理多个客户端请求[^1]。 #### 异步IO并发模型 随着Python 3.4引入标准库`asyncio`,异步编程成为一种更高效的选择。该模型允许在一个进程中同时运行大量轻量级的任务——协程(coroutine),并通过事件循环(event loop)协调这些任务之间的执行顺序。 下面的例子说明了怎样利用`asyncio`搭建一个基本的服务端应用: ```python import asyncio async def handle_echo(reader, writer): data = await reader.read(100) message = data.decode() addr = writer.get_extra_info('peername') print(f"Received {message} from {addr}") print("Send: %r" % message.upper()) writer.write(data.upper()) await writer.drain() print("Close the connection") writer.close() async def main(): server = await asyncio.start_server( handle_echo, '127.0.0.1', 8888) async with server: await server.serve_forever() if __name__ == "__main__": try: asyncio.run(main()) except KeyboardInterrupt: pass ``` 这段代码定义了一个异步函数`handle_echo()`作为回调处理器,并通过调用`start_server()`开启监听服务[^4]。 #### 使用gevent进行绿色线程并发 Gevent是一种基于协程的Python网络库,它采用libev或libuv来进行高效的事件驱动I/O操作。相比于传统的多线程或多进程方案,gevent可以在同一时间点上支持更多的并发连接数。 这里有一个简单的例子展示如何用gevent构建服务器: ```python from gevent.server import StreamServer from gevent.monkey import patch_all; patch_all() def handle(sock, address): sock.sendall(b'Connected!\n') while True: data = sock.recv(1024) if not data or data.strip().lower() == b'quit': break sock.sendall(data[::-1]) if __name__ == '__main__': server = StreamServer(('localhost', 1234), handle) server.serve_forever() ``` 上述脚本中,`patch_all()`确保所有的阻塞式API都被替换成了非阻塞版本;之后便可以直接编写看起来像是同步风格但实际上是非阻塞逻辑的代码[^2]. #### Twisted框架下的TCP并发解决方案 Twisted是一个成熟的事件驱动网络引擎,适用于开发各种类型的分布式系统和服务。其核心特性之一便是强大的协议栈抽象层以及灵活的数据传输机制。 以下是使用Twisted建立TCP回声服务器的方式: ```python from twisted.internet.protocol import Factory, Protocol from twisted.internet.endpoints import TCP4ServerEndpoint from twisted.internet import reactor class EchoProtocol(Protocol): def dataReceived(self, data): self.transport.write(data) factory = Factory.forProtocol(EchoProtocol) endpoint = TCP4ServerEndpoint(reactor, 8007) endpoint.listen(factory) reactor.run() ``` 在这个案例里,当接收到任何来自客户端的消息时,就会触发`dataReceived()`方法自动回复相同的内容给对方[^3].
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值