设为首页 加入收藏

TOP

6.6.2 常见的并发网络服务程序设计方案(2)
2013-10-07 16:03:48 来源: 作者: 【 】 浏览:56
Tags:6.6.2 常见 并发 网络服务 程序设计 方案

6.6.2 常见的并发网络服务程序设计方案(2)

L6~L13 是echo 服务的“业务逻辑循环”,从L21~L24 可以看出它一次只能服务一个客户连接。后面列举的方案都是在保持这个循环的功能不变的情况下,设法能高效地同时服务多个客户端。L9 代码值得商榷,或许应该用sendall() 函数,以确保完整地发回数据。

方案1 这是传统的Unix 并发网络编程(www.cppentry.com)方案,[UNP] 称之为child-per-client 或fork()-per-client,另外也俗称process-per-connection。这种方案适合并发连接数不大的情况。至今仍有一些网络服务程序用这种方式实现,比如PostgreSQL 和Perforce的服务端。这种方案适合“计算响应的工作量远大于fork() 的开销”这种情况,比如数据库服务器。这种方案适合长连接,但不太适合短连接,因为fork() 开销大于求解Sudoku 的用时。

Python 示例如下,注意其中L9~L16 正是前面的业务逻辑循环,self.request 代替了前面的client_socket。ForkingTCPServer 会对每个客户连接新建一个子进程,在子进程中调用EchoHandler.handle(),从而同时服务多个客户端。在这种编程(www.cppentry.com)方式中,业务逻辑已经初步从网络框架分离出来,但是仍然和IO 紧密结合。

  1. recipes/python/echo-fork.py  
  2. 1 #!/usr/bin/python  
  3. 2  
  4. 3 from SocketServer import BaseRequestHandler, TCPServer  
  5. 4 from SocketServer import ForkingTCPServer, ThreadingTCPServer  
  6. 5  
  7. 6 class EchoHandler(BaseRequestHandler):  
  8. 7 def handle(self):  
  9. 8 print "got connection from", self.client_address  
  10. 9 while True:  
  11. 10 data = self.request.recv(4096)  
  12. 11 if data:  
  13. 12 sent = self.request.send(data) # sendall  
  14. 13 else:  
  15. 14 print "disconnect", self.client_address  
  16. 15 self.request.close()  
  17. 16 break  
  18. 17  
  19. 18 if __name__ == "__main__":  
  20. 19 listen_address = ("0.0.0.0", 2007)  
  21. 20 server = ForkingTCPServer(listen_address, EchoHandler)  
  22. 21 server.serve_forever()  
  23. recipes/python/echo-fork.py 

方案2 这是传统的Java 网络编程(www.cppentry.com)方案thread-per-connection,在Java 1.4 引入NIO 之前,Java 网络服务多采用这种方案。它的初始化开销比方案1 要小很多,但与求解Sudoku 的用时差不多,仍然不适合短连接服务。这种方案的伸缩性受到线程数的限制,一两百个还行,几千个的话对操作系统的scheduler 恐怕是个不小的负担。

Python 示例如下,只改动了一行代码。ThreadingTCPServer 会对每个客户连接新建一个线程,在该线程中调用EchoHandler.handle()。

  1. $ diff -U2 echo-fork.py echo-thread.py  
  2. if __name__ == "__main__":  
  3. listen_address = ("0.0.0.0", 2007)  
  4. server = ForkingTCPServer(listen_address, EchoHandler)  
  5. server = ThreadingTCPServer(listen_address, EchoHandler)  
  6. server.serve_forever() 

这里再次体现了将“并发策略”与业务逻辑(EchoHandler.handle())分离的思路。用同样的思路重写方案0 的代码,可得到:
  1. $ diff -U2 echo-fork.py echo-single.py  
  2. if __name__ == "__main__":  
  3. listen_address = ("0.0.0.0", 2007)  
  4. server = ForkingTCPServer(listen_address, EchoHandler)  
  5. server = TCPServer(listen_address, EchoHandler)  
  6. server.serve_forever() 

方案3 这是针对方案1 的优化,[UNP] 详细分析了几种变化,包括对accept(2)“惊群”问题(thundering herd)的考虑。
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇6.6.2 常见的并发网络服务程序设.. 下一篇6.6.2 常见的并发网络服务程序设..

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容:

·用 C 语言或者限制使 (2025-12-25 08:50:05)
·C++构造shared_ptr为 (2025-12-25 08:50:01)
·既然引用计数在做 GC (2025-12-25 08:49:59)
·Java 编程和 c 语言 (2025-12-25 08:19:48)
·. net内存管理宝典这 (2025-12-25 08:19:46)