Skip to content

Commit d72a261

Browse files
committed
INIT
0 parents  commit d72a261

9 files changed

+292
-0
lines changed

.env

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
DATABASE_URL=postgresql+psycopg2://postgres:password@db:5432/post_db
2+
DB_USER=postgres
3+
DB_PASSWORD=password
4+
DB_NAME=post_db
5+
PGADMIN_EMAIL=[email protected]
6+
PGADMIN_PASSWORD=admin
7+
SECRET_KEY=89023f0j2039jf90jkakmciumcses9mcs99mcs93c48ym72tmarf737mc7h47wna4c8791289h8f1h3f8h376fh8bf4
8+
CELERY_BROKER_URL=redis://redis:6379/0
9+
CELERY_RESULT_BACKEND=redis://redis:6379/0

Dockerfile

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
FROM python:3.9.6-slim-buster
2+
ENV PYTHONUNBUFFERED=1
3+
WORKDIR /app
4+
COPY requirements.txt requirements.txt
5+
RUN pip3 install --upgrade pip
6+
RUN pip3 install -r requirements.txt
7+
COPY . /app
8+
EXPOSE 8000

README.md

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
https://www.youtube.com/watch?v=mcX_4EvYka4
2+
https://github.com/veryacademy/YT_FastAPI_Celery_Redis_Flower_Introduction
3+
4+
docker-compose build
5+
docker-compose up
6+
7+
conda env list
8+
conda create --name taskqueue-http python=3.9
9+
conda activate taskqueue-http
10+
conda env remove --name taskqueue-http
11+
pip install -r requirements.txt
12+
13+
pip3 install fastapi
14+
pip3 install uvicorn
15+
pip3 install orjson
16+
pip3 freeze > requirements.txt
17+
python3 ./app.py
18+
uvicorn main:app --host 0.0.0.0 --port 8001
19+
20+
curl -X POST http://localhost:8000/ -H "Content-Type: application/json" --data '{"name":"test", "url": "https://bahay.ph", "http_method": "GET"}'
21+
22+
curl -X POST http://localhost:8000/ -H "Content-Type: application/json" --data '{"name":"test", "url": "https://bahay.ph", "http_method": "GET"}'
23+
24+
curl -X POST http://localhost:8000/ -H "Content-Type: application/json" --data '{"name":"test", "url": "https://api.neuracash.com/transactions", "http_method": "GET"}'
25+
26+
1.12 KB
Binary file not shown.

__pycache__/main.cpython-39.pyc

1.71 KB
Binary file not shown.

celery_worker.py

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import os
2+
import time
3+
import requests
4+
from requests.exceptions import RequestException
5+
import json
6+
7+
8+
from celery import Celery
9+
from dotenv import load_dotenv
10+
11+
load_dotenv(".env")
12+
13+
celery = Celery(__name__)
14+
celery.conf.broker_url = os.environ.get("CELERY_BROKER_URL")
15+
celery.conf.result_backend = os.environ.get("CELERY_RESULT_BACKEND")
16+
17+
@celery.task(name="create_task", bind=True, autoretry_for=(RequestException,), retry_backoff=True)
18+
def create_task(self, url, http_method, body, headers):
19+
# try:
20+
if http_method == "GET":
21+
response = requests.get(url, headers=headers)
22+
elif http_method == "POST":
23+
response = requests.post(url, data=body, headers=headers)
24+
elif http_method == "PUT":
25+
response = requests.put(url, data=body, headers=headers)
26+
elif http_method == "PATCH":
27+
response = requests.patch(url, data=body, headers=headers)
28+
elif http_method == "DELETE":
29+
response = requests.delete(url, headers=headers)
30+
else:
31+
raise RequestException("HTTP Method not supported")
32+
33+
# GET response http code
34+
# response_headers = {}
35+
# for key, value in response.headers.items():
36+
# response_headers[key] = value
37+
38+
if not response.ok:
39+
raise RequestException(f'{url} returned unexpected response code: {response.status_code}')
40+
41+
return {
42+
"url": url,
43+
"http_method": http_method,
44+
"headers": headers,
45+
"body": body,
46+
"response_code": response.status_code,
47+
# "response_headers": response_headers,
48+
"response_body": response.text
49+
}
50+
# return response.json()
51+
# return str(response)
52+
53+
# except Exception as e:
54+
# self.retry(countdown=5, exc=e)
55+
# raise Exception(str(e))

