关于用DELPHI开发服务器软件中的一些经验(100分)

E

element

Unregistered / Unconfirmed
GUEST, unregistred user!
总结了一些经验,现贴出于大家讨论。
1、构件的使用
开始我一直使用Indy,但最近在开发一个100-350并发用户的服务器端时发现了Indy问题,由于用户的访问非常繁重,服务器工作两周后出现了70多的废连接。导致服务器响应变的慢。而且我观察到有时INDY能自动释放这些废连接,有时多到200多都不释放。后来我改DXSock 3.0构件,代码没有作任何改动,服务从6月1日到今天没有重启一次而且没有一个废连接,
我花了一周看DXSock的代码,改了些错误,也学习了很多,DXSock构成思想很好。它的连接响应线程不是动态创建的,而是你设置多少就一次性创建多少。节省了创建、释放的大量时间。
所以再开发服务器端软件我推进使用DXSOCK构件。

2、数据库使用
很多朋友在讨论多线程中使用数据库的问题,这在网络服务器开发中这是常常需要考虑的问题,很多朋友都采用一个线程建立一个数据库的连接,我认为这种方法在小规模、小并发应用中是可以的,但是一旦负荷上去、并发一多这样就很多问题了,比如资源消耗(数据库)、时间消耗。这种方式我用过,结果是运行一段时间后数据库再也连接不上了。
我再这方面有两个解决办法:
a、采用ASTA构件的方法,根据分析负荷一次性创建一个ADOCONNEC连接池,靠一个同步模块来管理,客户端请求-》构造SQL-》让同步管理模块来分配一个ADOCONNECT联机-》执行SQL,返回结果,如果这时ADOCONNECT满,此SQL等待。代码如下:
b、对于不需要实时返回数据库信息的应用,客户端请求来-》服务器创建SQL语句和一个TAG-》SQL语句送入队列,然后一条一条的执行,TAG返回客户端,过一段时间客户端再通过这个TAG来服务端获得结果。
********************************************队列管理代码***********************
Unit uitQueueManage;

Interface

Uses
Windows,
Messages,
SysUtils,
Variants,
Classes,
Contnrs;

Type
TSyncManage = Class(TObject)
Protected
FHandle: THandle;
Public
Property Handle: THandle Read FHandle;
Constructor Create(Const Name: String; InitCount, MaxBuffer: Integer);
Destructor Destroy; Override;
Function Release: Boolean; Overload;
Function WaitFor(TimeOut: Integer): Boolean;
End;

Type
PQueueItem = ^TQueueItem;
TQueueItem = Record
FRequestCount: Integer;
FCommand: Integer;
FDataStr: String;
FDataInt: Integer;
FDataBool: Boolean;
End;
TProgressEvent = Procedure(Sender: TObject; aQueueItem: TQueueItem) Of Object;
TQueueManage = Class(TObject)
Private
FQueue: TQueue;
FSyncManage: TSyncManage;
FOnProgress: TProgressEvent;
FLock: TRTLCriticalSection;
Public
Constructor Create(aQueueName: String; aBuffer: Integer);
Destructor Destroy; Override;
Procedure PushQueue(aQueueItem: TQueueItem);
Published
Property OnProgress: TProgressEvent Read FOnProgress Write FOnProgress;
End;

Type
TQueueThread = Class(TThread)
Private
hSyncManage: TSyncManage;
hOwner: TQueueManage;
hQueue: TQueue;
Procedure DoProgress;
Protected
Procedure Execute; Override;
Public
Constructor Create(aOwner: TQueueManage; aQueue: TQueue; aSyncManage: TSyncManage); Virtual;
End;

Implementation

//***********************************************************TSyncThread**************************

Constructor TSyncManage.Create(Const Name: String; InitCount, MaxBuffer: Integer);
Begin
FHandle := CreateSemaphore(Nil, InitCount, MaxBuffer, Pchar(Name));
If FHandle = 0 Then abort;
End;

Destructor TSyncManage.Destroy;
Begin
If FHandle <> 0 Then CloseHandle(FHandle);
End;

Function TSyncManage.WaitFor(TimeOut: Integer): Boolean;
Begin
Result := WaitForSingleObject(FHandle, TimeOut) = WAIT_OBJECT_0;
End;

