【问题标题】:C# server scalability issue on linuxlinux上的C#服务器可伸缩性问题
【发布时间】:2012-05-26 12:30:57
【问题描述】:

我有一个在 Visual Studio 2010 和 Mono Develop 2.8 上开发的 C# 服务器。 NET 框架 4.0

看起来此服务器在 Windows 上的表现(在可扩展性方面)比在 Linux 上好得多。 我使用 Apache 的 ab 工具在原生 Windows(12 个物理内核)以及 8 个和 12 个内核的 Windows 和 Ubuntu 虚拟机上测试了服务器可扩展性。

Windows 响应时间几乎是平稳的。当并发级别接近/超过核心数量时,它开始回升。

由于某种原因,Linux 的响应时间要差得多。从并发级别 5 开始,它们几乎呈线性增长。 8 核和 12 核 Linux VM 的行为也类似。

所以我的问题是:为什么它在 linux 上表现更差? (以及如何解决这个问题?)。

请查看随附的图表,它显示了完成 75% 请求的平均时间与请求并发的函数关系(范围栏设置为 50% 和 100%)。

我感觉这可能是由于 mono 的垃圾收集器。我尝试使用 GC 设置,但没有成功。 有什么建议吗?

一些额外的背景信息:服务器基于 HTTP 侦听器,该侦听器可以快速解析请求并将它们排队到线程池中。线程池负责通过一些密集的数学运算来回复这些请求(在大约 10 秒内计算出答案)。

【问题讨论】:

  • 什么是“C# 服务器”?和服务器有区别吗?我的意思是,在您的标题中,“C#”是否被用作形容词? “C# 服务器”是一种服务器吗?
  • >一些密集的数学运算(在大约 10 秒内计算出答案)。我认为您的问题就在那里。这不是服务器的典型场景。你的物理硬件有多少个内核,12 个?图表在 10 个并发请求后的样子?
  • 据我所知,单声道不适合这些用途。 Mono 的开发主要集中在小型桌面应用程序上。我从 mono 的开发人员那里得到了这些信息,我会尝试找到更多信息,如果我找到了,我会在这里发布。
  • 除了 sgen 垃圾收集器,您还应该尝试运行 Mono 的最新(即主干,从存储库编译)版本,最后有很多改进。

标签: c# performance mono scalability server-side


【解决方案1】:

您需要首先找出问题所在。首先使用HeapShot 监控您的内存使用情况。如果不是内存,请分析您的代码以查明耗时的方法。

此页面Performance Tips: Writing better performing .NET and Mono applications 包含一些有用的信息,包括使用单声道分析器。

过多的字符串操作和装箱通常是代码无法很好扩展的“隐藏”罪魁祸首。

【讨论】:

  • 谢谢 Mitch。我要去看看你建议的工具/文档。但我可以告诉你,服务器已经(密集地)在 Windows 上进行了分析和优化(使用 eqatec 分析器)。 Windows 上的服务器行为很棒:平坦的响应(在 12 个物理内核的机器上测试了多达 12 个并发请求)。相反,它在linux上的表现要差得多。内存带宽瓶颈应该以同样的方式影响linux和windows机器,这就是我排除它的原因。请看一下我上传的图表。由于 stackoverflow 反垃圾邮件政策,我之前无法附加它(我是新用户)
  • 我使用了您建议的工具 (HeapShot) 来分析 GC 行为。看起来我向服务器发送的每 20 个请求大约有 10 个垃圾收集。这些 GC 事件中的每一个都处理不到 10Mb 的 RAM。大部分工作都是通过释放 System.Single[] 完成的。无论如何,在我看来,这些内存事件太少了,不会影响服务器的可伸缩性。你怎么看?
【解决方案2】:

尝试使用 sgen 垃圾收集器(为此,推荐使用 Mono 2.11.x)。查看 mono 手册页了解更多详细信息。

【讨论】:

  • 谢谢。我尝试使用 mono --gc=sgen 运行。不幸的是,行为仍然相同
  • 您可以尝试的另一个选项是 LLVM 后端
【解决方案3】:

我不相信这是因为 GC。 AFAIK GC 副作用应该或多或少均匀地分布在线程中。

我的盲目猜测是:您可以通过使用 ThreadPool.SetMinThreads/SetMaxThreads API 来修复它。

【讨论】:

  • 我尝试改变最小/最大工作/io线程的数量,它似乎对可伸缩性没有(积极的)影响。不过还是谢谢。顺便说一句,效果均匀分布在所有线程中:请查看并发 9-10 时棕色和橙色曲线(单声道上的那些)上的误差线。您可以看到所有请求的回复时间几乎相同。
  • 好吧,我的盲目猜测 #2:I/O 完成端口(由 .NET CLR 线程池使用)是在 1993 年(Windows NT 3.5)发明的,从那时起就进行了微调和调试。 OTOH,epoll(IOCP 克隆,MONO 用于相同目的)自 2002 年以来才出现,因此 Windows IOCP 更好。在 Linux 上,一切都很好,直到并发请求 * 2
  • 顺便说一句,基准测试时,您是否同时启动所有请求?如果您以 1-2 秒的间隔一个接一个地启动它们会发生什么变化?
【解决方案4】:

我强烈建议您对代码进行一些关于各个方法运行时长的配置文件。您很可能会看到一些锁定或类似的多线程困难,这些困难并没有被单声道完美处理。 CPU 和 RAM 的使用也会有所帮助。

【讨论】:

    【解决方案5】:

    我相信这可能与我们追踪的涉及线程池和新线程的启动行为的问题相同,以及 setMinThreads 的单声道实现中的错误。请参阅我在该线程上的回答以获取更多信息:https://stackoverflow.com/a/12371795/1663096

    【讨论】:

      【解决方案6】:

      如果你的代码抛出很多异常,那么 mono 比 .NET 快 10 倍

      【讨论】:

      • 太棒了!但我的问题恰恰相反:windows 上的服务器可扩展性比 mono/linux 上的要好得多。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-03-06
      • 1970-01-01
      • 2016-09-16
      • 1970-01-01
      • 1970-01-01
      • 2019-10-04
      • 1970-01-01
      相关资源
      最近更新 更多