在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;
答案 0 :(得分:3)
您正在ServerCommandGet()
的上下文中运行实际查询,因此在查询完成之前,客户端将不会收到回复。对于您正在尝试的内容,您需要将查询移动到其自己的线程并让ServerCommandGet()
退出,以便客户端获得回复并继续,从而释放它以发送后续GETDATA
个请求。在ServerSessionEnd()
中,如果查询线程仍在运行,则必须终止查询线程,并释放TSessionData
对象。
您的代码还存在其他一些问题。
ServerCommandGet()
正在检查not Assigned(ARequestInfo.Session.Content)
,然后在ARequestInfo.Session.Content.AddObject()
为零时调用ARequestInfo.Session.Content
。我没有看到任何创建ARequestInfo.Session.Content
对象的代码。
如果客户发出多个EXECUTEQUERY
请求,您将使用相同的名称将这些请求全部存储在AResponseInfo.Session.Content
中,并使用&#39; U_SESSION&#39;。 GETDATA
将仅返回它找到的第一个查询的结果,ServerSessionEnd()
仅释放它找到的第一个查询。因此,要么为每个查询指定一个唯一的名称,然后将其发送回客户端,以便将其包含在GETDATA
中,并使ServerSessionEnd()
遍历整个Sender.Content
,否则不允许多个查询在同一个HTTP会话中。如果客户端在前一个查询仍处于活动状态时发出新的EXECUTEQUERY
,请在启动新查询之前终止上一个查询。
当客户端发出GETDATA
时,代码需要考虑所请求的查询可能不存在,例如它先前已过期且被ServerSessionEnd()
释放。此外,如果查询确实存在且已完成,则在调用AResponseInfo.ContentStream.CopyFrom()
时,您正在调用AResponseInfo.ContentStream
,但ServerCommandGet()
为nil。您有责任提供自己的ContentStream
对象。因此,要么取得TSessionData
内存流的所有权并将其指定为AResponseInfo.ContentStream
对象,要么创建一个新的TMemoryStream
进行复制,然后将其指定为{{ 1}}对象。无论哪种方式,AResponseInfo.ContentStream
都会在将TIdHTTPServer
发送给客户后将其释放。