Function TSyncManage.Release: Boolean;
Begin
Result := ReleaseSemaphore(FHandle, 1, Nil);
End;

//***********************************************************TQueueThread**************************

Constructor TQueueThread.Create(aOwner: TQueueManage; aQueue: TQueue; aSyncManage: TSyncManage);
Begin
Inherited Create(True);
hOwner := aOwner;
hQueue := aQueue;
hSyncManage := aSyncManage;
Priority := tpHigher;
Resume;
End;

Procedure TQueueThread.Execute;
Begin
While Not Terminated Do
Begin
hSyncManage.WaitFor(Integer(INFINITE)); //无限等
If Terminated Then Exit;
DoProgress;
End;
End;

Procedure TQueueThread.DoProgress;
Var
mQueueItem: PQueueItem;
Begin
mQueueItem := hQueue.Pop;
If Assigned(hOwner.FOnProgress) Then hOwner.FOnProgress(hOwner, mQueueItem^);
Dispose(mQueueItem);
End;

//***********************************************************TQueueManage*************************

Var
FQueueThread: TQueueThread;

Constructor TQueueManage.Create(aQueueName: String; aBuffer: Integer);
Begin
Inherited Create;
InitializeCriticalSection(FLock);
FQueue := TObjectQueue.Create;
FSyncManage := TSyncManage.Create(aQueueName, 0, aBuffer); //缓冲区大小
FQueueThread := TQueueThread.Create(Self, FQueue, FSyncManage);
End;

Destructor TQueueManage.Destroy;
Begin
EnterCriticalSection(FLock);
Try
FQueueThread.Terminate;
FSyncManage.Release;
FreeAndNil(FQueueThread);
FreeAndNil(FSyncManage);
FreeAndNil(FQueue);
Inherited Destroy;
Finally
LeaveCriticalSection(FLock);
DeleteCriticalSection(FLock);
End;
End;

Procedure TQueueManage.PushQueue(aQueueItem: TQueueItem);
Var
mQueueItem: PQueueItem;
Begin
New(mQueueItem);
mQueueItem^ := aQueueItem;
EnterCriticalSection(FLock);
FQueue.Push(mQueueItem);
LeaveCriticalSection(FLock);
FSyncManage.Release;
End;

End.
********************************************队列管理代码***********************
ADO连接同步管理我稍后放上来,欢迎大家讨论。
 
这样的贴子很有学习意义。
UP
 
学习,期待ADO连接同步管理
 
什么地方有DXSock .30下载?
 
很好,谢谢了!
 
什么地方有DXSock .30下载?

楼主真他妈是个好人。
我选楼主为 2003年大富翁杰出青年
 
呵呵,200~300个连接压力不够哦,DXSocket也就一般,
而且DELPHI自己的TQueue类效率很低,我自己改写了一个速度比他快8倍,看来
做服务器程序最好都自己从头写,用现成的始终不是最好的解决方法,而且用别
人的破戒的东西有偷盗之嫌。
 
很不错.
当然大一点的系统,建议还是不要用长连接,这样多数系统能服务的客户端就可以高出很多,一般的系统并发机率可能只有5-15%,我想很少会有系统超过30%以上.不过非长连接的话就要考虑维护会话状态的成本.
indy中大量使用类似如下的代码
ms := TMemorystream.create;
ms.write(....)...
这些是很没效率的,如果不为TMemorystream一次性分配足够空间,那么TMemorystream在分配的空间不够用的时候,就需要重新分配,这是一个极没效率的操作.频繁的重新分配,拷贝原数据等,使得TMemorystream在某些时间甚至比硬盘读写都要慢几倍.所以用indy的话就要分析一下系统的实际情况了.

 
一切都为了最求快,一切都要自己写,那就失去使用DELPHI的意义了。
服务器实际负荷=并发数*4.3。
 
楼上的你能否你写的Query给我学习,pingshx@163.com ,THX
楼住能否发你用的DXSocket给我,THX!
 
DELPHI一样可以从头来做的,只要你有耐心。
 
element:
你这话我听很多用VC的这么说,他们的意思就是DELPHI只能做一般的普通应用,
难的都要VC做。
 
