2011年10月31日星期一

datasnap的进阶 TDSHTTPService的工作方式

TDSHTTPService继承自TDSHTTPServerTransport->TDSServerTransport。它的HttpServer是负责传输,HttpServer是TDSHTTPServerIndy类,这个类的FServer是IIPHTTPServer接口,实现该接口的是TIdHTTPServerPeer类,这个类的FHTTPServer是TIdHTTPServerIP,TIdHTTPServerIP继承自TIdHTTPServer,从而和indy发生关系。

和indy要发生关系,经过了
TDSHTTPService.HttpServer-> TDSHTTPServerIndy.FServer -> 
  IIPHTTPServer(TIdHTTPServerPeer).FHTTPServer -> TIdHTTPServerIP  -> TIdHTTPServer
目前TDSHTTPService并没有像TDSTCPServerTransport一样公布Indy的socket的OnConnect,OnDisconnect。所以要监视链接,得自己写代码。好在明白了上面的实现关系,代码就可以写成如下
(TDSHTTPServerIndy(DSHTTPService1.Server).Server as TIdHTTPServerPeer).FHTTPServer.OnConnect :=
  procedure(AContext: TIdContext)
  begin

  end;
或者
(TDSHTTPServerIndy(DSHTTPService1.Server).Server as TIdHTTPServerPeer).SetOnConnect(
  procedure(AContext: IIPContext)
  begin

  end
  );
遗憾的是,目前
方法1里Datasnap.DSHTTP单元的TDSHTTPServerIndy类并没有公开出来,TIdHTTPServerPeer的FHTTPServer是个私有变量
方法2里除了TDSHTTPServerIndy没公开外,TIdHTTPServerPeer的方法SetOnConnect是个protected。
上面的法子目前都不灵,只有走RTTI的hack方法来做。

在Datasnap.DSHTTP单元里,
procedure TDSHTTPServerIndy.InitializeServer;
begin
  FServer.UseNagle := False;
  FServer.KeepAlive := True;
  FServer.ServerSoftware := 'DatasnapHTTPService/2011';

  FServer.OnCommandGet := Self.DoIndyCommand;
  FServer.OnCommandOther := Self.DoIndyCommand;
end;
也就将TDSHTTPServerIndy.DoIndyCommand和TIdHTTPServer.OnCommandGet和TIdHTTPServer.OnCommandOther 给关联上了。目前的datasnap只关联了TIdHTTPServer的这两个事件。

剩下的就不必说了,TIdHTTPServer解析完了HTTP,给将数据给DoIndyCommand,DoIndyCommand再实施调度服务方法的工作。
TDSHTTPServer.DoCommand来进行任务分发工作,举其中一个类型JSON_CONTEXT来说明。
procedure TDSHTTPServer.DoJSONCommand(ARequestInfo: TDSHTTPRequest;
                                      AResponseInfo: TDSHTTPResponse;
                                      Request: String);
begin
  if FCredentialsPassThrough then
    JSONService := TDSJSONService.Create(FDSServerName, FDSHostname, FDSPort, ARequestInfo.AuthUserName, ARequestInfo.AuthPassword, IPImplementationID)
  else
    JSONService := TDSJSONService.Create(FDSServerName, FDSHostname, FDSPort, FDSAuthUser, FDSAuthPassword, IPImplementationID);

  SessionID := GetRequestSessionId(aRequestInfo, True);
   ...
        case CmdType of
        hcGET:
          JSONService.ProcessGETRequest(Request, nil, ByteContent(ARequestInfo.PostStream),
                                        RespHandler);
          hcPOST:
            JSONService.ProcessPOSTRequest(Request, nil, ByteContent(ARequestInfo.PostStream),
                                        RespHandler);
          hcPUT:
            JSONService.ProcessPUTRequest(Request, nil, ByteContent(ARequestInfo.PostStream),
                                        RespHandler);
          hcDElETE:
            JSONService.ProcessDELETERequest(Request, nil, ByteContent(ARequestInfo.PostStream),
                                        RespHandler);
          else
          begin
            GetInvocationMetadata().ResponseCode := 501;
            GetInvocationMetadata().ResponseContent := Format(SCommandNotSupported, [ARequestInfo.Command]);
          end;
        end;
   ...
    if RespHandler = nil then
      FreeAndNil(JSONService);

    if RespHandler <> nil then
      RespHandler.Close;
  end;
end;

  constructor TDSService.Create(dsServerName, dsHostname: String; dsPort: Integer; AuthUser, AuthPassword, AIPImplementationID: String);
  begin
    inherited Create;
    FDBXProperties := TDBXDatasnapProperties.Create(nil);
    if DSServerName = EmptyStr then  //没指定了TDSHTTPService的Server时,就是用DSHostname, DSport建立DBXConnection,调用远程方法
    begin
      FDBXProperties.Values[ TDBXPropertyNames.DriverName ] := DRIVER_NAME;
      FDBXProperties.Values[ TDBXPropertyNames.HostName ] := dsHostname;
      FDBXProperties.Values[ TDBXPropertyNames.Port ] := IntToStr( dsPort );
      FLocalConnection := false; //不是本地连接
    end
    else
    begin  
      FDBXProperties.Values[ TDBXPropertyNames.DriverName ] := DSServerName; //指定了TDSHTTPService的Server时,就是自己的本地服务方法调用
      FLocalConnection := true; //是本地的
    end;
    FDBXProperties.DSAuthUser := AuthUser;
    FDBXProperties.DSAuthPassword := AuthPassword;
    FDBXProperties.Values[ TDBXPropertyNames.IPImplementationID ] := AIPImplementationID;

    StreamAsJSON := False;
  end;

可以看到DoJSONCommand里面,新建立一个TDSService,然后执行了TDSService.Execute。也就是说,每次的HttpRequest请求,TDSHTTPServer都会建立一个全新的DBXConnection,连上TDSHTTPService的DSHostname,DSPort指向的Server,然后执行完毕后断开DBXConnection连接,释放TDSService。这也就解释了前面说的用TDSHTTPService来做负载和容错,可以靠DSHostname,DSPort来做的原因: TDSHTTPService只是剥离了HTTP的外衣,然后自己当客户端调用了服务罢了

没有评论:

发表评论