docker-compose.yml

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
version: "3.8"
2+
3+
services:
4+
5+
app:
6+
container_name: app
7+
build: .
8+
command: bash -c "uvicorn main:app --host 0.0.0.0 --port 8000 --reload"
9+
volumes:
10+
- .:/app
11+
ports:
12+
- 8000:8000
13+
restart: always
14+
15+
redis:
16+
container_name: redis
17+
image: redis:6.2-alpine
18+
19+
celery_worker_01:
20+
container_name: celery_worker_01
21+
build: .
22+
command: celery -A celery_worker.celery worker --loglevel=info
23+
volumes:
24+
- .:/app
25+
environment:
26+
- CELERY_BROKER_URL=${CELERY_BROKER_URL}
27+
- CELERY_RESULT_BACKEND=${CELERY_RESULT_BACKEND}
28+
depends_on:
29+
- app
30+
- redis
31+
32+
# celery_worker_02:
33+
# container_name: celery_worker_02
34+
# build: .
35+
# command: celery -A celery_worker.celery worker --loglevel=info
36+
# volumes:
37+
# - .:/app
38+
# environment:
39+
# - CELERY_BROKER_URL=${CELERY_BROKER_URL}
40+
# - CELERY_RESULT_BACKEND=${CELERY_RESULT_BACKEND}
41+
# depends_on:
42+
# - app
43+
# - redis
44+
45+
# celery_worker_03:
46+
# container_name: celery_worker_03
47+
# build: .
48+
# command: celery -A celery_worker.celery worker --loglevel=info
49+
# volumes:
50+
# - .:/app
51+
# environment:
52+
# - CELERY_BROKER_URL=${CELERY_BROKER_URL}
53+
# - CELERY_RESULT_BACKEND=${CELERY_RESULT_BACKEND}
54+
# depends_on:
55+
# - app
56+
# - redis
57+
58+
flower:
59+
container_name: flower
60+
build: .
61+
command: celery -A celery_worker.celery flower --port=5555
62+
ports:
63+
- 5556:5555
64+
environment:
65+
- CELERY_BROKER_URL=${CELERY_BROKER_URL}
66+
- CELERY_RESULT_BACKEND=${CELERY_RESULT_BACKEND}
67+
depends_on:
68+
- app
69+
- redis
70+
- celery_worker_01
71+
# - celery_worker_02
72+
# - celery_worker_03
73+
74+
redisinsight:
75+
container_name: redisinsight
76+
image: redislabs/redisinsight:latest
77+
ports:
78+
- 8001:8001

main.py

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
from fastapi import Body, FastAPI
2+
from fastapi.responses import JSONResponse
3+
import uvicorn
4+
import json
5+
6+
from celery_worker import create_task
7+
8+
9+
app = FastAPI()
10+
11+
@app.get("/")
12+
def index():
13+
return JSONResponse({"message": "Hello World"})
14+
15+
@app.post("/")
16+
def index_post(data=Body(...)):
17+
http_method = data.get("http_method", "GET")
18+
url = data.get("url", "")
19+
body = data.get("body", "{}")
20+
headers = data.get("headers", '{"Content-Type": "application/json"}')
21+
expires = data.get("expires", 86400)
22+
name = data.get("name", "celery_worker_queue")
23+
24+
if len(url) == 0:
25+
return JSONResponse({"message": "URL is required"}, status_code=500)
26+
if not url.startswith("http://") and not url.startswith("https://"):
27+
return JSONResponse({"message": "URL must start with http:// or https://"}, status_code=500)
28+
29+
if http_method not in ["GET", "POST", "PUT", "PATCH", "DELETE"]:
30+
return JSONResponse({"message": "HTTP Method must be GET, POST, PUT, PATCH, or DELETE"}, status_code=500)
31+
32+
if len(body) > 0:
33+
try:
34+
json.loads(body)
35+
except:
36+
return JSONResponse({"message": "Body must be in JSON format"}, status_code=500)
37+
38+
if len(headers) > 0:
39+
try:
40+
headers = json.loads(headers)
41+
except:
42+
return JSONResponse({"message": "Headers must be in JSON format"}, status_code=500)
43+
44+
try:
45+
expires = int(expires)
46+
except:
47+
return JSONResponse({"message": "Dispatch deadline must be an integer"}, status_code=500)
48+
if expires < 0:
49+
return JSONResponse({"message": "Dispatch deadline must be a positive integer"}, status_code=500)
50+
51+
try:
52+
# create task
53+
task = create_task.apply_async(
54+
shadow_name=name,
55+
args=(url, http_method, body, headers),
56+
# max_retries=5,
57+
# expires=expires
58+
)
59+
# task = create_task.delay(url, http_method, body, headers)
60+
except Exception as e:
61+
return JSONResponse({"message": str(e)}, status_code=500)
62+
63+
return JSONResponse(
64+
{"message": "Task created successfully", "task_id": task.id}
65+
)
66+
67+
# task = create_task.delay(http_method, url, body, headers, expires)
68+
# # return JSONResponse({"Result": task.get()})
69+
# return JSONResponse({"Result": task.ready()})
70+
71+
# if __name__ == "__main__":
72+
# uvicorn.run(app)

requirements.txt

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
amqp==5.0.6
2+
anyio==3.4.0
3+
asgiref==3.4.1
4+
attrs==20.3.0
5+
billiard==3.6.4.0
6+
celery==5.2.1
7+
certifi==2020.12.5
8+
chardet==4.0.0
9+
click==8.0.3
10+
click-didyoumean==0.3.0
11+
click-plugins==1.1.1
12+
click-repl==0.2.0
13+
Deprecated==1.2.13
14+
fastapi==0.70.0
15+
flower==1.0.0
16+
h11==0.12.0
17+
humanize==3.12.0
18+
idna==2.10
19+
JsonForm==0.0.2
20+
jsonschema==3.2.0
21+
JsonSir==0.0.2
22+
kombu==5.2.2
23+
orjson==3.6.4
24+
prometheus-client==0.12.0
25+
prompt-toolkit==3.0.23
26+
pydantic==1.8.2
27+
pyrsistent==0.17.3
28+
python-dotenv==0.19.2
29+
Python-EasyConfig==0.1.7
30+
pytz==2021.3
31+
PyYAML==5.4.1
32+
redis==4.0.2
33+
requests==2.25.1
34+
Resource==0.2.1
35+
six==1.15.0
36+
sniffio==1.2.0
37+
starlette==0.16.0
38+
tornado==6.1
39+
typing_extensions==4.0.0
40+
urllib3==1.26.4
41+
uvicorn==0.15.0
42+
vine==5.0.0
43+
wcwidth==0.2.5
44+
wrapt==1.13.3

0 commit comments

Comments
 (0)