Search code examples
multithreadingdelphiwebserver

A Delphi webserver can receive multipe calls?


I have a webserver made in Delphi which is responsible to fetch data from MySQL server and retrieve formatted in JSON. Here is a simple example of how it fetches the list of loteamentos from the 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;

The logic of the method doesn't matter too much, it just matters that it fetch a table from the database and retrieve it in JSON.

The application will be running in a machine, the MySQL will be from this machine's localhost and the user will access the webserver by an external IP and the port.

Therefore, if the server is running on port 9070 in a machine which external IP is for example 45.65.89.187

The method will be called in the following way:

GET -> http://45.65.89.187/loteamentos

It will retrieve for me something like this:

[{"id":1,"nome":"RESIDENCIAL ...","metros":"348516,57"},

{"id":2,"nome":"RESIDENCIAL ...","metros":"215465,65"}]

Questions

  1. My question is, suppose 100 people are using my API on their phone. Imagine 100 people calling this same endpoint /loteamentos multiple times. Wouldn't it crash the server?

  2. I wonder that people calling the same endpoint at the same time doesn't create a line in the same Thread and disturb the server? Shouldn't I put the webserver to run in MultiThreading?

What I've done

I tested calling the endpoints from the webserver multiple times in 4 phones. The webserver start running at 2MB, after multiple calls it goes up to 40MB in a couple of minutes. Then, I stop calling it, but it keeps on 40MB and does not get lower.


Solution

  • A WebBroker application will create the first instance of TWebModule when the first request from a client comes in. Whenever a 2nd HTTP request of a client arrives at the WebBroker application, the WebBroker framework will search if the previously created WebModule instance is idle (idle = it is not executing a request action handler). If there are no idle instances of WebModule then a new TWebModule will be instantiated. The code for that is in Web.WebReq.pas, function TWebRequestHandler.ActivateWebModules: TComponent; By default a WebBroker application will create up to 32 TWebModule instances when the load is high. That number 32 is defined by the property Application.MaxConnections. Be aware that simultanious requests are already multithreaded, and all the code in your request handlers must be thread safe. A single TWebModule instance will only serve 1 request at a time, any other concurrent requests will be served by other instances of your TWebModule. With the possibility of having multiple TWebModule instances serving requests in parallel, the queries in a single TWebModule instance should use their own dedicated DB connection instance. While testing the load handling you could add a long Sleep(10000) to have many busy web request handlers, and check how your application responds. It's then easy to hit the limit of Application.MaxConnections, resulting in an exception.

    Your webserver might consume 40MB because the WebBroker framework has created for instance 10 TWebModule instances when the load was at it's peak (Application.InactiveConnections + Application.ActiveConnections = 10). If your TWebModule allocates objects in it's constructor, or if it has many components in it's DFM then they will all persist.

    Be also aware that any client-specific data should not reside in the TWebModule itself when the request is finished. Client A might be served by TWebModule instance 1 during the first request, and by instance 2 serves Client B simultaniously. In the next simultanious request, Client A might be served by instance 2 and Client B by instance 1.