728x90

소스코드

import time
import asyncio
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    return {"message":"Hello World"}

# FastAPI는 async를 사용하지 않아도 비동기로 작업을 수행
# https://tigris-data-science.tistory.com/entry/FastAPI%EC%9D%98-%EB%8F%99%EA%B8%B0-%ED%95%A8%EC%88%98-%EB%8F%99%EC%9E%91-%EB%B0%A9%EC%8B%9D
@app.get("/sleep") # API처리중에 다른API 처리가능
def time_sleep():
    time.sleep(5)
    return 5

# async를 사용하면 메인쓰레드에서 동작하여 cpu-bound 작업 수행 시 다른 쓰레드 Blocking됨
@app.get("/async-sleep") # API처리 끝날때까지 다른API block
async def async_sleep():
    time.sleep(5)
    return 5

# async를 사용할 땐 메모리 bound작업을 해야 다른 쓰레드가 Blocking되지 않음
@app.get("/async-asyncio-sleep") # API처리중에 다른API 처리가능
async def asyncio_sleep():
    await asyncio.sleep(5)
    return 5

참고 : 개발환경 라이브러리 버전

annotated-types==0.6.0
anyio==4.3.0
click==8.1.7
colorama==0.4.6
fastapi==0.110.0
h11==0.14.0
idna==3.6
pydantic==2.6.2
pydantic_core==2.16.3
sniffio==1.3.1
starlette==0.36.3
typing_extensions==4.10.0
uvicorn==0.27.1

 

728x90
728x90

소스코드

from fastapi import FastAPI
import time
import asyncio
from datetime import datetime

app = FastAPI()

sem = asyncio.Semaphore(3) # 최대 동시처리 가능 개수 제한

@app.get("/")
def root():
    print("/", datetime.now())
    return {"date_time": datetime.now()}

@app.get("/time-sleep")
def time_sleep():
    start_time = datetime.now()
    results = time.sleep(6)
    end_time = datetime.now()
    return {"start_time":start_time, "end_time":end_time, "results":results}


@app.get("/async")
async def async_root():
    print("/async", datetime.now())
    return {"date_time": datetime.now()}

async def _async_sleep(num, sec): # time sleep 은 I/O bound 작업을 테스트할 때 사용
    async with sem:
        print("start sleep", datetime.now())
        await asyncio.sleep(sec)
        print("end sleep", datetime.now())
        return f"hehe {num}"

async def task_in_parallel():
    # 여러 개의 비동기 작업을 동시에 실행
    results = await asyncio.gather( 
        asyncio.create_task(_async_sleep(1,6)),
        asyncio.create_task(_async_sleep(2,6)),
        asyncio.create_task(_async_sleep(3,6)),
        asyncio.create_task(_async_sleep(4,6)),
        asyncio.create_task(_async_sleep(5,6)),
    )
    return results
    
@app.get("/async-asyncio-sleep")
async def parallel_sleep():
    start_time = datetime.now()
    results = await task_in_parallel()
    end_time = datetime.now()
    return {"start_time":start_time, "end_time":end_time, "results":results}

@app.get("/async-sleep")
async def async_sleep():
    start_time = datetime.now()
    results = await _async_sleep(1,6)
    end_time = datetime.now()
    return {"start_time":start_time, "end_time":end_time, "results":results}


@app.get("/asyncio-sleep")
async def asyncio_sleep():
    start_time = datetime.now()
    results = await asyncio.sleep(6)
    end_time = datetime.now()
    return {"start_time":start_time, "end_time":end_time, "results":results}

 

  • 앱 띄우는 방법 : uvicorn main:app --port 8000
  • 브라우저에서 스웨거 접속하는 방법 : http://localhost:8000/docs
  • 동시처리 API 응답 : async-asyncio-sleep API 호출 시 6초짜리 time sleep 함수 5개를 3개 / 2개 비동기처리하여 약 12초만에 응답 받음
{
  "start_time": "2024-02-26T22:36:15.098960",
  "end_time": "2024-02-26T22:36:27.101120",
  "results": [
    "hehe 1",
    "hehe 2",
    "hehe 3",
    "hehe 4",
    "hehe 5"
  ]
}

 

  • 비동기처리 앱 로그 : async-asyncio-sleep API 처리 중 다른 API 요청 처리됨
