DataSnap Server
DSServer1Disconnect
这个函数什么时候执行?
void __fastcall TServerContainer1::DSServer1Disconnect(TDSConnectEventObject *DSConnectEventObject)
客户端正常退出的时候,会执行,SQLConnection1->Close();
如果客户端异常退出,进程异常终止,单独的demo异常终止会执行DSServer1Disconnect,但是主框架程序异常终止为何不触发??
Project E:\AppServer\myserver.exe faulted with message: 'application-defined exception (code 0x0eedfade) at 0x75e4c41f'. Process Stopped. Use Step or Run to continue.
是你的子线程直接操作 VCL的缘故??
方式一
这样创建的Form,进程中断也正常,触发了 DSServer1Disconnect。
Application->CreateForm(__classid(Tfrmyhdl), &frmyhdl);
方式二
如果用下面的方式创建Form,进程中断就不触发DSServer1Disconnect,server就不正常了!
frmyhdl = new Tfrmyhdl(Application);
frmyhdl->ShowModal();
delete frmyhdl;
但是不能因此而改变客户端的写法吧,如果客户端突然停电,服务器难道也要异常退出?所以应该还是服务端DataSnap的问题吧
客户端不调用方法,只连接,不关闭连接,也是出现这种情况,再次验证了方式二的窗体确实有问题。
dm->SQLConnection1->Close();
dm->SQLConnection1->Params->Values["HostName"] = LabeledEdit1->Text;
dm->SQLConnection1->Params->Values["Port"] = LabeledEdit2->Text;
dm->SQLConnection1->Open();
从服务端着手研究,新建一个空白datasnap服务端,然后客户端连接不关闭连接,进程中断,服务端是好的,没有退出。
下来比较空白工程和自己写的server代码里有什么是线程不安全的?
果然,ServerContainerUnit1.cpp里的所有事件的记录日志的功能去掉就OK啦,不闪退了。
客户端异常中断后不触发DSServer1Disconnect事件,DSTCPServerTransport1Disconnect事件里也获取不到参数信息。所以最终解决方案是删除DSTCPServerTransport1Disconnect事件就可以了。
经过试验,取消ServerContainerUnit1事件就正常了,屏蔽里边的代码事件保留也不行,必须把事件代码删除完,事件为空
void __fastcall TServerContainer1::DSTCPServerTransport1Disconnect(TDSTCPDisconnectEventObject Event) 客户端两种方式都可以正常了,服务端也不退出了。
ServerContainerUnit1其他事件代码保留正常,就DSTCPServerTransport1Disconnect有问题啊!
delphi里DSTCPServerTransport1Disconnect事件正常触发,可以获取信息(TIdTCPConnection*)Event.Connection,但是在c++builder里,这个事件触发了,但是Event.Connection无法获取真实的值。
有人也发现c++builder的这个datasnap的不过。
http://codeverge.com/embarcadero.datasnap/tdstcpdisconnecteventobject-is-real-bug/1097541
In C++Builder XE2 and XE3 event handler for TCP_connect: void __fastcall TServerContainer1::DSTCPServerTransport1Connect(TDSTCPConnectEventObject &Event) { Form1->ListBox1->Items->AddObject(IntToHex((int)Event.Connection,8),NULL) ; } and for TCP_disconnect void __fastcall ServerContainer1::DSTCPServerTransport1Disconnect(TDSTCPDisconnectEventObject Event) { Form1->ListBox1->Items->AddObject(IntToHex((int)Event.Connection,8),NULL) ; } passed not equal objects Event.Connection in parameters the object Event.Connection is not equalent in both handlers, but for DELPHI XE2 and XE3 this handlers passes equalent objects Event.Connection and all is well In CPU-view the real address of Event.Connection is saved in register EDX and this handler of disconnect is very well: void __fastcall TServerContainer1::DSTCPServerTransport1Disconnect(TDSTCPDisconnectEventObject Event) { TObject* PCONNECT=(TObject*)_EDX; Form1->ListBox1->Items->AddObject(IntToHex((int)PCONNECT,8),NULL) ; } on_connect and on_disconnect passes the same objects Connection, and visually can make sessions and users lists that is "ONLINE" Edited by: matsenko vladimir on Jan 31, 2013 10:50 AM
每次调用方法都会触发下面两个事件。
void __fastcall TServerContainer1::DSServerClass1Prepare(TDSPrepareEventObject *DSPrepareEventObject)
void __fastcall TServerContainer1::DSServer1Prepare(TDSPrepareEventObject *DSPrepareEventObject)
http://stackoverflow.com/questions/16602756/strange-0x0eedfade-exception-in-delphi-multi-thread-program
http://blog.devart.com/using-dac-products-in-multi-tier-db-application-development.html
声明周期选择LifeCyly/Session,dataSnapServer的内存不断增加,客户端退出了内存也不释放,而且越来越慢50个客户端*20次以后就慢了,是个隐患!这就是内存泄露?不知道delphi的应用服务器内存释放是否正常。
断开连接的4个事件都触发了。只能是内存泄露。
DSServer1Disconnect
DSServerClass1DestroyInstance
DSTCPServerTransport1Disconnect
DSServerModuleDestroy
经过试验,只连接SQLConnection1->Open();不执行任何方法,也不创建TServerMethods1Client对象,内存增长几乎忽略不计。调用了其中一个方法EchoString后就不断的吃内存了!
用delphi做了一个应用服务器,只有EchoString方法,是正常的,调用500次,内存不增长,没有内存泄露,增速是0.3M!
看来是c++builder的问题了。不知何时能解决!XE8了还是这样,每500次增长20~30M,用户那里天20M增长量。20M*60天=1.2G啊
c8新建空白工程加EchoString内存不增长,测试错误了?
FDConnection1的open放在create事件中
有FDConnection1->Open时,100次5M,500 次20M
在create事件中
没有任何FDConnection1的Open操作,无FDConnection1->Open时,500次,6M
在EchoString函数中打开并关闭连接
500次,4M
String TServerMethods1::EchoString(String value)
{
FDConnection1->Open(dm->FireDACConString);
FDConnection1->Close();
return value;
}
在EchoString函数中new打开并关闭连接,500次,4M
String TServerMethods1::EchoString(String value)
{
TFDConnection *con ;
con = new TFDConnection(NULL);
con->Open(dm->FireDACConString);
delete con;
return value;
}
把new在放在create事件中,delete在destroy事件打开并关闭连接,500次,23M,不正常了!
void __fastcall TServerMethods1::DSServerModuleCreate(TObject *Sender)
{
log("TServerMethods1DSServerModuleCreate");
// FDConnection1->Open(dm->FireDACConString);
con = new TFDConnection(NULL);
con->Open(dm->FireDACConString);
}
void __fastcall TServerMethods1::DSServerModuleDestroy(TObject *Sender)
{
FDConnection1->Close();
con->Close();
delete con;
log("TServerMethods1DSServerModuleDestroy");
}
内存问题新发现
后来发现是这个FDConnection1->Open(dm->FireDACConString);代码引起的,屏蔽此代码内存就不会无限增大。
void __fastcall TServerMethods1::DSServerModuleCreate(TObject *Sender)
{
log("TServerMethods1DSServerModuleCreate");
// FDConnection1->Open(dm->FireDACConString);
}
即使在destroy事件里关闭也不行,内存也不会释放。
void __fastcall TServerMethods1::DSServerModuleDestroy(TObject *Sender)
{
FDConnection1->Close();
}
在每个函数里执行query,然后切记关闭连接内存问题也就解决了。FDConnection1->Close();
越来越慢的问题
当客户端50数*登陆40次=2000次就开始慢了。
跟踪发现客户端
SQLConnection1->Open();不慢
TServerMethods1Client *aserver = new TServerMethods1Client(dm->SQLConnection1->DBXConnection);//不慢
aserver->log("我把日志写到服务端了");//调用第一个函数的时候就慢了,
调用后面的函数速度不慢,很快。无论第一次调用哪个函数都慢,调整函数的顺序,总之第一个函数就慢。
调用第一个函数触发服务端的事件如下
DSServerClass1Instance-Create
TServerMethods1DSServerModuleCreate
fun(我把日志写到服务端了);
应用服务器一个进程ex的线程数最大是200,上不去了。
客户端程序,正常退出,服务端的连接数减少正常,如果使用结束任务的方式结束进程,服务端的连接数未减少,这样导致进程无限制上升,积累到200个以后新的客户端就无法再连接上了。
解决办法:实时关闭,执行完一次方法就关闭连接。