【发布时间】:2011-12-21 14:14:22
【问题描述】:
我正在为一个必须同时处理大量请求并异步处理它们的项目设计一个服务器守护程序。我知道这样一个项目的规模之大,但我对此很认真,并且在继续前进之前正在努力制定清晰的设计和计划。
这是我的目标列表:
- 可扩展性 - 必须能够将架构并行到多个处理器甚至多个服务器上。
- 能够处理大量并行连接。
- 如果单个请求需要很长时间处理,则不得导致阻塞问题。
- 请求响应周转时间必须最短。
- 围绕 .NET 框架构建(将用 C# 编写)
我提出的架构和流程相当复杂,所以这是我最初设计的图表:
(和here it is on tinypic,以防它严重调整大小)
这个想法是请求通过网络进入(尽管我还没有决定 TCP 还是 UDP 是最好的)并立即传递到高速负载平衡器。然后负载均衡器使用加权随机数生成器选择一个请求队列 (RQ) 来放置请求。权重来自每个队列的大小。使用加权 RNG 而不是仅仅将请求放入最不忙的队列的原因是,它可以防止空但阻塞的队列(由于挂起的请求)锁定整个服务器。如果所有 RQ 都超过一定大小,负载均衡器会丢弃请求并将“服务器太忙”响应放入输出队列 (OPQ) - 此部分未显示在图表中。 p>
每个队列对应一个线程,其关联性设置为服务器上的一个 CPU 核心。这些线程是并行请求处理器的一部分,它处理来自每个队列的请求。请求分为以下三种类型之一:
立即 - 顾名思义,立即处理请求。
可延迟 - 可延迟请求被视为低优先级。它们在低负载期间立即处理,或者如果负载高则放入延迟请求队列 (DRQ)。负载均衡器从 DRQ 获取这些延迟请求,将它们标记为立即,然后将它们放回适当的 RQ。
定时 - 定时请求连同它们的目标时间戳一起放入定时请求队列 (TRQ)。这些请求通常是由另一个请求生成的,而不是由客户端显式发送的。当超过请求时间戳时,下一个可用的请求处理器线程将使用它并处理它。
处理请求时,可能会从内存中的键/值对缓存、键/值对缓存或磁盘或专用 SQL 数据库服务器中获取数据。缓存的值是 BSON,索引是字符串。我正在考虑使用Dictionary<T1,T2> 在内存中实现这一点,并为磁盘缓存使用 btree(或类似的)。
处理完成后创建响应,并将其放入输出队列 (OPQ)。然后一个循环消耗来自 OPQ 的响应并通过网络将它们传输回客户端。如果 OPQ 达到其最大大小的 80%,则停止四分之一的请求处理器线程。如果 OPQ 达到其最大大小的 90%,则停止一半的请求处理器线程。如果 OPQ 达到其最大大小,则所有请求处理器线程都将停止。这将通过信号量来实现,它还应该防止单个请求处理器线程被阻塞并留下陈旧的请求。
我正在寻找的是关于几个方面的建议:
- 此架构是否存在我遗漏的重大缺陷?
- 出于性能原因,我应该考虑更改哪些内容?
- TCP 还是 UDP 更适合请求?拥有 TCP 提供的“交付证明”会非常有用,但 UDP 的轻量级特性也很有吸引力。
- 在 Windows 服务器上处理 100k+ 并发连接时,是否需要考虑任何特殊的注意事项?我知道 Linux 的 TCP 堆栈处理得很好,但我对 Windows 不太确定。
- 我还有其他问题需要问吗?我是不是忘了考虑什么?
我知道要阅读的内容很多,可能还有很多问题要问,所以感谢您抽出宝贵的时间。
图表的更新版本here。
【问题讨论】:
-
这个项目进展如何/进展如何?有关于它的博客文章吗?我很想听听您在此过程中学到了什么以及得出了什么结论。
标签: .net networking architecture scalability parallel-processing