在写代码之前,首先要考虑使用的协议类型, 是tcp/ip还是http(s),不同的协议对应不同的设计,tcp/ip是长连,服务器端可以知道当前有多少在线用户,http可以方便的改为IIS方式,不同的协议类型,Server Class LifeCycle(Server Class是指TDSServerClass)可以有不同的选择,不同的选择对应不同的设计, Server Class LifeCycle对于Datasnap的性能是非常重要的,分为 Server, Session, Invocation三种,每种类型的特点,网上可搜到相关文章或李维先生的电子书。
除了分批次,少量取数据提高性能外,服务方法中参数的数据类型也会对性能有影响,Datasnap是基于DBX framework的,在delphi帮助提到 Currently, using a TDBXValue is the fastest way to pass a parameter, because these are the internal objects used to manage parameter lists. 如果参数是一般的string等简单类型,基本可以忽略,但如果参数是TStream类型时,可以用TDBXStreamValue来替换,记得在XE2时候测试过,比一般TStream类型节约30%的返回时间。
----------------------------------------------
学无止境
除了参数类型外,通过Server Class LifeCycle的Server, Session, Invocation三种类型,还可以"榨出"Datasnap一些性能来,这个技巧应该在XE or XE2时就有了,当时EMB在土豆,奇艺上发过一些视频,基中就有介绍,目前在EMB的网站上也还有。具体的代码就不上了,主要是说说原理。 当使用tcp协议时,TDSServerClass的LifeCycle可以设置为Server,Session, Invocation的一种,http时,delphi的文档上说: For a REST client connection, if Session LifeCycle is used on the server class, it behaves like Invocation LifeCycle. 所以利用Invocation类型,我们可以缓存TDSServerClass实例。具体办法是 在TDSServerClass的OnCreateInstance和OnDestroyInstance事件中池化TDSServerClass的实例,这和网上写的监控客户端连接是一个意思。
----------------------------------------------
学无止境
所以使用TDSClientCallbackChannelManager的时机,应该是“只为了被动的接收其它客户端或服务端的消息通知”,这种方式实际上属于“服务端推送”的方式。当程中已经有一个用SQLConnection建立的连接时,更不要用TDSClientCallbackChannelManager去发送信息,最好服务器程序中提供类似这样的方式: function NotifyCallback(const AChannelName, AClientId, AValue: string): Boolean; begin Result := FDSServer.BroadcastMessage(AChannelName, AClientId, TJSonString.Create(AValue)); end 用SQLConnection的现有连接去执行消息的广播,这样就减少了服务程序的连接,断开次数。
----------------------------------------------
学无止境
我现在想使用https协议,我的做法是增加了一个TDSCertFiles控件,然后设置好TDSHTTPService的CertFile属性,然后下载了一个最新openssl,把需要的那两个dll文件放在程序的目录底下,然后我用https://localhost:8081/datasnap/rest/TServerMethods1/ReverseString/abc提交的时候就出现问题了,提示如下: First chance exception at $7544B760. Exception class EIdOSSLUnderlyingCryptoError with message 'Error accepting connection with SSL. error:1408A0C1:SSL routines:SSL3_GET_CLIENT_HELLO:no shared cipher'. Process Project1.exe (4656)
First chance exception at $7544B760. Exception class EIdOSSLUnderlyingCryptoError with message 'Error accepting connection with SSL. error:1408A10B:SSL routines:SSL3_GET_CLIENT_HELLO:wrong version number'. Process Server_Controller.exe (1224)
function TDSClientCallbackChannelManager.RegisterCallback( const CallbackId, ChannelNames: string; const Callback: TDBXCallback): Boolean; var Status: Boolean; Item, LItem: TDSCallbackItem; begin TMonitor.Enter(FLocalCallbackRepo); try if not FLocalCallbackRepo.ContainsKey(CallbackId) then begin Item := TDSCallbackItem.Create(FChannelName, Callback, ChannelNames); FLocalCallbackRepo.Add(CallbackId, Item);
if FLocalCallbackRepo.Count = 1 then begin FState := ctsStarted; FStateError := EmptyStr;
// start a thread if this is the first registered callback (check if there an active one) {**********} FDSChannelThread := TDSChannelThread.Create( 每注册一次回调就创建一个线程。 {**********} procedure begin try ExecuteRemote('DSAdmin', 'ConnectClientChannel', procedure (Params: TDBXParameterList) begin Params[0].Value.AsString := FChannelName; Params[1].Value.AsString := FManagerId;
destructor TDSClientCallbackChannelManager.Destroy; begin try // unregister all outstanding callbacks CloseClientChannel(); except // igonore I/O errors at this point end; {**********} TDSClientCallbackChannelManager释放的时候,FDSChannelThread 只释放一次。 {**********888} if FDSChannelThread <> nil then begin FDSChannelThread.WaitFor; FDSChannelThread.Free; end; {**********8}