【问题标题】:Multiple requests on same IdHTTP inside thread线程内同一 IdHTTP 上的多个请求
【发布时间】:2019-04-11 01:53:18
【问题描述】:

我有一个应用程序每天发出数千个 HTTP 请求。 为了获得最佳性能,我决定只创建一次 IdHTTP 对象,并对所有请求使用相同的对象。 这就是问题的开始。在为每个请求创建一个 IdHTTP 时,一切都很顺利。 代码非常基本:

constructor HTTPThread.Create;
begin
  inherited Create(false);
  httpObject:= TIdHTTP.Create(Nil);
  sslObject:= TIdSSLIOHandlerSocketOpenSSL.Create(Nil);
  sslObject.SSLOptions.Method:= sslvTLSv1_2;
  httpObject.IOHandler:= sslObject;
  httpObject.Request.Accept:= frmHTTPRequests.Edit1.Text;
  httpObject.Request.UserAgent:= frmHTTPRequests.Edit3.Text;
  httpObject.ReadTimeout:= 15000;
  httpObject.HandleRedirects:= true;
  FreeOnTerminate:= true;
  OnTerminate:= TerminateProc;
end;


procedure HTTPThread.DoRequests;
var
    htmlSource: string;
begin
    try
      htmlSource:= httpObject.Get(Link);
      //a bunch of other stuff with HTML source
    except
      on E : Exception do
      Synchronize(procedure
      begin
        errorList.Add(E.Message);
        errorList.SaveToFile('Error.txt');
      end);
    end;
end;

我创建了这个,除了保存 Error.txt 文件来观察正在发生的事情...... 代码有时对于前 1k 请求运行良好,有时对于前 2k,它会有所不同。突然,它开始在 TXT 文件上写入相同的错误:

连接被对等方重置。 domain.com - 套接字错误 #10054

我尝试断开 httpObject,尝试了 httpObject.Request.Clear,但似乎没有任何效果。 有没有机会完成这项工作?

【问题讨论】:

  • 我在使用 TIdHttp 时遇到过类似的问题,我的解决方案是重新创建组件以防出错并重试。只有当第二次尝试也失败时,它才会被认为是一个实际问题。
  • 与执行实际请求相比,您是否实际测量过创建对象所需的时间和开销?我认为收益可以忽略不计,除非所有请求都发往同一台服务器,并且您可以利用保持活动连接的使用。
  • @GolezTrol 我没有检查...谢谢您的洞察力。
  • @HeartWare 好的...很高兴知道这是一个常见问题。

标签: multithreading delphi idhttp


【解决方案1】:

由于某些原因,Indy 在服务器响应 Connection reset by peer 时不会关闭套接字,因此您需要手动执行。

procedure HTTPThread.DoRequests;
const
  MAX_TRIES_COUNT = 5;
var
  htmlSource: string;
  TriesCount: Integer;
begin
  TriesCount := 0;
  repeat
    try
      htmlSource:= httpObject.Get(Link);
      //a bunch of other stuff with HTML source
    except
      on E: Exception do
      begin
        if E.Message.Contains('by peer') then
        begin
          httpObject.Disconnect;
          // Try to solve network connection issues
          Continue;
        end
        else
        begin
          // Some other error handlers
        end;
      end;
      inc(TriesCount);
    end;
  until (httpObject.ResponseCode = 200) or (TriesCount > MAX_TRIES_COUNT);
end;

附:我不建议您使用Synchronize() 进行线程同步。尝试改用TCriticalSection 或TMonitor。

【讨论】:

  • "由于某些原因,当服务器以 Connection reset by peer 响应时,Indy 不会关闭套接字" - 实际上,TIdHTTP 确实会在套接字错误时关闭套接字,并且如果服务器不同意使用 keep-alive,则在每个请求结束时(无论是成功还是失败)。
  • "if E.Message.Contains('by peer') then ..." - 不要那样做。更好的方法是这样做:if (E is EIdSocketError) and (EIdSocketError(E).LastError = 10054) then ...,或者:on E: EidSocketError do begin if E.LastError = 10054 then ... end;。不要依赖Message 检查,它们效率低且容易本地化。
  • @RemyLebeau,Indy 的消息是不变的,至少现在是这样。我很久以前就找到了这个解决方案,我不记得为什么我决定检查短信而不是代码,但我确信这是有原因的。我认为,您同意 Indy 错误地处理了此错误(不仅如此)。
  • @OlvinR​​oght "Indy 消息是不变的,至少现在是这样" - Indy 的文本消息存储在资源字符串中,因此需要用户进行本地化。 “我认为,你同意 Indy 错误地处理了这个错误” - 我不同意这种说法,因为它与 TIdHTTP 的源代码所说的不匹配。
  • @RemyLebeau 我不应该在发生错误时手动断开它。这应该已经在 Indy 内部的某个地方完成了。
猜你喜欢
  • 2021-03-25
  • 1970-01-01
  • 2023-03-27
  • 2020-01-12
  • 2019-02-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多