Elaborazione di richieste lente

A volte è necessario gestire richieste lente. Per evitare che le richieste lente influenzino l'elaborazione delle altre richieste da parte di Webman, è possibile adottare diverse soluzioni a seconda della situazione.

Soluzione 1 Utilizzo di una coda dei messaggi

Fare riferimento a coda redis coda stomp

Vantaggi

Può gestire picchi imprevisti di richieste di elaborazione.

Svantaggi

Non è possibile restituire direttamente i risultati al client. Se è necessario inviare i risultati, è necessario utilizzare altri servizi, come ad esempio la funzionalità di push di webman/push.

Soluzione 2 Aggiunta di una porta HTTP

Aggiungere una porta HTTP per gestire le richieste lente. Queste richieste lente entreranno in un gruppo specifico di processi attraverso questa porta e i risultati verranno restituiti direttamente al client.

Vantaggi

Può restituire direttamente i dati al client.

Svantaggi

Non può gestire picchi imprevisti di richieste.

Passaggi di implementazione

Aggiungere la seguente configurazione in config/process.php.

return [
    // ... altre configurazioni omesse ...

    'task' => [
        'handler' => \Webman\App::class,
        'listen' => 'http://0.0.0.0:8686',
        'count' => 8, // numero di processi
        'user' => '',
        'group' => '',
        'reusePort' => true,
        'constructor' => [
            'requestClass' => \support\Request::class, // impostazione della classe request
            'logger' => \support\Log::channel('default'), // istanza di log
            'appPath' => app_path(), // posizione della directory app
            'publicPath' => public_path() // posizione della directory public
        ]
    ]
];

In questo modo, l'interfaccia lenta può passare attraverso http://127.0.0.1:8686/, senza influenzare l'elaborazione delle altre richieste.

Per consentire al front-end di non percepire la differenza tra le porte, è possibile aggiungere un proxy a nginx per la porta 8686. Supponendo che i percorsi delle richieste delle interfacce lente inizino tutti con /tast, la configurazione di nginx sarà simile a questa:

upstream webman {
    server 127.0.0.1:8787;
    keepalive 10240;
}

# Aggiungere un nuovo upstream per 8686
upstream task {
   server 127.0.0.1:8686;
   keepalive 10240;
}

server {
  server_name webman.com;
  listen 80;
  access_log off;
  root /path/webman/public;

  # Le richieste che iniziano con /tast passano attraverso la porta 8686, si prega di modificare /tast in base alle esigenze
  location /tast {
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header Host $host;
      proxy_http_version 1.1;
      proxy_set_header Connection "";
      proxy_pass http://task;
  }

  # Altre richieste passano attraverso la porta originale 8787
  location / {
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header Host $host;
      proxy_http_version 1.1;
      proxy_set_header Connection "";
      if (!-f $request_filename){
          proxy_pass http://webman;
      }
  }
}

In questo modo, quando il client accede a dominio.com/tast/xxx, verrà gestito dalla porta 8686, senza influenzare l'elaborazione delle richieste sulla porta 8787.

Soluzione 3 Utilizzare l'invio dei dati in chunk HTTP asincrono

Vantaggi

Può restituire direttamente i dati al client.

Installare workerman/http-client

composer require workerman/http-client

app/controller/IndexController.php

<?php
namespace app\controller;

use support\Request;
use support\Response;
use Workerman\Protocols\Http\Chunk;

class IndexController
{
    public function index(Request $request)
    {
        $connection = $request->connection;
        $http = new \Workerman\Http\Client();
        $http->get('https://example.com/', function ($response) use ($connection) {
            $connection->send(new Chunk($response->getBody()));
            $connection->send(new Chunk('')); // invia un chunk vuoto per segnalare la fine della response
        });
        // Inviare prima un'intestazione http, i dati successivi vengono inviati in modo asincrono
        return response()->withHeaders([
            "Transfer-Encoding" => "chunked",
        ]);
    }
}

Nota
In questo esempio è stato utilizzato il client workerman/http-client per ottenere i risultati http in modo asincrono e restituire i dati. È possibile utilizzare anche altri client asincroni, come AsyncTcpConnection.