Rest服务器TServerMethods1方法中的数据库连接、超多线程与防止内存溢出的问题
分类:
文章
•
2025-02-21 21:12:28
RestFul服务器TServerMethods1方法中的
数据库连接、超多线程与防止内存溢出的问题




一、TServerMethods1本身就是线程在执行客户端请求的方法
function TServerMethods1.testAnonymousThreadX
( const pSql,pSelectKey:string ):TFDJsonDatasets;
var iSql,iSelectKey:string;
var FDQry1:TFDQuery; FDCon1:TFDConnection;
var FDJsonDatasets:TFDJsonDatasets;
RecordCountFDQry1:Integer;
AThread:TThread; AThreadHandle:Cardinal; ifFinished:Boolean;//:线程;线程句柄;线程函数执行完毕的返回值
begin
iSql:=pSql; iSelectKey:=pSelectKey; ifFinished:=false;
FDJsonDatasets:=TFDJSONDataSets.Create;
Result:=TFDJSONDataSets.Create;
AThread:=TThread.CreateAnonymousThread(
procedure
begin
FDCon1:=TFDConnection.Create(nil); //FDCon此连接用完后不能释放只能断开交给连接管理员Pooling管理
FDCon1.ConnectionDefName:='MSSQL';
FDQry1:=TFDQuery.Create(nil); //FDQuery此连接用完后不能释放只能断开交给连接管理员Pooling管理
FDQry1.Connection:=FDCon1;
FDQry1.Active:=false; FDQry1.DisableControls;
FDQry1.SQL.Text:=iSql; FDQry1.EnableControls; //' select module_name,Report_name,com_id from ctl04000 ';
//FDQry1.ResourceOptions.CmdExecMode :=TFDStanAsyncMode.amNonBlocking;//异步不阻塞执行查询
FDQry1.Active:=true;
//:下面的执行会报内存溢出:(其值可在连接管理员FDManager中事先设置好)
//if not(FDCon1.FetchOptions.RecordCountMode=cmFetched) then FDCon1.FetchOptions.RecordCountMode:=cmFetched;
//if not(FDCon1.FetchOptions.RowsetSize=999999) then FDCon1.FetchOptions.RowsetSize:=999999;
//if not(FDQry1.FetchOptions.RecordCountMode=cmFetched) then FDQry1.FetchOptions.RecordCountMode:=cmFetched;
//if not(FDQry1.FetchOptions.RowsetSize=999999) then FDQry1.FetchOptions.RowsetSize:=999999;
while FDQry1.State=dsInactive do sleep(0);//等待如果上面的尚未执行完毕FDQuery.Active:=true;
//TThread.Synchronize(nil,procedure begin if (FDQry1.RecordCount>0) then begin MainServerForm.Memo_Errors.Lines.Add('线程取得'+IntToStr(FDQry1.RecordCount)+'条记录'); end; end);
RecordCountFDQry1:=FDQry1.RecordCount; FDQry1.Active:=false;
while FDQry1.State<>dsInactive do sleep(0); FDCon1.Close; //:用完断开连接,但:千万不能释放连接和数据集
TFDJsonDatasetsWriter.ListAdd(FDJsonDatasets,iSelectKey,FDQry1);
ifFinished:=true; //:设置1个线程函数执行完毕的返回值
end );
//线程执行完毕后交由Windows核心来管理线程的释放FreeOnTerminate,不得自行写代码释放:
AThread.FreeOnTerminate:=true; AThreadHandle:=AThread.Handle; //AThread.GetHashCode;
AThread.start;
waitAThread(AThread); //:此线程进行单线程等待结束,AThread不结束,其后的代码不执行//:控制等待线程结束千万不能AThread.WaitFor会对所有线程进行等待
if (ifFinished=true) then //:线程函数执行完毕的返回值
begin //MainServerForm.Memo_Errors.Lines.Add('线程'+IntToStr(AThreadHandle)+'结束返回'+IntToStr(RecordCountFDQry1)+'条记录,传入参数AName:'+iSelectKey+',执行sql语句 :'+iSql);
Result:=FDJsonDatasets;//:回调客户端请求
TThread.Synchronize(nil, procedure begin MainServerForm.Memo_Errors.Lines.Add('线程'+IntToStr(AThreadHandle)+'结束返回'+IntToStr(RecordCountFDQry1)+'条记录,传入参数AName:'+iSelectKey+',执行sql语句 :'+iSql); end );
end;
end;
二、在TServerMethods1中若用到数据库连接及数据集增删改查
(一)、连接和数据集
1、连接应当用TFDConnect实例化AFDCon,AFDCon:=TFDConnect.Create(nil);
用完之后关闭,AFDCon.close;而不应当释放AFDCon.Free;
2、数据集应当用TFDQuery实例化AFDQry,AFDQry:=TFDQuery.Create(nil);
用完之后应当在其所属连接AFDCon关闭之前先关闭,AFDQry.close;或AFDQry.Active:=False;然后再断开其连接AFDCon.close;
(二)、TServerMethods1若使用子线程
1、在TServerMethods1接受客户端并发请求时,为使TServerMethods1能够成功并准确的回调客户端请求,应当进行单线程等待结束。否则线程尚未Execute执行完毕,就直接Result,一会造成客户端无法正确获取数据,二是可能会服务器抛出与Result相关的异常。
2、子线程应当Create后不立即执行Start,而由代码控制其执行的时机。
3、应当在子线程Start前,设置好释放规则:AThread.FreeOnTerminate:=true;
4、子线程可使用句柄、Hash值,来进行其跟踪。
(三)、不使用线程来执行TServerMethods1方法也可以的
因为TServerMethods1本身就是线程机制在执行,所以不在其中使用子线程将会获取更高的CPU执行效率,因为回调函数必须等待子线程执行结束才能正确返回值否则会报错。
关键还是控制好连接及其下相关的数据集组件。
效率对比:
TFDJsonDatasets回调数据集(劣势:目前没有过滤器的使用方法说明),不使用线程:
获取服务器数据所需时间(ms):25969
截止UI加载数据所需时间(ms):26000
TFDJsonDatasets回调数据集,使用线程:
获取服务器数据所需时间(ms):52186
截止UI加载数据所需时间(ms):52239
TDataset回调数据集(优势:能使用过滤器),借助内存表中转回调函数,不使用线程:
获取服务器数据所需时间(ms):29815
截止UI加载数据所需时间(ms):29850
三、关于回调的数据集TFDJsonDatasets
如果客户端请求:pSql:=' select module_name,Report_name,com_id,Report_Data from ctl04000 ';
其中包含Sql2000系列数据库表字段Report_Data,字段类型为Image图形,且该图形数据量很大1兆B以上(此例为4.33兆B)
此情形不易使用TFDJsonDatasets回调数据集,而应当使用如下方法:
1、采用TStream内存流
2、采用大数据方法JSonToBJson->BJsonToJSon
3、或图形存储于服务器磁盘文件,读取并进行Http或Tcp/ip协议下载至客户端,并由客户端程序读取到内存流,并由客户端TImage组件进行图形还原显示处理。
4、因本案图形存储内容是Fastrepport的打印格式,故可使用文件流载入下载后的图形文件:
frxReport1.LoadFromFile('Untitled.fr3') ;//调打印格式设计时模板文件
四、有需要的朋友可联系QQ:584798030