代码之家  ›  专栏  ›  技术社区  ›  Soon Santos

Delphi Web服务器可以接收多个调用吗?

  •  1
  • Soon Santos  · 技术社区  · 6 年前

    我有一个用Delphi制作的web服务器,它负责从MySQL服务器获取数据并检索格式化的数据 JSON . 下面是一个简单的示例,说明它如何获取 loteamentos DB .

    type
      TWM = class(TWebModule)
        ...
        procedure WMactLoteamentosAction(Sender: TObject; Request: TWebRequest;
          Response: TWebResponse; var Handled: Boolean);
    
        ...
    
    procedure TWM.WMactLoteamentosAction(Sender: TObject; Request: TWebRequest;
      Response: TWebResponse; var Handled: Boolean);
    var
      qryLoteamentos: TFDQuery;
      JsonArray: TJSONArray;
      JsonObject: TJSONObject;
    begin
      Response.ContentType := APPLICATION_JSON + '; ' + CHARSET_UTF8;
    
      // Search for loteamentos
      qryLoteamentos := TFDQuery.Create(nil);
      with qryLoteamentos do
      begin
        Connection := FDConnection;
        Active := False;
        SQL.Clear;
    
        Open('SELECT * FROM ' + T_LOTEAMENTO);
    
        if qryLoteamentos.RecordCount > 0 then
        begin
          JsonArray := TJSONArray.Create;
          try
            First;
            while not Eof do
            begin
              JsonObject := TJSONObject.Create;
              CapturarCamposLoteamento(JsonObject, qryLoteamentos);
              JsonArray.AddElement(JsonObject);
              Next;
            end;
          finally
            Response.Content := JsonArray.ToString;
            JsonArray.DisposeOf;
          end;
        end
        else
          handleEmptyResponse(Response);
      end;
    end;
    
    

    JSON文件

    应用程序将在一台机器上运行,MySQL将来自这台机器的本地主机,用户将通过外部IP和端口访问web服务器。

    因此,如果服务器在外部IP为45.65.89.187的机器的端口9070上运行

    方法将按以下方式调用:

    GET -> http://45.65.89.187/loteamentos

    它会为我找回这样的东西:

    [{"id":1,"nome":"RESIDENCIAL ...","metros":"348516,57"},
    
    {"id":2,"nome":"RESIDENCIAL ...","metros":"215465,65"}]
    

    问题

    1. /loteamentos 多次。它不会使服务器崩溃吗?

    2. 我很奇怪,人们在同一时间调用同一个端点,却不会在同一个端点中创建一行 Thread

    我做了什么

    我测试了在4部手机中多次从web服务器调用端点。Web服务器的运行速度从2MB开始,经过多次调用后,几分钟内就会达到40MB。然后,我停止调用它,但它保持在40MB并且没有降低。

    0 回复  |  直到 6 年前
        1
  •  1
  •   Robert    6 年前

    当客户机发出第一个请求时,WebBroker应用程序将创建TWebModule的第一个实例。 每当客户机的第二个HTTP请求到达WebBroker应用程序时,WebBroker框架将搜索先前创建的WebModule实例是否空闲(空闲=它没有执行请求操作处理程序)。 如果没有WebModule的空闲实例,那么将实例化一个新的TWebModule。 默认情况下,当负载较高时,WebBroker应用程序将创建多达32个TWebModule实例。数字32由属性Application.MaxConnections定义。 请注意,同时请求已经是多线程的,并且请求处理程序中的所有代码都必须是线程安全的。 在测试负载处理时,您可以添加一个长睡眠(10000)来拥有许多繁忙的web请求处理程序,并检查应用程序如何响应。然后很容易达到Application.MaxConnections的限制,从而导致异常。

    您的Web服务器可能会消耗40MB,因为WebBroker框架在负载达到峰值时(Application.InactiveConnections+Application.ActiveConnections=10)创建了10个实例TWebModule实例。如果TWebModule在它的构造函数中分配对象,或者如果它的DFM中有许多组件,那么它们都将持久存在。

        2
  •  0
  •   Ganesh Padmanabhan    5 年前

    我在delphi10.1下使用普通webBroker

    我有一个接受Json字符串的服务器。我解析它并将相同的数据发送回客户端。

    我的假设是Webbroker默认有32个线程,当客户端的并发请求开始攻击服务器并在32处停止时,这些线程将逐步创建(请参阅web.webreq.pas)

    为了测试这个场景,我创建了一个简单的客户机程序,它有一个For循环,该循环将通过一个带有JSon字符串的请求在服务器上不断触发。

    当我运行客户机程序的另一个并行实例时,WebModule的第二个实例就被创建了。当我再运行一个并行的客户端实例时,就会创建第三个WebModule实例……以此类推

    有趣的是。。。。

    还记得我的第一个客户端程序运行时,10K请求所用的时间是14秒吗?我观察到以下情况-随着并发客户机数量的增加, 处理所需的时间也会增加 当3个客户端程序同时触发请求时,实际上需要42秒才能完成(总共30K个请求)。

    你能澄清一下Web代理是否真的是多线程的吗?如果是这样,我还缺什么?

    我已经在这里附加了客户端和服务器源代码。

    客户代码如下

    enter code here

    unit URestclient;
    
    interface
    
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs, REST.Types, REST.Client,
      Data.Bind.Components, Data.Bind.ObjectScope, Vcl.StdCtrls, JsonTools, system.DateUtils;
    
    type
      TForm21 = class(TForm)
        RESTClient1: TRESTClient;
        RESTRequest1: TRESTRequest;
        RESTResponse1: TRESTResponse;
        Button1: TButton;
        Memo1: TMemo;
        Label2: TLabel;
        Edit1: TEdit;
        Edit2: TEdit;
        Label1: TLabel;
        Label3: TLabel;
        Label4: TLabel;
        Edit3: TEdit;
        procedure Button1Click(Sender: TObject);
      private
        procedure AfterRun;
        function SendJSonDataToIVR(MachineID, JS: String): boolean;
        { Private declarations }
      public
        { Public declarations }
      end;
    
    var
      Form21: TForm21;
      RESTClient: TRESTClient;
      RESTRequest: TRESTRequest;
      RESTResponse: TRESTResponse;
    
      N: TJSONnode;
      JS : String;
      i : integer;
    
    
    implementation
    
    {$R *.dfm}
    
    procedure TForm21.AfterRun;
    begin
      Memo1.Lines.Add(RESTResponse.content);
    end;
    
    procedure TForm21.Button1Click(Sender: TObject);
    var
      st, et :  TDatetime;
      S : String;
    begin
      Label2.caption := 'Started!';
      Label2.Repaint;
    
      memo1.Clear;
      JS := '{"CallStatus":"t","CallType":"N","BookingType":"I","CallerType":"C","RoomNo":"1788882",'
             +'"Channel":"1191","OriginalChannel":"1191","MobileNumber":"09123456789","DialTry":"1","MaxTry":"1"'
             +',"ServerID":"3","StartTime":"2020-10-13 10:41:40","EndTime":"2020-10-13 10:41:40",'
             +'"TransferNumber":"","Dstatus":"","userid":"1"}';
    
      S := 'http://' + trim(edit2.text) + ':' + trim(edit1.text) + '/tconnected';
      st := now;
    
      for i := 0 to strtoint(trim(Edit3.Text)) do
        SendJSonDataToIVR(S, JS);
    
      et := now;
    
      Label2.Caption := 'Time Taken (in ms) ' + MilliSecondsBetween(et, st).ToString;
    end;
    
    Function TForm21.SendJSonDataToIVR(MachineID : String; JS : String) : boolean;
    begin
      SendJSonDataToIVR := false;
      RESTClient := TRESTClient.Create('nil');
      RESTRequest := TRESTRequest.Create(nil);
      RESTRequest.Client := RESTClient;
    
      RESTClient.BaseURL := MachineID;
      RESTRequest.ClearBody;
      RESTRequest.AddBody(JS, ctAPPLICATION_JSON);
    
      RESTRequest.Method := TRESTRequestMethod.rmPost;
      RESTResponse := TRESTResponse.Create(nil);
      RESTRequest.Response := RESTResponse;
    
      RESTRequest.Execute();
      //Memo1.Lines.Add(RESTResponse.content);
    //  RESTRequest.ExecuteAsync(AfterRun, true, True);
    end;
    end.
    

    ** 服务器代码如下

    unit wmTConnected;
    
    interface
    
    uses
      System.SysUtils, System.Classes, Web.HTTPApp, JSontools,
      Data.DB,
      FireDAC.Stan.Def,
      FireDAC.Phys.PG,
      FireDAC.Phys.PGDef,
      FireDAC.DApt,
      FireDAC.Stan.Async,
      FireDAC.Stan.Option,
      FireDAC.Comp.Client, FireDAC.Stan.Intf,
      FireDAC.Stan.Error, FireDAC.UI.Intf, FireDAC.Phys.Intf, FireDAC.Stan.Pool,
      FireDAC.Phys, FireDAC.ConsoleUI.Wait;
    
    type
      TWebModule2 = class(TWebModule)
        procedure WebModule2TConnectedAction(Sender: TObject; Request: TWebRequest;
          Response: TWebResponse; var Handled: Boolean);
      private
        { Private declarations }
        procedure ParseJson(var Request: TWebRequest);
      public
        { Public declarations }
      end;
    
    var
      WebModuleClass: TComponentClass = TWebModule2;
      N : TJsonNode;
    
    implementation
    
    {%CLASSGROUP 'System.Classes.TPersistent'}
    
    {$R *.dfm}
    
    procedure TWebModule2.ParseJson(var Request: TWebRequest);
    begin
      N := TJsonNode.Create;
      try
        N.Parse(Request.Content);
        except
          begin
            Response.Content := 'Something went wrong during parsing of Incoming Json from Client machine';
          end;
      end;//try
    end;
    
    procedure TWebModule2.WebModule2TConnectedAction(Sender: TObject;
      Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
    begin
      ParseJson(Request);
      writeln(Timetostr(now));
      Response.Content := Request.Content;
    end;
    
    end.
    

    type
      TJsonNode = class
      public
        { A parent node owns all children. Only destroy a node if it has no parent.
          To destroy a child node use Delete or Clear methods instead. }
        destructor Destroy; override;
        { GetEnumerator adds 'for ... in' statement support }
        function GetEnumerator: TJsonNodeEnumerator;
        { Loading and saving methods }
        procedure LoadFromStream(Stream: TStream);
        procedure SaveToStream(Stream: TStream);
        procedure LoadFromFile(const FileName: string);
        procedure SaveToFile(const FileName: string);
        { Convert a json string into a value or a collection of nodes. If the
          current node is root then the json must be an array or object. }
        procedure Parse(const Json: string);
        { The same as Parse, but returns true if no exception is caught }
        function TryParse(const Json: string): Boolean;
        { Add a child node by node kind. If the current node is an array then the
          name parameter will be discarded. If the current node is not an array or
          object the Add methods will convert the node to an object and discard
          its current value.
     
          Note: If the current node is an object then adding an existing name will
          overwrite the matching child node instead of adding. }
        function Add(const Name: string; K: TJsonNodeKind = nkObject): TJsonNode; overload;
        function Add(const Name: string; B: Boolean): TJsonNode; overload;
        function Add(const Name: string; const N: Double): TJsonNode; overload;
        function Add(const Name: string; const S: string): TJsonNode; overload;
        { Delete a child node by index or name }
        procedure Delete(Index: Integer); overload;
        procedure Delete(const Name: string); overload;
        { Remove all child nodes }
        procedure Clear;
        { Get a child node by index. EJsonException is raised if node is not an
          array or object or if the index is out of bounds.
     
          See also: Count }
        function Child(Index: Integer): TJsonNode; overload;
        { Get a child node by name. If no node is found nil will be returned. }
        function Child(const Name: string): TJsonNode; overload;
        { Search for a node using a path string }
        function Find(const Path: string): TJsonNode;
        { Format the node and all its children as json }
        function ToString: string; override;
        { Root node is read only. A node the root when it has no parent. }
        property Root: TJsonNode read GetRoot;
        { Parent node is read only }
        property Parent: TJsonNode read FParent;
        { Kind can also be changed using the As methods:
     
          Note: Changes to Kind cause Value to be reset to a default value. }
        property Kind: TJsonNodeKind read FKind write SetKind;
        { Name is unique within the scope }
        property Name: string read GetName write SetName;
        { Value of the node in json e.g. '[]', '"hello\nworld!"', 'true', or '1.23e2' }
        property Value: string read GetValue write Parse;
        { The number of child nodes. If node is not an object or array this
          property will return 0. }
        property Count: Integer read GetCount;
        { AsJson is the more efficient version of Value. Text returned from AsJson
          is the most compact representation of the node in json form.
     
          Note: If you are writing a services to transmit or receive json data then
          use AsJson. If you want friendly human readable text use Value. }
        property AsJson: string read GetAsJson write Parse;
        { Convert the node to an array }
        property AsArray: TJsonNode read GetAsArray;
        { Convert the node to an object }
        property AsObject: TJsonNode read GetAsObject;
        { Convert the node to null }
        property AsNull: TJsonNode read GetAsNull;
        { Convert the node to a bool }
        property AsBoolean: Boolean read GetAsBoolean write SetAsBoolean;
        { Convert the node to a string }
        property AsString: string read GetAsString write SetAsString;
        { Convert the node to a number }
        property AsNumber: Double read GetAsNumber write SetAsNumber;
      end;