SocketIO-Kommunikation mit einem Gateway zwischen Client und Service?

8

Kern

Ich habe eine Anwendung, die auf einer Microservice-basierten Architektur (auf Kubernetes) ausgeführt wird. Die gesamte Kommunikation zu / von außerhalb der Anwendung erfolgt über eine API-Gateway .

Das bedeutet nur, dass Anfragen von meinem Frontend nicht direkt an die Dienste gehen, sondern über das Gateway.

Motiv

Jetzt muss ich eine Funktion implementieren, die eine Echtzeitkommunikation zwischen dem Frontend und einem internen Dienst erfordert. Da der interne Dienst jedoch nicht nach außen ausgerichtet ist, muss die Echtzeitdaten über das Gateway "weitergeleitet" werden.

Alle meine Dienste werden auf Node.js ausgeführt. Aus diesem Grund möchte ich Socket.IO verwenden , um die Echtzeitkommunikation zu implementieren.

die Architektur

Problem

Aber wie implementiert man den lila Doppelpfeil aus der Skizze?

Normalerweise stellt der Frontend-Client eine Verbindung zu dem Server her, auf dem Socket.IO ausgeführt wird. In meinem Fall ist dieser Server (der Echtzeit-Feature-Server) vom Client aus nicht zugänglich (und sollte es auch nie sein), was bedeutet, dass der Client eine Verbindung zum Gateway herstellen muss. Daher muss das Gateway einen Mechanismus implementieren, um alle eingehenden Nachrichten an den Echtzeitdienst und umgekehrt zu leiten.

Ideen

(1) Lassen Sie einen zweiten HTTP-Server auf Ereignisse auf dem Gateway warten und senden Sie diese Ereignisse an den Echtzeitserver. In der anderen Richtung sendet der Echtzeitserver Ereignisse an das Gateway, die sie dann an das Frontend senden. Ich denke, dieser Ansatz wird definitiv funktionieren, aber es scheint überflüssig, alles zweimal zu emittieren. Und es würde definitiv die Leistung beeinträchtigen?

(2) Verwenden Sie einen Socket.IO-Adapter, um " Ereignisse zwischen Knoten zu übergeben ". Dies scheint der richtige Weg zu sein, da er zum "Übergeben von Nachrichten zwischen Prozessen oder Computern" verwendet wird. Ich habe jedoch Probleme beim Einstieg, da es an Dokumentation / Beispielen mangelt. Ich benutze auch kein Redis (wird der Adapter benötigt?)

(3) Verwenden Sie das Paket socket.io-emitter , das seit dem letzten Commit vor 3 Jahren keine gute Option zu sein scheint.

(4) Noch etwas?

Florian Ludewig
quelle
Sie müssen also das API-Gateway verwenden, um den Datenverkehr umzuleiten? Wir haben socket.io in der pour-Anwendung implementiert, aber der Dienst wurde in einem separaten Dienst (Pod) bereitgestellt. Bei Verwendung von ingress-controllerk8s wird diesocket Datenverkehr an den Fußballdienst .
Giorgio Cerruti
@GiorgioCerruti Nein , ich nicht haben , um das Gateway zu verwenden, aber das ist , wie ich arbeiten , um meine Anwendung geplant. Sie würden also empfehlen, nur meinen "internen Service" über Ingress verfügbar zu machen?
Florian Ludewig
Ich habe es nginxals Ingress-Controller gemacht. Sie können auch Haproxy verwenden, wenn Sie es vorziehen. Sie haben 2 Dienste (Bereitstellung), 1 zum Verwalten von HTTP-Anforderungen und 1 zum Verwalten von Socket-Anforderungen. Mit ingress können Sie den Pfad verfügbar machen und den Datenverkehr an den richtigen Dienst /socketumleiten, z. B. jede Anforderung, an die ein Treffer weitergeleitet wirdsocket-service Anwendung .
Giorgio Cerruti
ok das klingt wie etwas Würze versuchen, weil ich bereits nginx Ingress benutze. Es wäre fantastisch, wenn Sie eine Antwort veröffentlichen könnten, die zeigt, wie sie implementiert wird (Hier ist die Eingangskonfiguration, die ich derzeit verwende: github.com/flolu/centsideas/blob/… )
Florian Ludewig

Antworten:

3

