高可用性 TCP 服务器应用程序
High-Availability TCP server application
在我的项目中,我有一个云托管虚拟机运行一个 C# 应用程序,它需要:
- 接受来自多个外部客户端(大约 500 个)的 TCP 连接
- 从连接的客户端异步接收数据(频率不高,大约每分钟 1 条消息)
- 对接收到的数据做一些处理
- 将接收到的数据转发给其他参与者
- 回复连接的客户端并可能进行一些异步发送(基于内部时间检查)
在我看来,设计非常简单。我提供了一个接受传入 TCP 连接的侦听器,当建立新连接时,会产生一个新线程;该线程在循环中运行(执行从 2 到 5 的活动点)并检查关联的套接字活动性(如果套接字已死,则线程退出循环并最终终止;稍后将尝试从套接字所属的外部客户端建立新连接到).
所以现在的问题是,对于数量有限的外部客户端(我会说 200/300),一切都运行顺利,但是随着数量的增长(或者当客户端以更高的频率发送数据时),通信变得非常缓慢并受阻。
我正在考虑一些更好的设计,例如:
- 使用任务而不是线程
- 使用线程池
- 用类似 1Thread10Socket 的东西替换 1Thread1Socket
甚至一些扩展策略:
- 在同一应用程序中打开两个不同的 TCP 侦听器(不同的端口)(重新配置客户端,以便其中一半针对每个侦听器)
- 在同一个虚拟机上提供两个具有两个不同 TCP 侦听器(不同端口)的相同应用程序
- 设置两个不同的虚拟机,每个虚拟机都使用相同的应用程序 运行(重新配置客户端,以便其中一半针对每个虚拟机地址)
最后的问题是:目前的设计是糟糕还是幼稚?你认为我处理沟通的方式有什么重要的关键吗?您是否有更强大和更有效的选择(在上述选项中,或任何其他选项)?
谢谢
听众的数量不太可能成为限制因素。在 Stack Overflow,我们每个实例处理大约 60k 个套接字,我们需要多个侦听器的唯一原因是我们可以将流量拆分到多个端口 以避免负载均衡器 的短暂端口耗尽。同样,我应该注意到 处的那些 60k 每个实例套接字服务器 运行 基本上 为零 CPU,因此:考虑多个 exe、VM、等等。那是 而不是 的问题。问题出在代码上,将糟糕的套接字基础结构分布在多个进程上只会隐藏问题。
编写高性能套接字服务器困难,但好消息是:您可以避免其中的大部分。 Kestrel(ASP.NET 核心 http 服务器)可以作为一个非常好的 TCP 服务器,处理大部分可怕的异步、套接字、缓冲区管理等为你,所以你只需要担心实际的数据处理。 "pipelines" API 甚至会为您处理后台缓冲区,因此您无需担心过度读取。
my 3-and-a-bit part blog series starting here - it is simply way too much information to try and post here. But it links through to a demo server - a dummy redis server hosted via Kestrel 对此进行了广泛的演练。它也可以在 没有 Kestrel 的情况下使用 Pipelines.Sockets.Unofficial 托管,但是...坦率地说,我会使用 Kestrel。那里显示的服务器与我们的 60k-per-instance web-socket 层大致相似(就广泛的初始化而言 - 而不是它所做的实际事情)。
在我的项目中,我有一个云托管虚拟机运行一个 C# 应用程序,它需要:
- 接受来自多个外部客户端(大约 500 个)的 TCP 连接
- 从连接的客户端异步接收数据(频率不高,大约每分钟 1 条消息)
- 对接收到的数据做一些处理
- 将接收到的数据转发给其他参与者
- 回复连接的客户端并可能进行一些异步发送(基于内部时间检查)
在我看来,设计非常简单。我提供了一个接受传入 TCP 连接的侦听器,当建立新连接时,会产生一个新线程;该线程在循环中运行(执行从 2 到 5 的活动点)并检查关联的套接字活动性(如果套接字已死,则线程退出循环并最终终止;稍后将尝试从套接字所属的外部客户端建立新连接到).
所以现在的问题是,对于数量有限的外部客户端(我会说 200/300),一切都运行顺利,但是随着数量的增长(或者当客户端以更高的频率发送数据时),通信变得非常缓慢并受阻。
我正在考虑一些更好的设计,例如:
- 使用任务而不是线程
- 使用线程池
- 用类似 1Thread10Socket 的东西替换 1Thread1Socket
甚至一些扩展策略:
- 在同一应用程序中打开两个不同的 TCP 侦听器(不同的端口)(重新配置客户端,以便其中一半针对每个侦听器)
- 在同一个虚拟机上提供两个具有两个不同 TCP 侦听器(不同端口)的相同应用程序
- 设置两个不同的虚拟机,每个虚拟机都使用相同的应用程序 运行(重新配置客户端,以便其中一半针对每个虚拟机地址)
最后的问题是:目前的设计是糟糕还是幼稚?你认为我处理沟通的方式有什么重要的关键吗?您是否有更强大和更有效的选择(在上述选项中,或任何其他选项)?
谢谢
听众的数量不太可能成为限制因素。在 Stack Overflow,我们每个实例处理大约 60k 个套接字,我们需要多个侦听器的唯一原因是我们可以将流量拆分到多个端口 以避免负载均衡器 的短暂端口耗尽。同样,我应该注意到 处的那些 60k 每个实例套接字服务器 运行 基本上 为零 CPU,因此:考虑多个 exe、VM、等等。那是 而不是 的问题。问题出在代码上,将糟糕的套接字基础结构分布在多个进程上只会隐藏问题。
编写高性能套接字服务器困难,但好消息是:您可以避免其中的大部分。 Kestrel(ASP.NET 核心 http 服务器)可以作为一个非常好的 TCP 服务器,处理大部分可怕的异步、套接字、缓冲区管理等为你,所以你只需要担心实际的数据处理。 "pipelines" API 甚至会为您处理后台缓冲区,因此您无需担心过度读取。
my 3-and-a-bit part blog series starting here - it is simply way too much information to try and post here. But it links through to a demo server - a dummy redis server hosted via Kestrel 对此进行了广泛的演练。它也可以在 没有 Kestrel 的情况下使用 Pipelines.Sockets.Unofficial 托管,但是...坦率地说,我会使用 Kestrel。那里显示的服务器与我们的 60k-per-instance web-socket 层大致相似(就广泛的初始化而言 - 而不是它所做的实际事情)。