本文示例源代碼或素材下載
DataSnap 2009的服務器對象的生命周期依賴於DSServerClass組件的設置
當DSServer啟動時從DSServerClass組件讀取LifeCycle屬性的值
注意:LifeCycle的值由於在啟動時就已經讀取 啟動後再修改LifeCycle的值將沒有任何效果
LifeCycle屬性的值可以是以下三種字符串之一
1.Session
該選項為默認設置
每個連接都會建立一個獨立的服務器對象為客戶端提供服務,服務器對象在連接關閉後釋放
因此多個客戶端訪問的是不同的服務器對象,是線程安全的
2.Invocation
對於每次服務端方法調用建立一個獨立的服務器對象為客戶端提供服務,服務器對象在調用結束後釋放
這個同樣也是線程安全的
但是每次調用都創建和釋放服務器對象對於頻繁調用的系統影響很大,如果把服務端對象用對象池管理配合此種方式將是個非常不錯的解決方案
3.Server
所有的客戶端使用同一個服務端對象,也就是該對象是單例的
需要開發人員自己來進行同步的控制,不是線程安全的
在服務端對象創建和釋放時將觸發DSServerClass的兩個重要的事件OnCreateInstance和OnDestroyInstance
在這裡我們可以使用自定義創建和釋放服務器對象 同樣我們可以用於服務端對象池
下面我們把上一次的DEMO稍微改動下來觀察下服務端對象的生命周期
我們先將DSServer組件的AutoStart設置為False 然後拖上兩個Button分別完成Start和Stop的調用
procedure TMainForm.StartClick(Sender: TObject);
begin
DSServer.Start;
end;
procedure TMainForm.StopClick(Sender: TObject);
begin
DSServer.Stop;
end;
在OnGetClass中記錄服務啟動時使用的生命周期
procedure TMainForm.DSServerClassGetClass(DSServerClass: TDSServerClass;
var PersistentClass: TPersistentClass);
begin
DSServerClass.LifeCycle := LifeCycles.Items.Strings[LifeCycles.ItemIndex];
LogMessage(Memo, '生命周期:' + DSServerClass.LifeCycle);
PersistentClass := TSM;
end;
LifeCycles是一個TRadioGroup存放了生命周期使用的三個字符串
最後在OnCreateInstance和OnDestroyInstance事件中記錄服務器對象的創建和釋放
procedure TMainForm.DSServerClassCreateInstance(
DSCreateInstanceEventObject: TDSCreateInstanceEventObject);
begin
LogMessage(Memo, '服務端對象創建');
end;
procedure TMainForm.DSServerClassDestroyInstance(
DSDestroyInstanceEventObject: TDSDestroyInstanceEventObject);
begin
LogMessage(Memo, '服務端對象釋放');
//DSDestroyInstanceEventObject.ServerClassInstance.Free;
end;
效果圖
圖片看不清楚?請點擊這裡查看原圖(大圖)。
通過Demo我們可以明顯的看出三種生命周期的區別 注意切換生命周期需要先停止服務器再啟動
但是在我們使用Invocation的時候 會造成內存洩露
打開服務端的ReportMemoryLeaksOnShutdown 調用了兩次方法後關閉服務端可以看到如下提示
可以看到服務端對象並沒有釋放
這裡需要我們通過在OnDestroyInstance手動釋放
DSDestroyInstanceEventObject.ServerClassInstance.Free;
但是我們會發現內存洩露依然存在TDSProviderDataModuleAdapter依然沒有釋放
這是由於DataSnap2009中繼承自TProviderDataModule的類都使用了適配器模式來支持舊的IAPPServer接口
在服務端對象創建的過程TDSServerClass.CreateInstance中我們可以看到
if (Instance <> nil) and Instance.InheritsFrom(TProviderDataModule) then
CreateInstanceEventObject.ServerClassInstance := TDSProviderDataModuleAdapter.Create(Instance);
因此在服務端釋放的TDSServerClass.DestroyInstance中需要釋放TDSProviderDataModuleAdapter對象
if DestroyInstanceEventObject.ServerClassInstance is TDSProviderDataModuleAdapter then
begin
Adapter := DestroyInstanceEventObject.ServerClassInstance as TDSProviderDataModuleAdapter;
DestroyInstanceEventObject.ServerClassInstance := Adapter.FProviderDataModule;
Adapter.FProviderDataModule := nil;
end else
Adapter := nil;
當使用Invocation生命周期時 傳遞的ServerClassInstance並不是TDSProviderDataModuleAdapter的對象
所以盡管我們手動釋放了我們的服務端對象 適配器對象任然造成了內存洩露