Rest服务器TServerMethods1方法中的数据库连接、超多线程与防止内存溢出的问题

RestFul服务器TServerMethods1方法中的

数据库连接、超多线程与防止内存溢出的问题

 

Rest服务器TServerMethods1方法中的数据库连接、超多线程与防止内存溢出的问题

Rest服务器TServerMethods1方法中的数据库连接、超多线程与防止内存溢出的问题

Rest服务器TServerMethods1方法中的数据库连接、超多线程与防止内存溢出的问题

Rest服务器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