zoukankan      html  css  js  c++  java
  • 多层数据库应用基于Delphi DataSnap方法调用的实现对象池技术

    之前说到中间层通过向客户端暴露方法的方式提供服务,实现数据库数据的读取和更新。方法调用的方式,其潜在的意义,就是说中间层不保存客户端状态信息,就像WEB服务一样,客户端需要自己保存自己的状态信息。进一步说,就是中间层具体提供方法的业务对象实例,不是也不应该专属于某个客户端,它应该能够为不同的客户端调用提供服务。如果我们把业务对象实例放到对象池中集中存放,调用方法时随用随取,方法结束即放回池中。这样就可以实现业务对象实例服务于不同的客户端调用请求。更重要的是,利用对象池,能够最大化服务器各种资源的使用效率,而且对客户端的响应也更快了,因为业务对象实例早就创建好了,取来即用。

    其实,DataSnap构架,已经为我们的这种构想提供了现实支持。简单的说,就是改造工厂类(TDSServerClass),把LifeCycle属性改为Invocation方式;在OnCreateInstance事件中从对象池中取业务类对象实例;在OnDestroyInstance事件中把业务类对象实例放回对象池。

    ...
    procedure TsmMainForm.dssMethodsCreateInstance(
    DSCreateInstanceEventObject: TDSCreateInstanceEventObject);
    begin
    DSCreateInstanceEventObject.ServerClassInstance := ServerMethodsPool.LockPoolObject;
    end;

    procedure TsmMainForm.dssMethodsDestroyInstance(
    DSDestroyInstanceEventObject: TDSDestroyInstanceEventObject);
    begin
    ServerMethodsPool.UnlockPoolObject(TPersistent(DSDestroyInstanceEventObject.ServerClassInstance));
    end;
    ...
    当然,还有对象池类的创建和释放,也很简单,例如:
    procedure TsmMainForm.FormCreate(Sender: TObject);
    begin
    ServerMethodsPool := ObjPoolMgr.TPoolManager.Create;
    ServerMethodsPool.InstanceClass := uServerMethods.TPooledDM;
    end;

    procedure TsmMainForm.FormDestroy(Sender: TObject);
    begin
    ServerMethodsPool.Free;
    end;

    下面就是如何实现对象池技术的问题。实现对象池并不复杂,另有两个问题需要注意:

    1、多线程。中间层TDSTCPServerTransport对象提供的是多线程服务,允许同时有多个客户端请求。所以对象池类的实现,要考虑多线程情况下公共对象或变量的访问冲突问题。

    2、内存泄漏。业务类的基类,采用TDataModule、TComponent或者TPersistent都可以,但不要采用TDSServerModule,因为若采用此基类,TDSServerClass在Invocation方式下会产生内存泄漏。

    下面是对象池类最基本的实现代码:


    unit ObjPoolMgr;

    interface

    uses
    Classes, SyncObjs, SysUtils, DSServer, DateUtils;

    type

    PServerObject = ^TServerObject;
    TServerObject = record
    ServerObject: TPersistent;
    InUse: Boolean;
    end;

    TPoolManager = class
    private
    FCriticalSection: TCriticalSection;
    FServerObjects: TList;
    private
    FInstanceClass: TPersistentClass;
    function CreateNewInstance: TPersistent; inline;
    procedure SetInstanceClass(const Value: TPersistentClass);
    public
    constructor Create;
    destructor Destroy; override;
    //从对象池中取出一个业务类实例对象
    function LockPoolObject: TPersistent;
    //把一个业务类实例对象放回对象池中
    procedure UnlockPoolObject(var Value: TPersistent);
    public
    //指定放入池中的业务类。
    property InstanceClass: TPersistentClass read FInstanceClass write SetInstanceClass;
    end;

    implementation

    constructor TPoolManager.Create;
    begin
    FServerObjects := TList.Create;
    FCriticalSection := TCriticalSection.Create;
    end;

    destructor TPoolManager.Destroy;
    var
    I: Integer;
    begin
    for I := 0 to FServerObjects.Count - 1 do
    begin
    PServerObject(FServerObjects[i]).ServerObject.Free;
    FreeMem(PServerObject(FServerObjects[i]));
    end;
    FServerObjects.Free;
    FCriticalSection.Free;
    inherited Destroy;
    end;

    procedure TPoolManager.SetInstanceClass(const Value: TPersistentClass);
    begin
    FInstanceClass := Value;
    end;

    function TPoolManager.CreateNewInstance: TPersistent;
    var
    p: PServerObject;
    Component: TComponent;
    begin
    if not Assigned(FInstanceClass) then Raise Exception.Create('Not specify class of instance!');

    FCriticalSection.Enter;
    try
    if FInstanceClass.InheritsFrom(TComponent) then
    begin
    Component := FInstanceClass.NewInstance as TComponent;
    Component.Create(nil);
    Result := Component;
    end
    else
    Result := FInstanceClass.Create;

    New(p);
    p.ServerObject := Result;
    p.InUse := True;
    FServerObjects.Add(p);
    finally
    FCriticalSection.Leave;
    end;
    end;

    function TPoolManager.LockPoolObject: TPersistent;
    var
    i: Integer;
    begin
    FCriticalSection.Enter;
    try
    for i := 0 to FServerObjects.Count - 1 do
    begin
    if not PServerObject(FServerObjects[I]).InUse then
    begin
    PServerObject(FServerObjects[I]).InUse := True;
    Result := PServerObject(FServerObjects[i]).ServerObject;
    Exit;
    end;
    end;
    finally
    FCriticalSection.Leave;
    end;
    Result := CreateNewInstance;
    end;

    procedure TPoolManager.UnlockPoolObject(var Value: TPersistent);
    var
    i: Integer;
    begin
    FCriticalSection.Enter;
    try
    for i := 0 to FServerObjects.Count - 1 do
    begin
    if Value = PServerObject(FServerObjects[i]).ServerObject then
    begin
    PServerObject(FServerObjects[i]).InUse := False;
    Value := nil;
    Break;
    end;
    end;
    finally
    FCriticalSection.Leave;
    end;
    end;
    ...


    上面的基本实现稍加修改,就可以完善更多的功能,比如设定实例数上限、定时清除超过某个时间未被使用的实例、查询当前池中对象实例数量等。

  • 相关阅读:
    Java 期末考试
    Java 方法重载,方法重写(覆盖),继承等细节注意
    Java 方法(变量)修饰符的使用顺序
    java考试易错题大全
    python获取进程id号:
    C语言如何判断单个数字是否溢出:
    VS2017编译错误:#error: Building MFC application with /MD[d] (CRT dll version) requires MFC shared dll version
    VS2017出现不存在从"CString"到"const char*"的适当转换函数
    Python实现将图片以二进制格式保存到MySQL数据库中,以及取出:
    解决springboot 出现异常: java.net.BindException: Address already in use: bind
  • 原文地址:https://www.cnblogs.com/MaxWoods/p/2187831.html
Copyright © 2011-2022 走看看