python asgi server ( uvicorn / starlette )
* ASGI (Asynchronous Server Gateway Interface)
그대로 읽으면 비동기 서버 게이트웨이 인터페이스 이다.
자세한 의미는 여기 을 참조한다. 대강 보면 웹 서버, 프레임워크, 응용프로그램 들의 호환성 표준 이라 생각 하면 될듯하다.
아래에서는 Async web server ( uvicorn / starlette ) 을 구성하고 동작시키는 예제를 확인 할것이다. (on Docker)
# 환경
## Host (Docker build)
NAME="Ubuntu" VERSION="18.04.1 LTS (Bionic Beaver)"
Docker version 18.06.1-ce, build e68fc7a
## Container OS /home/anywon # cat /etc/os-release NAME="Alpine Linux" ID=alpine VERSION_ID=3.8.1
## Lan /home/anywon # python3.6 -V Python 3.6.6
## Pip lib
starlette==0.9.9 uvicorn==0.3.23
Python 에서 async function 는 아래와 같이 def 지시어 앞에 async 가 붙는다.
async def test(): return 0
* sample_code 를 clone 후 script dir 에 있는 build.web_server.sh 를 실행하면 자동으로 docker images 가 생성이 된다.
Container OS 는 alpine 이며, Python 과 uvicorn, starlette 를 설치하여 구동이 된다.
# docker images REPOSITORY TAG IMAGE ID CREATED SIZE web_server 1.0 82e813dc9a6d 4 days ago 233MB
Docker images build 가 끝이나면 docker images command 를 통하여 images 를 확인할 수 있다. 그뒤
run_web_server_container.sh 를 실행하면 자동으로 Container 가 구동 되게 된다.
# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES fb5e0d810f66 web_server:1.0 "/usr/bin/supervisord" 30 minutes ago Up 30 minutes web_server # ps -aux | grep python root 13497 0.0 0.1 94976 24104 pts/0 S 17:28 0:00 /usr/bin/python3.6 /usr/bin/uvicorn --fd 0 app_server:app --log-level=info --http=h11 root 13498 0.0 0.1 94976 24024 pts/0 S 17:28 0:00 /usr/bin/python3.6 /usr/bin/uvicorn --fd 0 app_server:app --log-level=info --http=h11 root 13499 0.0 0.1 94976 24148 pts/0 S 17:28 0:00 /usr/bin/python3.6 /usr/bin/uvicorn --fd 0 app_server:app --log-level=info --http=h11 root 13500 0.0 0.1 94976 23984 pts/0 S 17:28 0:00 /usr/bin/python3.6 /usr/bin/uvicorn --fd 0 app_server:app --log-level=info --http=h11
위와 같이 STATUS 가 Up 상태이면 정상적으로 서버가 구동중이다. 또는 ps -aux | grep python 을 사용하여 확인이 가능하다.
* worker count 를 증가 하고 싶으면 아래와 같이 변경을 하면 된다.
files/conf 의 supervisord.conf 을 열고 [fcgi-program:uvicorn] directory=/home/anywon/web_server socket=tcp://0.0.0.0:18080 command=/usr/bin/uvicorn --fd 0 app_server:app --log-level=info --http=h11 numprocs=4 process_name=web_server-%(process_num)d stdout_logfile=/home/anywon/supervisor/web_server_stdout.log stdout_logfile_maxbytes=50MB ; max # logfile bytes b4 rotation (default 50MB) stdout_logfile_backups=10 ; # of stdout logfile backups (default 10) stderr_logfile=/home/anywon/supervisor/web_server_stderr.log stderr_logfile_maxbytes=50MB ; max # logfile bytes b4 rotation (default 50MB) stderr_logfile_backups=10 ; # of stdout logfile backups (default 10) fcgi-program:uvicorn sction 의 numprocs=4 값을 변경 한다. (기본값은 4로 설정)
* 실행 코드
# Ref (uvicorn) : https://www.uvicorn.org/ # Ref (starlette) : https://www.starlette.io/ from starlette.routing import Mount, Route, Router from starlette.applications import Starlette from starlette.responses import Response, JSONResponse from starlette.requests import Request async def ping(request): headers = {'Content-Type' : 'text/plain'} return Response('OK', status_code = 200, headers = headers) async def sample(request): # Get body body = await request.body() # Get uri_1 uri_1 = request.path_params['uri_1'] # Get uri_2 uri_2 = request.path_params['uri_2'] # Get full url with queryString full_url = request.url # Get request headers headers = request.headers # Get QueryString : dict qp = request.query_params headers = { 'Content-Type' : 'application/json', 'keep-alive' : 'timeout=30, max=100', 'connection' : 'Keep-Alive' } # Response # return Response(data, status_code = 200, headers = headers]) # Response JSON return JSONResponse(data_json, status_code = 200, headers = headers) API_VERSION = 'v1' app = Starlette() app.add_route('/', ping, methods=["GET"]) app.add_route('/', ping, methods=["PUT"]) app.add_route('/api/test' + API_VERSION + '/{uri_1}/{uri_2}', sample, methods=["GET"]) app.add_route('/api/test' + API_VERSION + '/{upload_path:path}', sample, methods=["PUT"])
실행이 되면 위의 app_server.py code 가 실행이 되며, add_route 를 통하여 원하는 uri 를 생성 및 method 를 지정할수 있다.
해당 route 의 request 가 발생되어 처리할 function 을 상단에 지정할수 있으며, 기본적으로 async def 로 시작하여 async code base 를 확인할수 있다.
기본적인 사용 방법은 위의 sample code 를 보면 확인이 가능하며, 추가적으로 궁금한 사항은 여기 에서 확인 가능 하다.