ADO连接同步管理代码,我是从应用中剥离出来的,对不起不能提供全部。供大家讨论
Unit uitSyncConnect;

Interface

Uses
Windows,
Messages,
SysUtils,
Variants,
Classes,
Contnrs,
Activex,
ADODB,
DB;

Const
csMaxQueueThread = 9;

Type
TSyncManage = Class(TObject)
Protected
FHandle: THandle;
Public
Property Handle: THandle Read FHandle;
Constructor Create(Const Name: String; InitCount, MaxBuffer: Integer);
Destructor Destroy; Override;
Function Release: Boolean; Overload;
Function WaitFor(TimeOut: Integer): Boolean;
End;

Type
PQueueItem = ^TQueueItem;
TQueueItem = Record
rThreadTag: Integer;
rEvent: THandle;
rADOQuery: TADOQuery;
rIsCommand: Boolean;
rIsError: Boolean;
rMessage: String[255];
End;
TSyncConnect = Class(TObject)
Private
FThreadCount: Integer;
FQueue: TQueue;
FSyncManage: TSyncManage;
FLock: TRTLCriticalSection;
Public
Constructor Create(Const aQueueName: String; Const aConnectStr: String; Const aBuffer: Integer = 1024; Const aThreadCount: Integer = 5);
Destructor Destroy; Override;
Procedure PushQueue(Var aQueueItem: TQueueItem);
Published

End;

Type
TQueueThread = Class(TThread)
Private
hSyncManage: TSyncManage;
hQueue: TQueue;
hTag: Integer;
hADOConnect: TADOConnection;
Protected
Procedure Execute; Override;
Public
Constructor Create(Const aConnStr: String; Const aTag: Integer; aQueue: TQueue; aSyncManage: TSyncManage); Virtual;
Destructor Destroy; Override;
End;

Implementation

//***********************************************************TSyncThread**************************

Constructor TSyncManage.Create(Const Name: String; InitCount, MaxBuffer: Integer);
Begin
FHandle := CreateSemaphore(Nil, InitCount, MaxBuffer, PChar(Name));
If FHandle = 0 Then abort;
End;

Destructor TSyncManage.Destroy;
Begin
If FHandle <> 0 Then CloseHandle(FHandle);
End;

Function TSyncManage.WaitFor(TimeOut: Integer): Boolean;
Begin
Result := WaitForSingleObject(FHandle, TimeOut) = WAIT_OBJECT_0;
End;

Function TSyncManage.Release: Boolean;
Begin
Result := ReleaseSemaphore(FHandle, 1, Nil);
End;

//***********************************************************TQueueThread**************************

Constructor TQueueThread.Create(Const aConnStr: String; Const aTag: Integer; aQueue: TQueue; aSyncManage: TSyncManage);
Begin
Inherited Create(True);
hQueue := aQueue;
hSyncManage := aSyncManage;
Priority := tpHigher;
hADOConnect := TADOConnection.Create(Nil);
hADOConnect.ConnectionString := aConnStr;
hTag := aTag;
Resume;
End;

Destructor TQueueThread.Destroy;
Begin
Terminate;
WaitFor;
FreeAndNil(hADOConnect);
Inherited Destroy;
End;

Procedure TQueueThread.Execute;
Var
mQueueItem: PQueueItem;
Begin
While Not Terminated Do
Begin
hSyncManage.WaitFor(Integer(INFINITE)); //ÎÞÏÞµÈ
If Terminated Then Exit;
If hQueue.Count <= 0 Then Exit;
mQueueItem := hQueue.Pop;
mQueueItem.rThreadTag := hTag;
mQueueItem.rIsError := False;
CoInitialize(Nil);
If Assigned(mQueueItem.rADOQuery) Then
Begin
Try
mQueueItem.rADOQuery.Connection := hADOConnect;
If mQueueItem.rIsCommand Then
mQueueItem.rADOQuery.ExecSQL
Else
mQueueItem.rADOQuery.Open;
Except
On E: Exception Do
Begin
mQueueItem.rIsError := True;
mQueueItem.rMessage := E.Message;
End;
End;
End;
CoUninitialize;
SetEvent(mQueueItem^.rEvent);
End;
End;

//***********************************************************TSyncConnect*************************