Okay, im Grunde habe ich die Anwendung so gestaltet

Ingress

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: centsideas-ingress
  annotations:
    kubernetes.io/tls-acme: 'true'
    kubernetes.io/ingress.class: 'nginx'
    cert-manager.io/cluster-issuer: letsencrypt
spec:
  tls:
    - hosts:
        - centsideas.com
        - api.centsideas.com
      secretName: centsideas-tls
  rules:
    - host: api.centsideas.com
      http:
        paths:
          - path: /socker.io
            backend: 
             serviceName: socket-service
             servicePort: 8000
          -  path: /
             backend:
              serviceName: centsideas-gateway
              servicePort: 3000
    - host: centsideas.com
      http:
        paths:
          - backend:
              serviceName: centsideas-client
              servicePort: 8080

Bedienung

apiVersion: v1
kind: Service
metadata:
 name: socket-service
 annotations:
  service.beta.kubernetes.io/external-traffic: "OnlyLocal" 
 namespace: namespace
spec:
 sessionAffinity: ClientIP
 ports:
  - name: ws-port
    protocol: TCP
    port: 8000
 type: ClusterIP
 selector:
  service: ws-api

Anschließend erstellen Sie Ihre Bereitstellung, um den ws-Dienst bereitzustellen. Auf diese Weise können Sie auch k8s HPA (horizontale Pod-Autoskalierung) aktivieren, um den Dienst socket.io zu skalieren. Sie müssen Anmerkungen und andere Optionen basierend auf Ihrer k8s-Version ändern (ich denke, die Anmerkung service.beta.kubernetes.io/external-traffic: "OnlyLocal"ist veraltet).

Giorgio Cerruti
quelle
OK gut! Nur zur Klarstellung: Für jeden internen Dienst, der Web-Sockets benötigt, würde ich einen weiteren socket-servicehinzufügen und ihn dem Ingress hinzufügen?
Florian Ludewig
Es hängt davon ab, ob. Wenn Sie neue Dienste (neue Codebasis) verfügbar machen müssen, müssen Sie für jeden Mikrodienst neue k8s-Dienste erstellen. Wenn Sie stattdessen Ihre socket.io-Anwendung skalieren müssen (erstellen Sie mehr Instanzen derselben Codebasis), tun Sie dies nicht. Ein Dienst ist im Grunde ein LoadBalancer, der von k8s mithilfe von iptables erstellt wurde. Er gleicht Ihre Anforderung zwischen Instanzen aus, die selbst entscheiden, welcher Pod nicht mit Anforderungen / Ressourcen beschäftigt ist.
Giorgio Cerruti
Sinnvoll, es kommt auf den Anwendungsfall an :) Also werde ich ein paar Tage warten, nur für den Fall, dass ich andere Antworten bekomme. Wenn nicht, bekommst du das Kopfgeld, danke!
Florian Ludewig
0

Da der interne Dienst nicht nach außen gerichtet ist, empfehle ich die Verwendung eines Tunnels. ngrok ist ein Befehl für eine sofortige und sichere URL zu Ihrem localhost-Server über ein beliebiges NAT oder eine Firewall. Wenn Ihr Server den Socket-Dienst über einen bestimmten Port verfügbar macht, verwenden Sie verfügbar macht , erstellen Sie ngrok einen Reverse-Proxy, um die Welt verfügbar zu machen, mit der Sie eine Verbindung zu Ihrer Frontend-Anwendung herstellen können. Die Verwendung dieses Befehls ist sehr einfach. Hier ist ein Beispiel für die Verwendung:

  1. Registrieren Sie sich und laden Sie die ngrok-Datei unter der folgenden Adresse herunter Offizielle Website
  2. Führen Sie einfach die folgende Anweisung aus, damit es funktioniert

    ./ngrok http 3000

  3. Um es dauerhaft zu machen, müssen Sie einen Dienst erstellen und eine Datei ngrok.yml für die beste Konfiguration verwenden.

Hier ist die offizielle Dokumentation hier

Godie007
quelle
Ich war in meiner Antwort wahrscheinlich nicht präzise genug. Ihr Ansatz mag funktionieren, aber der interne Service muss intern bleiben. Niemand von außen sollte in der Lage sein, mit einer direkten Verbindung zu sprechen
Florian Ludewig