【问题标题】:Node request queue backed up节点请求队列已备份
【发布时间】:2012-12-14 22:39:56
【问题描述】:

TL;DR - 在配置 globalAgent 时是否有任何最佳实践,以允许高吞吐量和大量并发请求?

这是我们的问题:

据我所知,Node 中的连接池由 http 模块管理,该模块在 globalAgent 对象中对请求进行排队,该对象对 Node 进程是全局的。在任何给定时间从 globalAgent 队列中拉出的请求数由打开的套接字连接数决定,该连接数由 globalAgent 的 maxSockets 属性决定(默认为 5)。

当使用“keep-alive”连接时,我希望一旦请求得到解决,处理该请求的连接将可用并且可以处理 globalAgent 队列中的下一个请求。

然而,似乎在处理任何其他排队请求之前,每个连接都已解决,直到最大数量。

在观察组件之间的网络流量时,我们看到如果 maxSockets 为 10,则 10 个请求成功解析。然后有一个暂停 3-5 秒的暂停(大概是在建立新的 tcp 连接时),然后再解决 10 个请求,然后再暂停,等等。

这似乎是错误的。 Node 应该擅长处理大量并发请求。因此,即使有 1000 个可用的套接字连接,如果在 1-999 解决之前无法处理请求 1000,那么您就会遇到瓶颈。但是我无法弄清楚我们做错了什么。

更新

以下是我们如何发出请求的示例——尽管值得注意的是,只要节点进程发出 http 请求,包括当该请求由广泛使用的第三方库发起时,就会发生这种行为。我不认为它特定于我们的实施。不过……

class Client
  constructor: (@endpoint, @options = {}) ->
    @endpoint = @_cleanEndpoint(@endpoint)
    throw new Error("Endpoint required") unless @endpoint && @endpoint.length > 0

    _.defaults @options,
        maxCacheItems: 1000
        maxTokenCache: 60 * 10
        clientId : null
        bearerToken: null # If present will be added to the request header
        headers: {}
    @cache = {}

    @cards  = new CardMethods @
    @lifeStreams = new LifeStreamMethods @
    @actions = new ActionsMethods @

  _cleanEndpoint: (endpoint) =>
    return null unless endpoint
    endpoint.replace /\/+$/, ""


  _handleResult: (res, bodyBeforeJson, callback) =>
      return callback new Error("Forbidden") if res.statusCode is 401 or res.statusCode is 403

      body = null

      if bodyBeforeJson and bodyBeforeJson.length > 0
        try
          body = JSON.parse(bodyBeforeJson)
        catch e
          return callback( new Error("Invalid Body Content"), bodyBeforeJson, res.statusCode)

      return callback(new Error(if body then body.message else "Request failed.")) unless res.statusCode >= 200 && res.statusCode < 300
      callback null, body, res.statusCode

  _reqWithData: (method, path, params, data, headers = {}, actor, callback) =>
    headers['Content-Type'] = 'application/json' if data
    headers['Accept'] = 'application/json'
    headers['authorization'] = "Bearer #{@options.bearerToken}" if @options.bearerToken
    headers['X-ClientId'] = @options.clientId if @options.clientId

    # Use method override (AWS ELB problems) unless told not to do so
    if (not config.get('clients:useRealHTTPMethods')) and method not in ['POST', 'PUT']
      headers['x-http-method-override'] = method
      method = 'POST'

    _.extend headers, @options.headers

    uri = "#{@endpoint}#{path}"
    #console.log "making #{method} request to #{uri} with headers", headers
    request
      uri: uri
      headers: headers
      body: if data then JSON.stringify data else null
      method: method
      timeout: 30*60*1000   
     , (err, res, body) =>
       if err
         err.status =  if res && res.statusCode then res.statusCode else 503
         return callback(err)

       @_handleResult res, body, callback

【问题讨论】:

  • 当然@AndyD - 用代码更新。虽然看到我的笔记。无论 http 请求是如何发出的,我们都会看到这个问题。似乎我们没有遵循 Node 约定或最佳实践,而不是编码错误。
  • 我们使用 nano 连接到 cloudant,我们看到从 micro aws 实例到 cloudant 的请求高达 200 个/秒。所以你是对的,节点应该由它决定。如果您还没有,请尝试使用请求 github.com/mikeal/request。 (我不认为它会有所作为,但仍然值得一试)。

标签: node.js networking httprequest


【解决方案1】:

说实话,coffeescript 不是我的强项,所以无法真正评论代码。

但是,我可以给您一些想法:在我们的工作中,我们使用 nano 连接到 cloudant,并且我们看到从微型 AWS 实例到 cloudant 的请求高达 200 个/秒。所以你是对的,节点应该由它决定。

如果您还没有使用请求https://github.com/mikeal/request,请尝试使用。 (我认为这不会有什么不同,但值得一试,因为这就是 nano 的用途)。

这些是我要研究的领域:

  1. 服务器不能很好地处理多个请求并对其进行限制。您是否对您的服务器进行过任何性能测试?如果由于某种原因它无法处理负载,或者您的请求在操作系统中受到限制,那么您的客户端做什么都没关系。

  2. 您的客户端代码在某处有一个长时间运行的函数,该函数会阻止节点处理您从服务器返回的任何响应。也许 1 个特定响应会导致响应回调花费太长时间。

端点是否都是不同的服务器/主机?

【讨论】:

  • 感谢@AndyD 的回答。是的,端点都是不同的服务器/主机。我们确实调查了您提到的第一个问题,并增加了操作系统允许的打开文件描述符的最大数量。结合增加分配给 globalAgent 的 maxSockets 似乎已经缓解了这个问题——至少现在是这样。最终,我认为我们在不合适的情况下使用 HTTP 进行 IPC。
猜你喜欢
  • 2013-04-04
  • 2021-01-13
  • 2016-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-10
  • 2020-09-22
  • 2017-08-09
相关资源
最近更新 更多