知识卡片
每会话一进程与每会话一线程的权衡
内容
应用服务器自身的并发(怎样同时处理多个请求)与前面讨论的数据源并发不同:它不涉及事务,因此打交道时用不上相对受控的事务系统这套工具,处理起来风险更直接——显式的多线程编程配合锁和同步阻塞太复杂,容易埋下几乎无法重现、只在极少数情况下发作的诡异错误,因此策略应该是尽量避免让应用开发者直接接触显式的同步和锁机制。最简单的做法是每会话一进程:每个会话独占一个进程,进程间天然完全隔离,开发者完全不用担心多线程问题(早期Perl Web系统甚至每请求建一个新进程);代价是进程很贵,大量会话意味着大量资源消耗,可以用进程池缓解——每个进程一次只处理一个请求、但能跨时间处理来自不同会话的多个请求,用更少资源撑住同样多会话,隔离性几乎不受影响(Apache mod_perl等大规模系统采用此法),唯一要小心的是必须保证每个请求结束时都彻底释放所占资源。更进一步是每会话一线程:一个进程内跑多个线程,每次请求由某个线程处理,线程比进程轻得多,能用更少硬件扛更多请求、服务器效率更高;代价是线程之间没有隔离,任何线程理论上都能碰到它能访问的任何数据。作者本人的立场是:尽管每会话一进程效率不如每会话一线程,但两者可伸缩性相当,前者健壮性更好(某个线程崩溃可能拖垮整个进程,而进程级隔离能限制这种破坏),对经验较少的团队而言,用硬件成本换取免于处理线程问题(以及随之而来的调试成本)往往是划算的——但作者也坦言很少见到有人真正做过量化的性能对比测试。
结构图:
flowchart LR
A["应用服务器并发策略"] --> B["每会话一进程<br/>(可配合进程池)"]
A --> C["每会话一线程"]
B --> B1["优:进程间完全隔离/健壮性好<br/>缺:资源消耗大(进程池缓解)"]
C --> C1["优:资源占用少/吞吐更高<br/>缺:线程间无隔离,需自行创建隔离区"]
参考来源
- 位置:《企业应用架构模式》第一部分"表述"之"第5章 并发"之"5.7 应用服务器并发"(源文件:_epub-src/OEBPS/Text/000040.html)
- 结论依据:原文说明"最简单的处理办法是使用每会话一进程……每会话一进程带来的问题是大量资源的消耗……可以通过使用进程池来提高利用率……每会话一线程的方式……每会话一线程的问题是线程之间没有隔离……依我们看来,使用每会话一进程有很多可说之处……用硬件代价来避免处理线程的麻烦……是值得的",直接支撑本卡结构图。
- 原始内容:尽管每会话一进程没有每会话一线程的效率高,但它们有相同的可伸缩性。而且有更好的健壮性——如果某个线程崩溃了,可能会导致整个进程垮掉,但是使用每会话一进程能限制这种破坏。