Var
FQueueThread: Array[0..csMaxQueueThread] Of TQueueThread;

Constructor TSyncConnect.Create(Const aQueueName: String; Const aConnectStr: String; Const aBuffer: Integer = 1024; Const aThreadCount: Integer = 5);
Var
i: Integer;
Begin
Inherited Create;
InitializeCriticalSection(FLock);
FQueue := TObjectQueue.Create;
FSyncManage := TSyncManage.Create(aQueueName, 0, aBuffer);
If aThreadCount - 1 > csMaxQueueThread Then FThreadCount := csMaxQueueThread Else FThreadCount := aThreadCount - 1;
For i := 0 To FThreadCount Do
FQueueThread := TQueueThread.Create(aConnectStr, i, FQueue, FSyncManage);
End;

Destructor TSyncConnect.Destroy;
Var
i: Integer;
Begin
EnterCriticalSection(FLock);
Try
For i := 0 To FThreadCount Do
Begin
FQueueThread.Terminate;
FSyncManage.Release;
FreeAndNil(FQueueThread);
End;
FreeAndNil(FSyncManage);
FreeAndNil(FQueue);
Finally
LeaveCriticalSection(FLock);
DeleteCriticalSection(FLock);
Inherited Destroy;
End;
End;

Procedure TSyncConnect.PushQueue(Var aQueueItem: TQueueItem);
Var
mQueueItem: PQueueItem;
Begin
New(mQueueItem);
mQueueItem^ := aQueueItem;
mQueueItem^.rEvent := CreateEvent(Nil, False, False, Nil);
EnterCriticalSection(FLock);
FQueue.Push(mQueueItem);
LeaveCriticalSection(FLock);
FSyncManage.Release;
WaitForSingleObject(mQueueItem^.rEvent, 60000);
ResetEvent(mQueueItem^.rEvent);
CloseHandle(mQueueItem^.rEvent);
aQueueItem := mQueueItem^;
Dispose(mQueueItem);
End;

End.
 
我感觉你的代码里有很多的New Dispose,很多地方改缓冲的资源没有缓冲,需要提高一些效率。
 
张无忌:
你好,首先我要声明我是一个忠实的DELPHI使用者,我就是看到很多VC的论坛在讨论服务器技术,所以我才想我什么DELPHI不能写出同样的系统呢?
我希望这只是个开端,希望大家多多讨论DELPHI这方面的应用。
下一步我还想写一些关于DELPHI在基础协议方面的文字,
比如:
Radius Server的编写
802.1x协议的DELPHI 的实现
PCANDIS的DELPHI封装API等等
 
请张无忌兄多多指教。
 
Procedure TSyncConnect.PushQueue(Var aQueueItem: TQueueItem);
Var
mQueueItem: PQueueItem;
Begin
New(mQueueItem);
mQueueItem^ := aQueueItem;
mQueueItem^.rEvent := CreateEvent(Nil, False, False, Nil);
EnterCriticalSection(FLock);
FQueue.Push(mQueueItem);
LeaveCriticalSection(FLock);
FSyncManage.Release;
WaitForSingleObject(mQueueItem^.rEvent, 60000);
ResetEvent(mQueueItem^.rEvent);
CloseHandle(mQueueItem^.rEvent);
aQueueItem := mQueueItem^;
Dispose(mQueueItem);
End;
//这个里面你可以一次创建足够的队列结构个数,比如队列最大长度1000个结构,循环使用
这些结构。1000个足够了,我自己的服务器处理并发1000个连接也没有到这个数,我测试过最大128个。频繁的调用New Dispose会找成内存脆片,。不利于稳定性
Procedure TQueueThread.Execute;
Var
mQueueItem: PQueueItem;
Begin
CoInitialize(Nil);//放在这,不用每次解析请求都要初始化,同时我感觉你这样的ADO处理可能有点问题

While Not Terminated Do
Begin
hSyncManage.WaitFor(Integer(INFINITE)); //ÎÞÏÞµÈ
If Terminated Then Exit;
If hQueue.Count <= 0 Then Exit;
mQueueItem := hQueue.Pop;
mQueueItem.rThreadTag := hTag;
mQueueItem.rIsError := False;
// CoInitialize(Nil);

 
顶部