Estoy haciendo una aplicación con matraz para la parte trasera y angular para la parte delantera, con una arquitectura de microservicios. Para la implementación, estoy usando docker, y ahora, el problema es comunicarse entre servicios. He leido que la mejor opcion es rabbit-mqtt pero no he encontrado ningun tutorial.
Tengo poco tiempo porque es para terminar la carrera, así que necesito un tutorial que me permita crear la comunicación entre servicios rápidamente.
El matraz está inquieto y uso el administrador para crear API-CRUD.
Gracias por adelantado
Así es como lo hice:
from flask import Flask import pika import uuid import threading app = Flask(__name__) queue = {} class FibonacciRpcClient(object): def __init__(self): self.connection = pika.BlockingConnection( pika.ConnectionParameters(host='rabbit')) self.channel = self.connection.channel() result = self.channel.queue_declare('', exclusive=True) self.callback_queue = result.method.queue self.channel.basic_consume( queue=self.callback_queue, on_message_callback=self.on_response, auto_ack=True) def on_response(self, ch, method, props, body): if self.corr_id == props.correlation_id: self.response = body def call(self, n): self.response = None self.corr_id = str(uuid.uuid4()) queue[self.corr_id] = None self.channel.basic_publish( exchange='', routing_key='rpc_queue', properties=pika.BasicProperties( reply_to=self.callback_queue, correlation_id=self.corr_id, ), body=str(n)) while self.response is None: self.connection.process_data_events() queue[self.corr_id] = self.response print(self.response) return int(self.response) @app.route("/calculate/<payload>") def calculate(payload): n = int(payload) fibonacci_rpc = FibonacciRpcClient() threading.Thread(target=fibonacci_rpc.call, args=(n,)).start() return "sent " + payload @app.route("/results") def send_results(): return str(queue.items()) import pika connection = pika.BlockingConnection( pika.ConnectionParameters(host='localhost')) channel = connection.channel() channel.queue_declare(queue='rpc_queue') def fib(n): if n == 0: return 0 elif n == 1: return 1 else: return fib(n - 1) + fib(n - 2) def on_request(ch, method, props, body): n = int(body) print(" [.] fib(%s)" % n) response = fib(n) print(" [.] calculated (%s)" % response) ch.basic_publish(exchange='', routing_key=props.reply_to, properties=pika.BasicProperties(correlation_id=props.correlation_id), body=str(response)) ch.basic_ack(delivery_tag=method.delivery_tag) channel.basic_qos(prefetch_count=1) channel.basic_consume(queue='rpc_queue', on_message_callback=on_request) print(" [x] Awaiting RPC requests") channel.start_consuming()Los 2 anteriores se basan en el tutorial de RabbitMQ en RPC.
FROM python:3 RUN mkdir code ADD flask_server.py requirements.txt /code/ WORKDIR /code RUN pip install -r requirements.txt ENV FLASK_APP flask_server.py EXPOSE 5000 CMD ["flask", "run", "-h", "0.0.0.0"] services: web: build: . ports: - "5000:5000" links: rabbit volumes: - .:/code rabbit: hostname: rabbit image: rabbitmq:latest ports: - "5672:5672"Ejecute docker-compose up y el servidor Flask debería comenzar a comunicarse con el servidor RabbitMQ.
Hay muchas formas de escribir el servidor RabbitMQ, el trabajador y el Dockerfile.
La primera respuesta muestra buenos ejemplos de ellos.
Solo enfatizaré que el servidor RabbitMQ podría no estar listo cuando el trabajador (el servicio web en su caso) intente acceder a él .
Para eso , sugeriré escribir el archivo docker-compose.yml de esta manera :
version: "3" services: web: build: . ports: - "5000:5000" restart: on-failure depends_on: - rabbitmq volumes: - .:/code rabbit: image: rabbitmq:latest expose: - 5672 healthcheck: test: [ "CMD", "nc", "-z", "localhost", "5672" ] interval: 3s timeout: 10s retries: 3Entonces, ¿qué hice aquí?
1) Agregué las propiedades depend_on y restart en el servicio web y la propiedad healthcheck en el servicio de conejo .
Ahora el servicio web se reiniciará hasta que el servicio de conejo se vuelva saludable.
2) En el servicio de conejo , utilicé la propiedad de expose en lugar de los ports porque, en su caso, el puerto 5672 debe compartirse entre los contenedores y no necesita estar expuesto al host.
De los documentos de Expose :
Exponga los puertos sin publicarlos en la máquina host; solo podrán acceder a ellos los servicios vinculados. Solo se puede especificar el puerto interno.
3) Eliminé la propiedad de links porque (tomado de aquí ):
No se requieren enlaces para permitir que los servicios se comuniquen; de forma predeterminada, cualquier servicio puede llegar a cualquier otro servicio en el nombre de ese servicio.
https://youtu.be/ZxVpsClqjdw este video explica todo con código
y tiene un docker-compose como
version: '3' services: redis: image: redis:latest hostname: redis rabbit: hostname: rabbit image: rabbitmq:latest environment: - RABBITMQ_DEFAULT_USER=admin - RABBITMQ_DEFAULT_PASS=mypass web: build: context: . dockerfile: Dockerfile hostname: web command: ./scripts/run_web.sh volumes: - .:/app ports: - "5000:5000" links: - rabbit - redis worker: build: context: . dockerfile: Dockerfile command: ./scripts/run_celery.sh volumes: - .:/app links: - rabbit - redis depends_on: - rabbity para hacer el uso de la conexión
BROKER_URL = 'amqp://admin:mypass@rabbit//' CELERY = Celery('tasks',backend=REDIS_URL,broker=BROKER_URL)para una explicación más detallada https://medium.com/swlh/dockerized-flask-celery-rabbitmq-redis-application-f317825a03b