INFO:     Application startup complete.
start sleep 2024-02-26 22:46:12.735016
start sleep 2024-02-26 22:46:12.735016
start sleep 2024-02-26 22:46:12.736013
/async 2024-02-26 22:46:14.222185
INFO:     127.0.0.1:55682 - "GET /async HTTP/1.1" 200 OK
/ 2024-02-26 22:46:16.353565
INFO:     127.0.0.1:55682 - "GET / HTTP/1.1" 200 OK
end sleep 2024-02-26 22:46:18.739985
end sleep 2024-02-26 22:46:18.740557
end sleep 2024-02-26 22:46:18.741149
start sleep 2024-02-26 22:46:18.741675
start sleep 2024-02-26 22:46:18.741675
/async 2024-02-26 22:46:20.443945
INFO:     127.0.0.1:55682 - "GET /async HTTP/1.1" 200 OK
/ 2024-02-26 22:46:23.335636
INFO:     127.0.0.1:55682 - "GET / HTTP/1.1" 200 OK
end sleep 2024-02-26 22:46:24.738253
end sleep 2024-02-26 22:46:24.738253
INFO:     127.0.0.1:55681 - "GET /async-asyncio-sleep HTTP/1.1" 200 OK

 

참고 : 개발환경 라이브러리 버전

annotated-types==0.6.0
anyio==4.3.0
click==8.1.7
colorama==0.4.6
fastapi==0.110.0
h11==0.14.0
idna==3.6
pydantic==2.6.2
pydantic_core==2.16.3
sniffio==1.3.1
starlette==0.36.3
typing_extensions==4.10.0
uvicorn==0.27.1
728x90

'Python > Web' 카테고리의 다른 글

FastAPI 개발자가 직접 개발한 FastAPI backend 프로젝트 구조  (0) 2024.03.03
WSGI와 ASGI 단순 비교  (0) 2024.02.28
FastAPI 비동기 처리  (1) 2024.02.26
[windows] nginx 프록시 설정 방법  (0) 2022.11.01
nginx 기초  (0) 2022.10.25
728x90

프록시 설정 이유

하나의 서비스를 이루는 서버가 여러 개 있을 때,

사용자는 각 서버가 어떤 역할을 하는지 파악하고 원하는 서버에 요청을 해야할까? 

ex)

http://localhost:5000/service1/~~

http://localhost:5050/service2/~~

 

중간에 프록시 서버를 두면 프록시서버가 사용자의 요청을 분기처리하여 서비스를 호출한다

사용자는 프록시서버만 알면 된다

ex)

http://localhost:80/service1/~~

http://localhost:80/service2/~~

 

 

윈도우 nginx 설치

 

설정파일 설정

※ 사전준비 : 기동되고 있는 서버의 url

 

1. {nginx설치경로}/conf/sites-enabled/customizing.conf 파일 생성

upstream spring-frontend {
    server localhost:8080;
}
upstream flask-backend {
    server localhost:5000;
}
upstream fastapi-backend {
    server localhost:5050;
}

