我在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;