使用PHP Curl库实现持久化/保活HTTP连接?

65

我使用一个简单的PHP库通过HTTP将文档添加到SOLR索引中。

目前涉及到3个服务器:

  1. 运行索引任务的PHP服务器
  2. 保存被索引数据的数据库服务器
  3. Solr服务器

在处理100万个文档时,每秒索引80个文档,我发现PHP和Solr服务器上的网络接口中断率异常高(每秒达2000次),而且图表几乎完全一致。但是,在数据库服务器上情况不太明显(每秒仅为300次)。我想这很可能是因为我打开并重用了与数据库服务器的单个连接,但由于Solr客户端库的编写方式,目前每个Solr请求都会通过cURL打开一个新的HTTP连接。

所以,我的问题是:

  1. 能否让cURL打开一个keepalive会话?
  2. 如何重用连接?是否简单地重用cURL句柄资源即可?
  3. 是否需要设置任何特殊的cURL选项?(例如,强制使用HTTP 1.1协议?)
  4. cURL keepalive连接有什么需要注意的地方吗?脚本运行时间长达数小时,我能使用单个连接还是需要定期重新连接?

3
我曾经在解析需要身份验证并保持会话的许多页面的整个网站时使用过它。使用初始句柄资源,您可以继续执行命令以获取页面并保持与客户端的同一会话和连接。使用命令行,这已经持续了大约20分钟(对于我们所有的数据需求-因此可能需要更长时间),而无需重新连接。但我不确定这是否是您所询问的内容,因此这只是一个评论,而不是答案 :) - Shadi Almosri
另外需要注意的是,通常根据你所做的事情和连接的服务器,可能需要设置一些选项。所有这些都在这里得到了很好的记录:http://uk3.php.net/manual/en/function.curl-setopt.php。 - Shadi Almosri
3
我会尽力进行翻译:这部分常见问题解答虽然不是非常详细,但仍然相关:http://curl.haxx.se/docs/faq.html#Can_I_perform_multiple_requests - Frank Farmer
2
我遇到的一个问题是:通过单个curl句柄进行大约10万次请求后,我的脚本达到了512兆的内存使用量;在开始重用连接之前,它从未超过60 MB。现在,我每1000个请求重新连接一次(这可能比必要的频率更高,但连接开销应该非常小)。 - Frank Farmer
3
还有一个选项:CURLOPT_MAXCONNECTS - 允许的最大持久连接数。当达到限制时,将使用 CURLOPT_CLOSEPOLICY 来确定关闭哪个连接。 - David d C e Freitas
4个回答

61

cURL PHP文档 (curl_setopt) 解释如下:

CURLOPT_FORBID_REUSE - TRUE时可以强制关闭连接,以确保在处理完成后不会被池化并重用。

因此:

  1. 是的,实际上默认情况下它应该重复使用连接,只要您重复使用cURL句柄即可。
  2. 默认情况下,cURL会自行处理持久连接;如果您需要一些特殊的头部信息,请检查CURLOPT_HTTPHEADER。
  3. 服务器可能会发送一个keep-alive超时(默认情况下Apache为15秒或100个请求,以先到者为准)-但当发生这种情况时,cURL将打开另一个连接。

2
太棒了!我差点就要在StackOverflow上发布我的第一个问题了。只要我们添加请求头“Connection: close”,这个解决方案就适用于我们的中间件。 - renevanderark

24

Curl默认发送保持活动状态的标头,但是:

  1. 使用没有任何参数的curl_init()创建上下文。
  2. 将上下文存储在其将生存的作用域中(不是局部变量)
  3. 使用CURLOPT_URL选项将URL传递给上下文
  4. 使用curl_exec()执行请求
  5. 不要使用curl_close()关闭连接

非常基本的示例:

function get($url) {
    global $context;
    curl_setopt($context, CURLOPT_URL, $url);
    return curl_exec($context);
}

$context = curl_init();
//multiple calls to get() here
curl_close($context);

在第二个调用之前,您还需要设置Cookie,类似于:curl_setopt($context, CURLOPT_COOKIE, 'name=value'); 例如,对于我的请求,可以这样设置:curl_setopt($context, CURLOPT_COOKIE, 'PHPSESSID=bl392rgi8q664l7faat33hfta4'); - Malus Jan

16
  1. 在访问的服务器上必须启用keep-alive,并且最大keep-alive请求应该合理。如果是Apache服务器,请参考apache文档

  2. 你需要重复使用相同的cURL上下文。

  3. 在配置cURL上下文时,请在头部启用带有超时的keep-alive:

  4. curl_setopt($curlHandle, CURLOPT_HTTPHEADER, array(
        'Connection: Keep-Alive',
        'Keep-Alive: 300'
    ));
    

2
Frank,我刚刚重新测试了我的代码,它似乎默认是开启的。不过明确设置一下也无妨。 - Oleg Barshay
1
@OlegBarshay,你知道我们是否需要删除curl_close($curlHandle);以保持连接吗? - zeflex
1
@zeflex 是的,如果你调用 curl_close,连接将会被关闭,你必须将其移除。 - Grain
curl_exec会在服务器断开连接时创建一个新的连接并重新设置句柄吗,即使保持活动状态仍然有效? - AaA

1
如果你不关心请求的响应,你可以将它们异步执行,但是这样做可能会导致 SOLR 索引超载。不过我觉得问题不大,SOLR 的速度相当快。 PHP 异步调用?

这确实很有趣,但它根本没有解决连接重用的问题。事实上,它只会让我的连接开销问题更加严重。 - Frank Farmer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接