【问题标题】:How can I tell if my Ruby server script is being overloaded?如何判断我的 Ruby 服务器脚本是否过载?
【发布时间】:2012-06-05 04:17:17
【问题描述】:

我的服务器上运行着一个守护进程的 ruby​​ 脚本,如下所示:

@server = TCPServer.open(61101)                         
loop do                                                 
  @thr = Thread.new(@server.accept) do |sock|
    Thread.current[:myArrayOfHashes] = []   # hashes containing attributes of myObject
    SystemTimer.timeout_after(5) do
      Thread.current[:string] = sock.gets
      sock.close

      # parse the string and load the data into myArrayOfHashes

      Myobject.transaction do           # Update the myObjects Table
        Thread.current[:myArrayOfHashes].each do |h|
          Thread.current[:newMyObject] = Myobject.new
          # load up the new object with data
          Thread.current[:newMyObject].save
        end
      end

    end
  end
  @thr.join
end

此服务器接收和管理我的 rails 应用程序的数据,该应用程序都在 Mac OS 10.6 上运行。客户端在 15 日每 15 分钟调用一次服务器,而我目前只有 16 个左右的客户端在 15 日每 15 分钟调用一次,我想知道以下几点:

  1. 如果两个客户端在足够近的时间同时调用,一个客户端的连接尝试会失败吗?
  2. 如何确定我的服务器可以同时容纳多少个客户端连接?
  3. 如何监控我的服务器使用了多少内存?

另外,有没有一篇你可以指点我的文章来讨论实现这种服务器的最佳方法?我的意思是我可以让多个服务器实例监听同一个端口吗?那会有帮助吗?

我正在使用 Bluepill 来监控我的服务器守护进程。

【问题讨论】:

  • 好奇:如果你之后只使用join,为什么还要使用Thread.new?这相当于sock = @server.accept,然后只是删除所有对线程的引用。
  • 我认为真正的答案是我可能不知道自己在做什么。我所知道的是@thr.join 使一切都开始可靠地工作。我会用中间的代码更新我的帖子,让你也许能够说出我的理解差距在哪里。
  • 如果您使用Thread.new 而不是join,那么是的,如果您的代码不是线程安全的,那可能会很奇怪。不过,您更安全,但只需删除所有 Thread 引用;像这样:http://pastebin.com/8m6CnbU4.
  • 这种方法绝对不适合我。从我在线程上所做的搜索以及不同的系统如何以不同的方式处理它们,我不确定为什么会这样,但是我在 Mac OS 上运行并且我很清楚(从我的数据库中看到的结果)当我刚刚有一个简单的sock=@server.accept 时,变量在我的线程中变得混乱。所以真的我只是不确定@thr.join 到底在做什么。问题是在我有 @thr.join 之前,很多线程根本不会完成。如果没有@thr.join,我将如何解决这个问题?
  • 好吧……好吧,我明白了。不过,使用线程并没有真正的优势,因为无论何时使用@thr.join 都会同步它们。我建议您检查一下您正在尝试做的事情。

标签: ruby


【解决方案1】:

1 和 2
答案是否定的,两个相互靠近的客户端连接不会导致连接失败(但是多个客户端连接可能会失败,见下文)。

原因是操作系统在所有服务器套接字中内置了一个默认的所谓的侦听队列。因此,即使您在程序中调用accept 的速度不够快,操作系统仍会继续为您缓冲传入的连接。只要监听队列没有被填满,它就会缓冲这些连接。

那么这个队列的大小是多少?

在大多数情况下,通常使用的默认大小是 5。大小是在您创建套接字并在此套接字上调用 listen 后设置的(请参阅 man page for listen here)。

对于 Ruby,TCPSocket 会自动为你调用 listen,如果你查看 TCPSocket 的 C 源代码,你会发现它确实将大小设置为 5:

https://github.com/ruby/ruby/blob/trunk/ext/socket/ipsocket.c#L108

SOMAXCONN在这里定义为5:

https://github.com/ruby/ruby/blob/trunk/ext/socket/mkconstants.rb#L693

现在,如果您没有足够快地调用 accept 并且队列被填满,会发生什么? 答案在listen的man page中找到:

backlog 参数定义了 sockfd 的挂起连接队列可以增长到的最大长度。如果连接请求在队列已满时到达,客户端可能会收到带有 ECONNREFUSED 指示的错误,或者,如果底层协议支持重传,则可能会忽略该请求,以便稍后重新尝试连接成功。

但是,在您的代码中,如果超过 5 个客户端尝试同时连接,则可能会导致队列填满:您在循环结束时调用 @thr.join

当你这样做时,实际上发生的事情是你的服务器不会接受任何新的传入连接,直到你接受线程中的所有东西都完成执行。

因此,如果数据库内容和您在接受线程内执行的其他操作需要很长时间,则侦听队列可能会同时填满。这取决于您的处理需要多长时间,以及可能同时连接的客户端数量。

3
你没有说你在哪个平台上运行,但在 linux/osx 上,最简单的方法是在你的控制台中运行top。对于更高级的内存监控选项,您可能需要查看这些选项:

ruby/ruby on rails memory leak detection
track application memory usage on heroku

【讨论】:

  • 嗨卡斯帕。所以当你问一个问题然后意识到你不再需要你的问题的答案时,我有点恐慌,但不知道你在做什么。好的 #1 是我现在需要能够同时容纳大约 80 个连接。作为一个临时修复,所以我的服务器在我寻找更好的解决方案时不会拒绝任何连接尝试,我可以将 SOMAXCONN 变量设置为 100 吗?
  • @pitachip 不,改变 SOMAXCONN 不是这里的解决方案。而且我怀疑操作系统会允许您将其设置得如此之高。看起来您需要花点时间阅读一下 Threads。当您不了解自己在做什么时,您就无法创建适当的程序。一种解决方案是让您拥有一个线程,它所做的就是接受新的连接。然后这个线程触发“子线程”来处理传入的客户端请求。添加 @thr.join 之前的内容。但是你需要先正确理解线程安全和共享变量。是时候学习了!
  • 我可能夸大了我的缺乏理解,因为我倾向于这样做:)。我已经阅读了很多关于线程和线程安全的文章,以及关于为什么线程在这个系统上不好的文章。我还没有找到一个解释,它推荐了一种实现“正确”的简单多客户端 TCP 服务器的方法。我上面的内容是许多人推荐的,目前正在工作,但显然非常不正确。我是一个伟大的研究,并希望阅读有关该主题的权威材料。你能给我指出一个明确的来源吗?因为在这种情况下,谷歌似乎没有帮助。
猜你喜欢
  • 1970-01-01
  • 2021-06-23
  • 2010-10-19
  • 1970-01-01
  • 2012-02-26
  • 2015-04-15
  • 1970-01-01
  • 2018-08-22
  • 2013-03-28
相关资源
最近更新 更多