Langsame Geschäftsverarbeitung

Manchmal müssen wir langsame Geschäftsabläufe verarbeiten. Um zu vermeiden, dass langsame Geschäftsabläufe die Verarbeitung anderer Anfragen in Webman beeinträchtigen, können je nach Situation unterschiedliche Lösungsansätze verwendet werden.

Ansatz eins: Verwendung einer Nachrichtenwarteschlange

Siehe Redis-Warteschlange STOMP-Warteschlange

Vorteile

Kann plötzliche, massenhafte Geschäftsverarbeitungsanfragen bewältigen.

Nachteile

Es kann kein direktes Ergebnis an den Client zurückgegeben werden. Wenn Ergebnisse gesendet werden müssen, ist die Zusammenarbeit mit anderen Diensten nötig, zum Beispiel mit webman/push, um die Verarbeitungsresultate zu pushen.

Ansatz zwei: Hinzufügen eines HTTP-Ports

Ein neuer HTTP-Port wird hinzugefügt, um langsame Anfragen zu verarbeiten. Diese langsamen Anfragen gelangen über den Zugriff auf diesen Port in eine spezielle Gruppe von Prozessen zur Verarbeitung, wobei das Ergebnis direkt an den Client zurückgegeben wird.

Vorteile

Daten können direkt an den Client zurückgegeben werden.

Nachteile

Kann plötzliche, massenhafte Anfragen nicht bewältigen.

Implementierungsschritte

Fügen Sie in config/process.php die folgende Konfiguration hinzu.

return [
    // ... andere Konfigurationen hier weggelassen ...

    'task' => [
        'handler' => \Webman\App::class,
        'listen' => 'http://0.0.0.0:8686',
        'count' => 8, // Anzahl der Prozesse
        'user' => '',
        'group' => '',
        'reusePort' => true,
        'constructor' => [
            'requestClass' => \support\Request::class, // request-Klasse einstellen
            'logger' => \support\Log::channel('default'), // Log-Instanz
            'appPath' => app_path(), // Standort des app-Verzeichnisses
            'publicPath' => public_path() // Standort des public-Verzeichnisses
        ]
    ]
];

Damit können langsame Schnittstellen die Gruppe von Prozessen unter http://127.0.0.1:8686/ verwenden, ohne die Geschäftsverarbeitung anderer Prozesse zu beeinträchtigen.

Um für das Frontend die Portdifferenz unsichtbar zu machen, kann ein Proxy für den Port 8686 in Nginx hinzugefügt werden. Angenommen, die Anforderungs-Pfade der langsamen Schnittstellen beginnen alle mit /tast, dann könnte die gesamte Nginx-Konfiguration wie folgt aussehen:

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

# Fügen Sie ein weiteres upstream für 8686 hinzu
upstream task {
   server 127.0.0.1:8686;
   keepalive 10240;
}

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

  # Anfragen, die mit /tast beginnen, gehen über den Port 8686, bitte ändern Sie /tast entsprechend Ihrer benötigten Präfixe
  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;
  }

  # Andere Anfragen gehen über den ursprünglichen Port 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;
      }
  }
}

Wenn der Client dann domain.com/tast/xxx aufruft, wird dies über den separaten Port 8686 verarbeitet, ohne dass die Anfragen auf dem Port 8787 beeinträchtigt werden.

Ansatz drei: Verwendung von HTTP Chunked für asynchrone, segmentweise Datenübertragung

Vorteile

Daten können direkt an den Client zurückgegeben werden.

Installieren Sie 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('')); // Leeren Chunk senden, um das Response zu beenden
        });
        // Zuerst einen HTTP-Header senden, die nachfolgenden Daten werden asynchron gesendet
        return response()->withHeaders([
            "Transfer-Encoding" => "chunked",
        ]);
    }
}

Hinweis
In diesem Beispiel wird der workerman/http-client verwendet, um asynchron HTTP-Ergebnisse abzurufen und Daten zurückzugeben. Es können auch andere asynchrone Clients verwendet werden, wie z.B. AsyncTcpConnection.