8

谷歌搜索此 ADO 错误消息表明它在 ASP.NET 开发中经常遇到,但我没有发现太多提及它何时发生在 Delphi 应用程序中。我们有一些客户站点正在经历暂时的网络问题,这是有症状的错误消息。我们可以很容易地在办公室测试中复制它;只需在您的 delphi TADOConnection 对象连接到该服务器实例上的数据库时关闭 MS SQL Server 服务,您就会得到以下异常:

   [DBNETLIB][ConnectionWrite (send()).]General network error. Check your network documentation.

是的,抓住这个异常,你就知道(或者你知道吗?)这个错误已经发生了。除了这是一个 800 KLOC+ 应用程序,在数据库操作周围有超过 10,000 个 try-except 块,其中任何一个都可能因此错误而失败。

TADOConnection有一些错误事件,在这种情况下都不会触发。但是,一旦发生这种情况,ADO 连接本身就会出现故障,即使您重新启动 SQL 数据库,TADOConnection.Connected 仍然是正确的,但它是在骗您。它确实处于故障状态。

那么,我的问题是:

你能以任何比进入 10,000 个单独的 try-except 块并设置一些全局“重新连接 ADO 全局变量”更简单的方式检测到这种错误状态并从中恢复吗?

我希望有一种方法可以进入 TADOConnection.ConnectionObject (底层原始 OLEDB COM ADO 对象)并在我们开始新查询时检测到此故障情况,以便我们可以重置 ADOConnection 并在下次运行时继续一个问题。由于我们的代码的组织方式允许我们在“失败后”检测到这一点,而不是像我在 10 行演示应用程序中那样做。

This other SO question询问为什么会发生,这不是我要问的,请不要给我“预防”答案,我已经知道了,我正在寻找恢复和检测停滞的ADO - 连接技术,而不是捕获异常。事实上,这是异常出错的一个很好的例子;在这种故障模式下,ADO 是一个 schrodingers-cat 对象。

我知道 MS 知识库文章,以及互联网上流传的各种解决方案。一旦错误情况(在我们的情况下通常是短暂的)已经清除,我正在询问是否在不丢失客户数据的情况下进行恢复。这意味着我们冻结我们的应用程序,向客户显示异常,当客户单击重试或继续时,我们会尝试修复并继续。请注意,我们现有的代码执行了一百万个 try-except-log-and-continue 代码,这会妨碍我们,所以我期待有人回答未处理异常的应用程序处理程序是最好的方法,但遗憾的是我们不能使用它。但是,我真的希望可以检测到冻结/故障/死 ADO 连接对象。

这是我所拥有的:

try
  if fQueryEnable and ADOConnection1.Connected then begin
    qQueryTest1.Active := false;
    qQueryTest1.Active := true;
    Inc(FQryCounter);
    Label2.Caption := IntToStr(qQueryTest1.RecordCount)+' records';

  end;
except
      on E:Exception do begin
         fQueryEnable := false;
         Memo1.Lines.Add(E.ClassName+' '+E.Message);
         if E is EOleException and Pos('DBNETLIB',E.Message)>0 then begin
            ADOConnectionFaulted := boolean; { Global variable. }
         end;
         raise;
      end;
end;

上述解决方案的问题是我需要在我的应用程序中复制并粘贴大约 10,000 个位置。

4

3 回答 3

8

好吧,没有人回答这个问题,我认为一些后续行动会有所帮助。

这是我学到的:

  • 没有可靠的情况可以在测试环境中重现此一般网络错误。也就是说,我们正在处理 Irreproducible Results,这是许多开发人员跳入邪恶黑客的地方,试图“修补”他们损坏的系统。

  • 当 SQL 库给出“一般网络错误”时,修复底层故障总是比在代码中修复更好。没有任何修复被证明是可能的,因为通常这意味着“网络非常不可靠,以至于 TCP 本身已经放弃传递我的数据”,这种情况发生在以下情况:

    • 你的网线坏了。

    • 您在网络上有重复的 IP 地址。

    • 您有决斗的 DHCP 服务器,每个服务器处理不同的默认网关。

    • 您有本地以太网段,它们之间的连接性很差。

    • 您的以太网交换机或集线器出现故障。

    • 您被出现故障的防火墙间歇性地阻止。

    • 您的客户可能已经更改了他们网络上的某些内容,现在可能无法使用您的软件。(这最后一个实际上发生的比你想象的要多)

    • 有人可能已经使用cliconfg特定于单个工作站的注册表设置的其他客户端配置元素配置了 SQL 别名,并且这种本地配置可能会导致难以诊断的不良行为,并且可能仅限于大型工作站上的一个或多个工作站网络。

在 TCP 或 SQL 级别都无法检测和报告上述任何情况。当 SQL 最终放弃时,它给出了这个“一般网络错误”,我的软件再多的哄骗都不会让它放弃,即使它放弃了,我也会做一个“尝试/除/忽略”反模式。这个错误非常严重,我们应该将它一直提交给用户,在错误日志中将其记录到磁盘,放弃(退出程序),并告诉用户网络连接已关闭。

于 2012-11-30T14:57:15.727 回答
2

由于编码错误,我也看到了这种情况。

如果您使用连接打开记录集,并且在第一个连接未关闭的情况下在循环中将同一连接重复用于另一个记录集,则可能会导致类似的错误。

另一个在 Web 应用程序中很少见的情况是,当应用程序池正在回收时,您可能会收到类似的错误。

我们在同一台服务器上有不同的站点,我注意到使用相同的应用程序但具有不同的定制,只有一个站点导致了这个问题。这导致了上述发现。

这个博客帮助我找到了问题:

http://offbeatmammal.hubpages.com/hub/Optimising_SQL_Server

于 2013-09-03T15:32:43.977 回答
1

此处的代码检测到断开事件触发并使用计时器重新连接。假设您在阅读此代码时意识到必须将 TTimer 拖放到此处显示的此数据模块上,并使用下面显示的代码创建 OnTimer 事件。

请检查下一个代码:

unit uDM;

interface

uses
  SysUtils, Classes, DB, ADODB, Vcl.ExtCtrls;

type
  TDM = class(TDataModule)
    ADOConnection: TADOConnection;
    ConnectionTimmer: TTimer;
    procedure ADOConnectionDisconnect(Connection: TADOConnection;
      var EventStatus: TEventStatus);
    procedure ConnectionTimmerTimer(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  DM: TDM;

implementation

{$R *.dfm}

procedure TDM.ADOConnectionDisconnect(Connection: TADOConnection;
  var EventStatus: TEventStatus);
begin
  if eventStatus in [esErrorsOccured, esUnwantedEvent] then
    ConnectionTimmer.Enabled := True;
end;

procedure TDM.ConnectionTimmerTimer(Sender: TObject);
begin
  ConnectionTimmer.Enabled := False;
  try
    ADOConnection.Connected := False;
    ADOConnection.Connected := True;
  except
    ConnectionTimmer.Enabled := True;
  end;
end;

end.
于 2020-01-26T17:13:16.920 回答