Delphi 如何在IdhttpServer上实现长时间运行的查询?

Delphi 如何在IdhttpServer上实现长时间运行的查询?,delphi,delphi-2010,indy10,Delphi,Delphi 2010,Indy10,在IdHttpServer上实现长时间运行查询的好方法是什么。我已经写了简单的逻辑来做到这一点,请建议更好的方法来达到同样的效果,因为我正在努力与它的性能。 我使用D2010和Indy 10.5.8来实现目标,还建议如果我们经常从会话中检索值,这会是资源密集型的吗 procedure TForm1.ServerCommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPRes

在IdHttpServer上实现长时间运行查询的好方法是什么。我已经写了简单的逻辑来做到这一点,请建议更好的方法来达到同样的效果,因为我正在努力与它的性能。 我使用D2010和Indy 10.5.8来实现目标,还建议如果我们经常从会话中检索值,这会是资源密集型的吗

procedure TForm1.ServerCommandGet(AContext: TIdContext;
  ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
   SessionObj : TSessionData;
begin
 if ARequestInfo.Document = 'EXECUTEQUERY' then
 begin
   if not Assigned(ARequestInfo.Session.Content) then
   begin
      SessionObj := TSessionData.Create;
      ARequestInfo.Session.Content.AddObject('U_SESSION', SessionObj);
      SessionObj.RunLongQuery;
   end;
 end;

 if ARequestInfo.Document = 'GETDATA' then
 begin
   SessionObj := TSessionData(ARequestInfo.Session.Content.Objects[ ARequestInfo.Session.Content.IndexOf('U_SESSION')]);
   if SessionObj.GetQueryStat = Done  then
    begin
       AResponseInfo.ContentStream.CopyFrom(SessionObj.GetMemStream, SessionObj.GetMemStream.Size);
       SessionObj.GetMemStream.Clear;
       AResponseInfo.ResponseNo := 200;
    end else if SessionObj.GetQueryStat = Error then
        AResponseInfo.ResponseNo := 500
    else AResponseInfo.ResponseNo := 102;
 end;

end;

procedure TForm1.ServerSessionEnd(Sender: TIdHTTPSession);
begin
  TSessionData(Sender.Content.Objects[ Sender.Content.IndexOf('U_SESSION')]).Free;
end;

{ TProcessQuery }

constructor TProcessQuery.Create;
begin
  myConn := TMyConnection.Create(nil);
  myConn.LoginPrompt := False;
  myConn.UserName := 'UserName';
  myConn.Password := 'Password';
  myConn.Server := 'Host';
  myConn.Database := 'DBName';
  myConn.Connected := True;
  myQuery := TMyQuery.Create(nil);
  myQuery.Unidirectional := True;
  myQuery.Options.CreateConnection := False;
  myQuery.Connection := myConn;
  Fstat := None;
  Fstream := TMemoryStream.Create;
end;

destructor TProcessQuery.Destroy;
begin
  if Assigned(myConn) then begin
    myConn.Close;
    myConn.Disconnect;
    FreeAndNil(myConn);
  end;
end;

procedure TProcessQuery.ExecuteQuery;
begin
  Status := Started;
  myQuery.SQL.Text := '<Some Query>';
  myQuery.Open;
  try
    try
    while not myQuery.Eof do
    begin
         Status := Inprogress;
         //Add to FStream which would be returned to user.
    end;
    except
        on Exception do
        Status := Error;
    end;
  finally
    myQuery.Close;
  end;
end;

{ TSessionData }

constructor TSessionData.Create;
begin
  FProcessQuery :=  TProcessQuery.Create;
end;

function TSessionData.GetMemStream: TMemoryStream;
begin
  result := FProcessQuery.Fstream;
end;

function TSessionData.GetQueryStat: TStatus;
begin
  result := FProcessQuery.Status;
end;

procedure TSessionData.RunLongQuery;
begin
  FProcessQuery.ExecuteQuery
end;
过程TForm1.ServerCommandGet(AContext:TIdContext;
ARequestInfo:TIdHTTPRequestInfo;AResponseInfo:TIdHTTPResponseInfo);
变量
会话BJ:会话数据;
开始
如果ARequestInfo.Document='EXECUTEQUERY',则
开始
如果未分配(ARequestInfo.Session.Content),则
开始
SessionObj:=TSessionData.Create;
ARequestInfo.Session.Content.AddObject('U_Session',SessionObj);
SessionObj.RunLongQuery;
结束;
结束;
如果ARequestInfo.Document='GETDATA',则
开始
SessionObj:=TSessionData(ARequestInfo.Session.Content.Objects[ARequestInfo.Session.Content.IndexOf('U_Session'));
如果SessionObj.GetQueryStat=Done,则
开始
AResponseInfo.ContentStream.CopyFrom(SessionObj.GetMemStream,SessionObj.GetMemStream.Size);
SessionObj.GetMemStream.Clear;
AResponseInfo.ResponseNo:=200;
如果SessionObj.GetQueryStat=错误,则结束else
AResponseInfo.ResponseNo:=500
else AResponseInfo.ResponseNo:=102;
结束;
结束;
过程TForm1.ServerSessionEnd(发送方:TIdHTTPSession);
开始
TSessionData(Sender.Content.Objects[Sender.Content.IndexOf('U_SESSION')).Free;
结束;
{TProcessQuery}
构造函数TProcessQuery.Create;
开始
myConn:=TMyConnection.Create(nil);
myConn.LoginPrompt:=False;
myConn.UserName:=“用户名”;
myConn.Password:=“密码”;
myConn.Server:=“主机”;
myConn.Database:=“DBName”;
myConn.Connected:=真;
myQuery:=TMyQuery.Create(nil);
myQuery.单向:=True;
myQuery.Options.CreateConnection:=False;
myQuery.Connection:=myConn;
Fstat:=无;
Fstream:=TMemoryStream.Create;
结束;
析构函数TProcessQuery.Destroy;
开始
如果已分配(myConn),则开始
麦康.关闭;
断开连接;
FreeAndNil(myConn);
结束;
结束;
过程TProcessQuery.ExecuteQuery;
开始
状态:=已启动;
myQuery.SQL.Text:='';
myQuery.Open;
尝试
尝试
而不是myQuery.Eof do
开始
状态:=正在进行中;
//添加到将返回给用户的FStream。
结束;
除了
例外情况
状态:=错误;
结束;
最后
myQuery.Close;
结束;
结束;
{TSessionData}
构造函数TSessionData.Create;
开始
FProcessQuery:=TProcessQuery.Create;
结束;
函数TSessionData.GetMemStream:TMemoryStream;
开始
结果:=FProcessQuery.Fstream;
结束;
函数TSessionData.GetQueryStat:TStatus;
开始
结果:=FProcessQuery.Status;
结束;
程序TSessionData.RunLongQuery;
开始
FProcessQuery.ExecuteQuery
结束;

您正在
ServerCommandGet()的上下文中运行实际查询,因此在查询完成之前,客户端不会收到回复。对于您正在尝试的内容,您需要将查询移动到它自己的线程,并让
ServerCommandGet()
退出,以便客户端获得答复并可以继续,从而释放它来发送后续的
GETDATA
请求。在
ServerSessionEnd()
中,如果查询线程仍在运行,则必须终止该线程,并释放
TSessionData
对象

您的代码还存在一些其他问题

ServerCommandGet()
正在检查
是否未分配(ARequestInfo.Session.Content)
然后在
ARequestInfo.Session.Content
为零时调用
ARequestInfo.Session.Content.AddObject()。我没有看到任何创建
ARequestInfo.Session.Content
对象的代码

如果客户端发出多个
EXECUTEQUERY
请求,您将使用相同的名称“U\U Session”将它们全部存储在
AResponseInfo.Session.Content
GETDATA
将只返回它找到的第一个查询的结果,
ServerSessionEnd()
只释放它找到的第一个查询。因此,为每个查询指定一个唯一的名称并将其发送回客户端,以便它可以将其包含在
GETDATA
中,并使
ServerSessionEnd()
在整个
Sender.Content
中循环,或者不允许在同一HTTP会话中进行多个查询。如果客户机在上一个查询仍处于活动状态时发出新的
EXECUTEQUERY
,请在启动新查询之前终止上一个查询


当客户机发出
GETDATA
时,代码需要考虑请求的查询可能不存在,例如它以前是否已过期并由
ServerSessionEnd()释放。另外,如果查询确实存在并且已完成,则调用的是
AResponseInfo.ContentStream.CopyFrom()
,但调用
ServerCommandGet()
AResponseInfo.ContentStream
为零。您负责提供自己的
ContentStream
对象。因此,要么拥有
TSessionData
的内存流的所有权并将其分配为
AresOnSeInfo.ContentStream
对象,要么创建一个新的
TMemoryStream
复制到其中,然后将其分配为
AresOnSeInfo.ContentStream
对象。无论哪种方式,
TIdHTTPServer
都会在将
AresOnSeInfo.ContentStream发送到客户端后释放它。

您正在
ServerCommandGet()
的上下文中运行实际的查询,因此客户端在查询完成之前不会收到回复。对于您正在尝试的内容,您需要将查询移动到它自己的线程,并让
ServerCommandGet()
退出,以便客户端获得答复并可以继续,从而释放它来发送后续的
GETDATA
请求。在
ServerSessionEnd()
中,必须终止查询线程