4

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
4

1 回答 1

1

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

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

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

这些是我要研究的领域:

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

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

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

于 2012-12-14T23:52:16.377 回答