RestFul客户端与RestFul应用服务器等多层分布式交互过程及多核多处理器环境下处理多线程或任务等相关问题-系列之一

RestFul客户端与RestFul应用服务器等多层分布式交互过程

及多核多处理器环境下处理多线程并行或任务等相关问题-系列之一

一、信息流图

RestFul客户端与RestFul应用服务器等多层分布式交互过程及多核多处理器环境下处理多线程或任务等相关问题-系列之一_第1张图片

二、常见问题:

1、套接字问题

RestFul客户端与RestFul应用服务器等多层分布式交互过程及多核多处理器环境下处理多线程或任务等相关问题-系列之一_第2张图片

RestFul客户端与RestFul应用服务器等多层分布式交互过程及多核多处理器环境下处理多线程或任务等相关问题-系列之一_第3张图片

RestFul客户端与RestFul应用服务器等多层分布式交互过程及多核多处理器环境下处理多线程或任务等相关问题-系列之一_第4张图片

客户端也会有类似报错:

RestFul客户端与RestFul应用服务器等多层分布式交互过程及多核多处理器环境下处理多线程或任务等相关问题-系列之一_第5张图片

原因:在最后一层,即:数据库连接层及事务处理层:DataBase Server,Socket:Address和IP套接字层:TFDConnection和TFDManager(两者要调用要唯一),TFDConnection和TFDManager同时处理连接,就会报错,比如:动态获取连接参数

procedure TServerMethods1.DataModuleCreate(Sender: TObject);
var iListcount:integer;
//触发时间:TWebModule1.DSServerClass1GetClass
begin
  //初始化动态获取数据库连接参数:
  if (FDConGlobal.ConnectionDefName).trim='' then
  begin
    FDConGlobal.Params.Clear;
    FDConGlobal.ConnectionDefName := 'MSSQL';
  end;

      //  ......此时,你要是同时,或在Appliaction Server的其它任何地方触发了类似操作 :

      //  FDManager1.GetConnectionDefParams('MSSQL',AList);  //
  if MainServerForm.myGetCatalogNames.Trim='' then
    with MainServerForm do
    begin
      myGetCatalogNames:=FDConGlobal.Params.Database;

      //  ......FDManager1.GetCatalogNames等等

    end;

end;

因而:在所有RestFul开发环境下,因为客户端是可以无状态的(取决于生命周期的设定方式),所有套接字问题,都是在分水岭TServerMethods1.DataModuleCreate事件触发的前后产生的!最底层的应用服务器以上的层次,是产生套接字问题的源泉。所谓套接字,指的是TCP的IP地址和Port端口,一次请求和响应的轮回,它们必须是唯一被使用的,其所有连接属性,不可以重复赋值或在其交互过程中被动态改变的,可以理解为其交互过程中它们必须是静态的。

2、应用服务器方法响应客户端获取时无效指针类问题

        if not Assigned(ADataSet) then
        ADataSet:=

          ClientModule1.ServerMethods1Client.getTFDDataSet(pSql,pSelectKey);
        //:调用服务端方法被客户端组件引用时,处理方式很重要,
          //:否则会报invalid Pionter错误!

RestFul客户端与RestFul应用服务器等多层分布式交互过程及多核多处理器环境下处理多线程或任务等相关问题-系列之一_第6张图片

       关键: if not Assigned(ADataSet) then ;  //在多核多处理器环境下,如果使用任务(线程池)或多线程并行,一定要注意!!!单核环境或win32应用不存在此问题!

3、应用服务器方法响应后客户端处理数据时对象锁监视问题

3.1、问题及解决方案:

RestFul客户端与RestFul应用服务器等多层分布式交互过程及多核多处理器环境下处理多线程或任务等相关问题-系列之一_第7张图片

RestFul客户端与RestFul应用服务器等多层分布式交互过程及多核多处理器环境下处理多线程或任务等相关问题-系列之一_第8张图片