server {
    listen 80;
    proxy_max_temp_file_size 0;
    client_max_body_size 256M;

    location / {
        proxy_pass http://spring-frontend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header REMOTE-HOST $remote_addr;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
    }
    location /service1/ {
        proxy_pass http://flask-backend;
    }
    location ~^/service2/ {
        proxy_pass http://fastapi-backend;
    }
    location ~^/(docs|openapi.json)$ {
        proxy_pass http://fastapi-backend;
    }
}
  • upstream > 연결할 서버 url 정의
    • 예시 :
      upstream spring-frontend {server localhost:8080;}
  • server > location에 url mapping
    • 예시 : localhost/service1/ 로 들어오는 요청은 flask-backend(http://localhost:5000/service1)로 연결
      location /service1/ { proxy_pass http://flask-backend; }

2. {nginx설치경로}/conf/nginx.conf 파일에 위에서 만든 conf 파일 불러오도록 설정

worker_processes  1;

events {
    worker_connections  1024;
}

http {
    include mime.types;
    include sites-enabled/*.conf;
}

3. nginx 재기동

nginx -s stop # 멈추기
nginx # 시작

 

접속 확인

위의 설정파일에서 localhost/docslocalhost:5050/docs 연결했음 > 브라우저에서 localhost/docs에 접속하여 localhost:5050/docs 를 접속했을 때와 같은 서버가 뜬다면 성공

 

 

 

 

nginx 기초

nginx > 웹서버 정적 파일을 처리하는 HTTP 서버로서의 역할 HTML, CSS, Javascript, 이미지와 같은 정보들을 웹브라우저에 전송하는 역할 응용프로그램 서버에 요청을 보내는 리버스 프록시로서의 역할

bigseok.tistory.com

728x90

'Python > Web' 카테고리의 다른 글

FastAPI 개발자가 직접 개발한 FastAPI backend 프로젝트 구조  (0) 2024.03.03
WSGI와 ASGI 단순 비교  (0) 2024.02.28
FastAPI 비동기 처리  (1) 2024.02.26
FastAPI async 비동기 동시 처리  (1) 2024.02.26
nginx 기초  (0) 2022.10.25
728x90

nginx > 웹서버

  • 정적 파일을 처리하는 HTTP 서버로서의 역할
    • HTML, CSS, Javascript, 이미지와 같은 정보들을 웹브라우저에 전송하는 역할
  • 응용프로그램 서버에 요청을 보내는 리버스 프록시로서의 역할
    • 클라이언트가 가짜 서버에 요청하면, 프록시 서버가 배후서버(reverse server)로부터 데이터를 가져오는 역할을 한다.
    • 프록시 서버가 Nginx, 리버스 서버가 응용프로그램 서버를 의미
    • 웹 응용프로그램 서버에 리버스 프록시(Nginx)를 두는 이유는 요청에 대한 버퍼링 때문
    • 클라이언트가 직접 App 서버에 요청하는 경우 프로세스 1개가 응답 대기상태가 되어야 하기 때문에, 프록시 서버를 둠으로서 요청을 배분하는 역할을 함
      • nginx.conf 파일에서 location 지시어를 사용하여 요청을 배분
      • Nginx는 비동기 처리 방식(Event-Drive) 방식을 채택

설치

  • 리눅스 설치
su - # 관리자모드 
apt-get update # apt-get 업데이트 
sudo apt-get install nginx # nginx 설치 
nginx -v # nginx 버전
  • 윈도우 설치

nginx 기본 명령어

## nginx.exe가 존재하는 위치에서 아래 명령어 사용 ##

# 버전
nginx -v

# 시작 : nginx 설치경로로 가서 nginx.exe 더블클릭
nginx (nginx 명령어로 시작 가능하지만 cmd 멈춤.. 끄고 다른 cmd창 열어야함)

# 기동 중인 nginx에 명령하기
## 설정파일 변경 반영을 위해 reload (내린 후 다시 로드해야 되는 경우도 있음)
nginx -s reload;  
## 멈추기
nginx -s stop

nginx 디렉토리 구조 (리눅스 버전)

sudo find / -name nginx.conf # 디렉토리 찾기
cd /etc/nginx # nginx 경로로 이동

sudo apt-get install tree # 트리명령어 사용을 위해 설치
tree . # 현재 디렉토리 트리구조로 보기

nginx.conf 기본 환경설정 튜닝하기

nginx.conf 파일은 Nginx가 동작해야 할 방식을 설정 값을 통해 지정

root 계정만 수정이 가능하기 때문에, 수정이 필요하다면 sudo vim nginx.conf 를 통해 파일 열기

user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
        worker_connections 768;
        # multi_accept on;
}

http {

        ##
        # Basic Settings
        ##

        sendfile on;
        tcp_nopush on;
        tcp_nodelay on;
        keepalive_timeout 65;
        types_hash_max_size 2048;
        # server_tokens off;

        # server_names_hash_bucket_size 64;
        # server_name_in_redirect off;

        include /etc/nginx/mime.types;
        default_type application/octet-stream;

        ##
        # SSL Settings
        ##

        ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
        ssl_prefer_server_ciphers on;

        ##
        # Logging Settings
        ##

        access_log /var/log/nginx/access.log;
        error_log /var/log/nginx/error.log;

        ##
        # Gzip Settings
        ##

        gzip on;

        # gzip_vary on;
        # gzip_proxied any;
        # gzip_comp_level 6;
        # gzip_buffers 16 8k;
        # gzip_http_version 1.1;
        # gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

        ##
        # Virtual Host Configs
        ##

        include /etc/nginx/conf.d/*.conf;
        include /etc/nginx/sites-enabled/*;
}


#mail {
#       # See sample authentication script at:
#       # http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript
#
#       # auth_http localhost/auth.php;
#       # pop3_capabilities "TOP" "USER";
#       # imap_capabilities "IMAP4rev1" "UIDPLUS";
#
#       server {
#               listen     localhost:110;
#               protocol   pop3;
#               proxy      on;
#       }
#
#       server {
#               listen     localhost:143;
#               protocol   imap;
#               proxy      on;
#       }
#}

최상단(Core 모듈)

user  seokhwa; # (default : www-data)
worker_processes  1;
error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;
  • user : NGINX 프로세스가 실행되는 권한
    • nginx는 마스터(master)와 워커(worker) 프로세스로 나뉜다.
    • 워커 프로세스가 실질적인 웹서버 역할을 수행한다.
    • user 지시어는 워커 프로세스의 권한을 지정한다.
    • 워커 프로세스를 악의적 사용자가 제어하면 해당 머신을 최고 사용자의 권한으로 원격제어하는 것이기 때문에 위험하다.
  • work_processes : NGINX 프로세스 실행 가능 수
    • 위에서 언급한 워커 프로세스이다. 실질적인 웹서버 역할을 한다.
    • auto도 무방하지만, 명시적으로 서버에 장착되어 있는 코어 수 만큼 할당하는 것이 보통이며, 더 높게도 설정 가능하다.
  • pid : NGINX 마스터 프로세스 ID 정보가 저장됨

events 블락

events { 
    worker_connections  1024;
    # multi_accept on; (디폴트값 : off) 
}
  • NGINX의 특징인 비동기 이벤트 처리 방식에 대한 옵션 설정
  • worker_connections는 하나의 프로세스가 처리할 수 있는 커넥션의 수
  • 최대 접속자수 = {worker_processes} X {worket_connections}

http 블락

http { 
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
 
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    '$status $body_bytes_sent "$http_referer" '
    '"$http_user_agent" "$http_x_forwarded_for"';
 
    access_log  /var/log/nginx/access.log  main;
 
    sendfile        on;
 
    #tcp_nopush     on; 
 
    keepalive_timeout  65;
 
    #gzip  on; 
 
    include /etc/nginx/conf.d/*.conf;
}
  • keepalive_timeout : 접속시 커넥션을 몇 초동안 유지할지에 대한 설정값
    • 이 값이 높으면 불필요한 커넥션(접속)을 유지하기 때문에 낮은 값 또는 0을 권장한다. (default=10)
  • servers token : NGINX의 버전을 숨길것인가에 대한 옵션
    • 보안상 주석을 제거하여 설정하는 것이 좋다.
  • types_hash_max_size, server_names_hash_bucket_size 호스트의 도메인 이름에 대한 공간 설정
    • 이 값이 낮을 경우 많은 가상 호스트 도메인을 등록한다거나, 도메인 이름이 길 경우 bucket 공간이 모자라 에러가 생길 수 있으므로 넉넉하게 설정하는 것이 좋다.

기타

  • include옵션 : 가상 호스트 설정이나 반복되는 옵션 항목을 inlcude를 통해 불러올 수 있다.
    • 리버스 프록시를 각 도메인에 설정한다고 했을 떄 헤더 처리 옵션등을 conf.d디렉토리에 넣어두고 incldue 명령을 통해 불러올 수 있다.
728x90

+ Recent posts