Tic Tac Toe: Content Cycle
Let's try to connect Celery / RabbitMQ to our project. As a basis, take a project with Flask . Celery will do the random number calculation.
We clone the project on your computer:
git clone https://github.com/nomhoi/tic-tac-toe-part6.git
Launch containers:
cd tic-tac-toe-part6 docker-compose up -d
We build the web application:
cd front npm install npm run-script build
Open the browser at http: // localhost .
The nginx service has remained unchanged. In the flask service, we added the Celery package installation to the requirements.txt file and mounted the folder with the Celery project source:
volumes: - ./flask:/code - ./celery/app/proj:/code/proj
New celery and rabbit services have been added .
celery: container_name: celery build: context: celery/ dockerfile: Dockerfile volumes: - ./celery/app:/app depends_on: - rabbit networks: - backend rabbit: container_name: rabbit hostname: rabbit image: rabbitmq:3.7.15-alpine environment: - RABBITMQ_DEFAULT_USER=admin - RABBITMQ_DEFAULT_PASS=CT2gNABH8eJ9yVh ports: - "5672:5672" networks: - backend
Celery service is based on this tutorial . Those who are not familiar with Celery, it makes sense to go through this tutorial here:
$ docker exec -it celery python Python 3.7.3 (default, May 11 2019, 02:00:41) [GCC 8.3.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> from proj.tasks import add >>> add.delay(2, 2) <AsyncResult: 43662174-657f-4dd3-ab1a-22f5950c8794> >>>
As you can see, our Celery project is designed as a proj package. In the Celery tasks, our getnumber task has been added to the tasks.py file :
@app.task def getnumber(): return randrange(9)
Let me remind you that we added the Celery package to this service and mounted the folder with the proj project. The source code for this project is now present in two services, flask and celery .
from flask import Flask, jsonify from proj.tasks import getnumber from proj.celery import app as celery app = Flask(__name__) @app.route('/number') def number(): task = getnumber.delay() return task.id @app.route('/result/<task_id>') def result(task_id): task = getnumber.AsyncResult(task_id) result = task.get(timeout = 3) response = { 'state': task.state, 'number': result, } return jsonify(response)
In the number handler, we call the getnumber task, which runs in the celery worker, and returns the task identifier. In the result handler, we get the result of the completed task by identifier and return the response in front end JSON format.
In the manager of our game, by clicking the Get random number button, we first send a request to the backend at the address / number and get the Celery task identifier from it. After that, in the getResult function, we periodically send a request to the backend to receive the result at the address / result / <task_id> .
async function getResult(task_id) { var i = 1; var timerId = setTimeout(async function go() { console.log("Result request: " + i); console.log("Task Id: " + task_id) const res = await fetch(`result/` + task_id); const response = await res.text(); if (res.ok) { let result = JSON.parse(response); console.log(result) if (result.state === 'SUCCESS') { let i = parseInt(result.number); if ($status === 1 || $history.state.squares[i]) { promise_number = result.number + ' - busy'; return; } promise_number = i; history.push(new Command($history.state, i)); return; } } if (i < 5) setTimeout(go, 500); i++; }, 500); }
Changed the output of the backend query results:
{#await promise} <p>...</p> {:then taskid} <p>Task Id: {taskid}</p> {:catch error} <p style="color: red">{error.message}</p> {/await} {#await promise_number} <p>...</p> {:then number} <p>Number: {number}</p> {:catch error} <p style="color: red">{error.message}</p> {/await}
In fact, now the result comes immediately after the first request. Try our intelligent agent living in celery to make a little thoughtful, so as not to immediately give an answer.
From time to time, an error starts from flask 'a "500 (INTERNAL SERVER ERROR)", this raises the exception "celery.exceptions.TimeoutError: The operation timed out." In celery . Only rebooting services helps. While not digging what's the matter, please take a look.
GetResult processes the response only with the SUCCESS state; in other cases, a second request is made. You can add response handling with FAILURE and PENDING. The formation of the response in the result handler may also depend on the state of the task.
Instead of the RabbitMQ message broker, you can try connecting Redis.
You can try to execute requests from the application through message brokers.
And also try to do it based on the Boost.Beast example.
Source: https://habr.com/ru/post/461531/
All Articles