并在客户端退出时,引发内存泄漏!

       应用环境:在多核多处理器环境下,如果使用任务(线程池)或多线程并行。单核环境或win32应用不存在此问题!

       正确方法:1、在任务代码中,使用TMonitor.Enter和TMonitor.Exit机制。(1)、在方法进入时:TMonitor.Enter(self,0); //多核多处理器环境,经常用uses system; (2)、在事件总线通知任务结束时之后:AEvent.SetEvent;//:事件总线通知:任务结束!  TMonitor.Exit(self); 

                         2、应当防止请求事件的组件(比如按钮)短时间内重复点击:所谓短时间内,指该请求事件被通知执行完毕的时间内(AEvent.SetEvent;  //:事件总线通知:任务结束)。(1)、应当在任务开始前,禁用“请求事件的组件”:按钮等.Enabled:=true; (2)、在事件总线通知任务结束之后:AEvent.SetEvent;//:事件总线通知:任务结束后:按钮等.Enabled:=true;

3.2、总体架构如下:

例程:

RestFul客户端与RestFul应用服务器等多层分布式交互过程及多核多处理器环境下处理多线程或任务等相关问题-系列之一_第9张图片

(1)、事件按钮等调用任务

procedure TfmxMatter.事件按钮等OnContitionsClick( Sender: TObject);

    getDatabaseFromServer('ctl03001',300,1,50,
      'select com_id,item_id,item_name,item_type, '
     +' convert(Datetime,convert(varchar(23),maintenance_datetime,121)) as maintenance_datetime '
     +' from ctl03001','物品资料');
end;

(2)、进入任务方法或函数

procedure TfmxMatter.getDatabaseFromServer(
  pSql,pSelectKey:string);

var ATask:ITask;  AEvent:TEvent; 
    ADataSet: TDataSet;   //ifFinishTask:Integer;
begin


  事件按钮等.Enabled:=false;
  Memo1.Lines.Clear;
  AEvent:=TEvent.Create; //:创建事件总线对象


     //多任务处理预留: if arrayTasks[0].Status=TTaskStatus.Running then arrayTasks[0].Wait(INFINITE);
        //ifFinishTask:=0;
  AEvent:=TEvent.Create; //:创建事件总线对象
     //创建并启动(多线程池化的)任务多核多处理器并行(多路CPU或GPU同时工作,或单处理器多核同步) //APool:= TThreadPool.Create; //:任务POSIX不能带池化参数产生


  ATask:=TTask.Create(
  procedure
    var AtestFieldDefsCircle:Integer;
  begin
    TMonitor.Enter(self,0); //多核多处理器环境,经常用uses system;
    try
      try
        if not Assigned(ADataSet) then //多核多处理器环境,单核环境或win32应用不存在此问题!
        ADataSet:=
          ClientModule1.ServerMethods1Client.getTFDDataSet(pSql,pSelectKey);
        //:调用服务端方法: //:其中ADataSet处理方式很重要,
          //:否则会报invalid Pionter错误:if not Assigned(ADataSet) then

        while ADataSet.State=dsInactive do sleep(0);
        if ADataSet.Active=true then ADataSet.First;
        FDMemTMain_Temp.FieldDefs.Clear;
        FDMemTMain_Temp.FieldDefs:=ADataSet.FieldDefs;
        //Memo1.Lines.Add('TFDMemTable字段数:'+IntToStr(FDMemTMain_Temp.FieldDefs.Count));
        FDMemTMain_Temp.Open;
        while not (ADataSet.Eof) do
        begin
          FDMemTMain_Temp.Append;
          for AtestFieldDefsCircle:=0 to FDMemTMain_Temp.FieldDefs.Count-1 do
          begin
            FDMemTMain_Temp.FieldByName(
              FDMemTMain_Temp.FieldDefs[AtestFieldDefsCircle].Name).Value
            :=ADataSet.FieldByName(
              FDMemTMain_Temp.FieldDefs[AtestFieldDefsCircle].Name).Value;
          end;
          FDMemTMain_Temp.Post;
          ADataSet.Next;
        end;
        //Memo1.Lines.Add('TFDMemTable记录数:'+IntToStr(FDMemTMain_Temp.RecordCount));
        FDMemTMain_Temp.Last; FDMemTMain_Temp.First;//不像其基类TDataSet,TClientDataSet可以任意操纵Cursor,而且是双向的游标。
        FDMemTMain_Temp.IndexesActive:=true;
        while not (FDMemTMain_Temp.Eof) do
        begin // 此段代码只用于调试,POSIX不允许重复操作Doublication not Allowed :
          Memo1.Lines.Add(
            '索引字段数:'+IntToStr(FDMemTMain_Temp.IndexFieldCount)+','
           +'内存表索引记录数:'+IntToStr(FDMemTMain_Temp.IndexDefs.DataSet.RecordCount)+','
           +'内存表索引:'+IntToStr(FDMemTMain_Temp.IndexDefs.DataSet.RecNo-1)+','
           +'运营商编码:'+FDMemTMain_Temp.FieldByName('com_id').AsString.Trim+','
           +'物品编码:'+FDMemTMain_Temp.FieldByName('item_id').AsString.Trim+','
           +'物品名称:'+FDMemTMain_Temp.FieldByName('item_name').AsString.Trim+','
           +'型号:'+FDMemTMain_Temp.FieldByName('item_type').AsString.Trim+','
          );
          FDMemTMain_Temp.Next;
        end;
        //:上面是任务的具体内容!
        AEvent.SetEvent;//:事件总线通知:任务结束!
      except // 万一异常,代码很重要 :
        TMonitor.Exit(self);
        事件按钮等.Enabled:=true;
        AEvent.Free; //:出现异常释放:事件总线!
        exit; //:出现异常就不要再往下执行了!
        raise Exception.Create('出错或取消啦');
      end;
    finally
      TMonitor.Exit(self);
      事件按钮等.Enabled:=true;
      //AEvent.Free;

          //:切忌:将事件总线在try内释放AEvent.Free;
    end;
    //事件总线AEvent.SetEvent后:确认任务是否完成:
        //Assert(AEvent.WaitFor(15000)>TWaitResult.wrSignaled);
    //:调试时:确认任务结束时长的信息标记,即AEvent.SetEvent,然后:
    AEvent.Free; //:确认收到任务结束的信号标记即AEvent.SetEvent后再:释放事件总线对象
  end );
  //多任务管理://arrayTasks[0]:=ATask;//TTask.WaitForAll(arrayTasks); //if arrayTasks[0].Status=TTaskStatus.Running then arrayTasks[0].Wait(INFINITE);
  ATask.Start;

end;

// 商业及企业级应用:需要代码,请联系: qq :  584798030

RestFul客户端与RestFul应用服务器等多层分布式交互过程及多核多处理器环境下处理多线程或任务等相关问题-系列之一_第10张图片

 

 

 

 

 

 

 

你可能感兴趣的:(数据库,分布式多层,delphi,App开发)