¿Comunicación SocketIO con una puerta de enlace entre el cliente y un servicio?

8

Esencia

Tengo una aplicación que se ejecuta en una arquitectura basada en microservicios (en Kubernetes). Toda la comunicación hacia / desde fuera de la aplicación ocurre a través de un API Gateway .

Lo que significa que las solicitudes de mi interfaz no van directamente a los servicios, sino que tienen que pasar por el Gateway.

Motivo

Ahora necesito implementar una función que requiera comunicación en tiempo real entre la interfaz y un servicio interno. Pero como el servicio interno no está expuesto al exterior, necesito una forma de "enrutar" los datos en tiempo real a través de la puerta de enlace.

Todos mis servicios se ejecutan en Node.js, por lo que quiero usar Socket.IO para implementar la comunicación en tiempo real.

arquitectura

Problema

Pero, ¿cómo implementar la flecha doble púrpura del boceto?

Por lo general, el cliente frontend se conectaría al servidor donde se ejecuta Socket.IO. Pero en mi caso, este servidor (el servidor de funciones en tiempo real) no es accesible desde el cliente (y nunca debería serlo), lo que significa que el cliente tiene que conectarse a la puerta de enlace. Por lo tanto, el Gateway necesita implementar algún mecanismo para enrutar todos los mensajes entrantes al servicio en tiempo real y viceversa.

Ideas

(1) Haga que un segundo servidor HTTP escuche eventos en el Gateway y emita esos eventos al servidor en tiempo real. En la otra dirección, el servidor en tiempo real emitirá eventos a la puerta de enlace, que luego los emitirá a la interfaz. Creo que este enfoque definitivamente funcionará, pero parece redundante emitir todo dos veces. ¿Y definitivamente dañaría el rendimiento?

(2) Use un adaptador Socket.IO para " pasar el evento entre nodos ", lo que parece ser el camino correcto porque se usa para "pasar mensajes entre procesos o computadoras". Pero tengo problemas para comenzar debido a la falta de documentación / ejemplos. Tampoco estoy usando Redis (¿es necesario usar el adaptador?)

(3) Use el paquete socket.io-emitter , que no parece ser una buena opción ya que la última confirmación fue de hace 3 años.

(4) ¿Algo más?

Florian Ludewig
fuente
Entonces, ¿debe usar API Gateway para redirigir el tráfico? Hemos implementado socket.io en la aplicación pour, pero el servicio se ha implementado en un servicio separado (pod). El uso de ingress-controllerk8s envía el sockettráfico al servicio de fútbol.
Giorgio Cerruti
@GiorgioCerruti No, no tengo que usar Gateway, pero así es como planifiqué mi aplicación para que funcione. Entonces, ¿recomendaría simplemente exponer mi "servicio interno" a través de Ingress?
Florian Ludewig
Lo hice usando nginxcomo controlador de ingreso. También puede usar haproxy si lo prefiere. Tendrá 2 servicios (implementación) 1 para administrar las solicitudes HTTP (s) y 1 para administrar las solicitudes de socket. Con el ingreso, puede exponer la ruta y redirigir el tráfico al servicio adecuado, por ejemplo, cada solicitud que /socketse redirija a la socket-serviceaplicación.
Giorgio Cerruti
ok, eso suena como algo que intenta probar, porque ya uso nginx Ingress. Sería increíble si pudiera publicar una respuesta que muestre cómo implementarla (Aquí está la configuración de ingreso que uso actualmente: github.com/flolu/centsideas/blob/… )
Florian Ludewig

Respuestas:

3

Bien, básicamente diseñé la aplicación así

Ingreso

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

Servicio

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

Luego crea su implementación para implementar el servicio ws. De esta manera, también puede activar k8s HPA (escalado automático de pod horizontal) para ampliar el servicio socket.io. Debe cambiar las anotaciones y otras opciones en función de su versión de k8s (creo que la anotación service.beta.kubernetes.io/external-traffic: "OnlyLocal"ha quedado en desuso).

Giorgio Cerruti
fuente
¡Ok, perfecto! Solo para aclarar: ¿para cada servicio interno que requiere sockets web agregaría otro socket-servicey lo agregaría al Ingress?
Florian Ludewig
Depende. Si necesita exponer nuevos servicios (nueva base de código) sí, debe crear nuevos servicios k8s en cada microservicio. En cambio, si necesita ampliar su aplicación socket.io (crear más instancias de la misma base de código), no, no lo necesita. Un servicio es básicamente un LoadBalancer hecho por k8s usando iptables, equilibrará su solicitud entre instancias que deciden por sí mismas qué pod no está ocupado con solicitudes / recursos.
Giorgio Cerruti
Tiene sentido, depende del caso de uso :) Así que esperaré unos días en caso de que obtenga otras respuestas. Si no, obtendrás la recompensa, ¡gracias!
Florian Ludewig
0

Como el servicio interno no está expuesto al exterior, recomiendo usar un túnel. ngrok es un comando para una URL instantánea y segura a su servidor localhost a través de cualquier NAT o firewall. Si su servidor expone el servicio de socket a través de un determinado puerto, use ngrok para crear un proxy inverso para exponer el mundo con el que puede conectarse a su aplicación frontend. Usar este comando es muy simple, aquí hay un ejemplo de cómo usarlo:

  1. Regístrese y descargue el archivo ngrok en la siguiente dirección Sitio oficial
  2. Simplemente ejecute las siguientes instrucciones para que funcione

    ./ngrok http 3000

  3. Para hacerlo permanente, debe crear un servicio y usar un archivo ngrok.yml para la mejor configuración.

Aquí está la documentación oficial. Aquí

Godie007
fuente
Probablemente no fui lo suficientemente preciso en mi respuesta. Su enfoque podría funcionar, pero el servicio interno debe permanecer interno. Nadie del exterior debería poder hablar con él con una conexión directa
Florian Ludewig