Django | Locust 부하테스트와 Redis 적용

Sua·2021년 5월 11일
5

Django

목록 보기
23/23
post-thumbnail

쿼리문 최적화를 통해 하나의 요청에 60초가 걸리던 것을 4초로 단축시켰다. 하지만 여전히 오래 걸린다.

부하테스트 툴인 locust를 사용해서 서버가 과연 몇 명의 접속자를 처리할 수 있을지 테스트해보려고 한다. 또한, redis를 적용해서 성능을 개선하고 테스트해보겠다.

부하테스트

loadtest

본격적으로 부하테스트를 하기 전 loadtest로 터미널에서 간단하게 테스트를 해볼 것이다.

아래 명령어로 설치한다.

npm install -g loadtest

더 자세한 설명한 설명은 이 사이트를 참고


10개의 요청을 처리하는데 걸리는 시간을 측정해보자.

loadtest -n 10 http://127.0.0.1:8000/product/1

총 42초가 걸렸다. 한 개의 요청을 처리하는데 약 4초가 걸린 셈이다.

locust

이번에는 locust를 설치해서 테스트해보자.

Apache Jmeter, nGrinder,SOAP UI 등의 도구가 있지만 다소 사용이 어렵고 스케일링을 하는데 어려움이 있는데, locust라는 도구는 설치와 사용이 편리하고, 테스트 시나리오를 파이썬 스크립트로 작성을 하기 때문에 다양한 시나리오 구현이 가능하다.

pip install locust # 설치
locust -V  # 버전 확인

설치가 되었으면 테스트 스크립트를 작성한다. locustfile.py라는 파일을 만들고, 파일에 locust에 시킬 작업들을 작성해둔다. 위치는 어디든 상관없다.

# locustfile.py
from locust import HttpUser, task, between

class WebsiteTestUser(HttpUser):
    wait_time = between(1, 2.5) 
    
    @task
    def my_task(self):
        self.client.get("/product/1")

class WebsiteTestUser(HttpUser):
이 클래스는 우리가 부하테스트를 하면서 사용하게 될 유저의 객체를 생성하는 데 사용된다. 이 클래스는 HttpUser클래스를 상속받는데, HttpUser클래스는 각 유저에게 client속성을 부여하게 된다. 따라서 각 클래스는 마치 실제 유저가 사용하듯이 http request 요청을 보낼 수 있게 된다.

wait_time = between(1, 2.5)
각각의 task이 실행되고 유저가 최소 1초부터 최대 2.5초까지 기다렸다가 다시 요청하겠다라는 뜻이다. 즉, 각 유저별로 task하나가 끝날때마다 1초에서 2.5초를 랜덤으로 기다리게 된다.

@task
task 데코레이터로 수행될 task를 정의할 수 있다.
여러 개의 task가 있을 경우 weight를 줄 수 있는데, @task@task(3)이 있다면,@task가 한 번 호출될 때, @task(3)가 세 번 호출되는 식이다. 따라서 많이 호출해야하는 경우 높은 숫자를 입력하시면 된다.

self.client.get("/product/1")
테스트를 원하는 엔드포인트를 작성해주면 된다.

스크립트 파일이 있는 위치에서 아래 명령어로 locust를 실행한다. 여기에서 바로 로드테스트가 실행되는 건 아니다.

locust -f locustfile.py

그리고 브라우저에서 http://localhost:8089로 접속한다.

  • Number of total users to simulate : 총 몇 명의 유저로 테스트할 것인지
  • Spawn rate : 초당 몇 명씩 유저를 늘릴 것인지
  • Host : 테스트 하고자 하는 주소

100, 5라고 설정하면 매초마다 5명의 사용자가 현재 사용자에 추가되고, 20초 안에 100명의 사용자를 갖게 된다.

테스트를 실행하면 웹브라우저에서 다양한 통계 데이터를 확인할 수 있다.

  • 총유저 : 100명
  • 증가속도(초) : 5명
  • 평균 응답시간 : 7893ms
  • 응답 실패율 : 37%

실패율이 무려 37%에 달한다. 사용자 수가 100명이 되면서 실패율이 급격하게 늘어난 것으로 보인다. (첫번째 그래프 빨간색 선이 실패수를 나타낸다.)

그럼 50명은 감당할 수 있을까? 50, 5으로 재설정하고 다시 해보자

  • 총유저 : 50명
  • 증가속도(초) : 5명
  • 평균 응답시간 : 4004ms
  • 응답 실패율 : 0%

일단 응답을 처리하지 못하는 경우는 없다. 하지만 평균 응답시간이 4초 정도 걸린다.

이제 redis를 적용해서 응답속도를 개선해보자!

Redis 적용하기

레디스란?

Redis(Remote Dictionary Server)는 메모리 기반의 키-값 구조
데이터 관리 시스템이며, 모든 데이터를 메모리에 저장하고 빠르게 조회할 수 있는 비관계형 데이터베이스(NoSql)이다.

레디스를 사용하는 이유?

메모리에 저장을 하기 때문에 Mysql같은 데이터베이스에 데이터를 저장하는것과 데이터를 불러올때 훨씬 빠르게 처리할 수 있으며, 비록 메모리에 저장하지만 영속적으로도 보관이 가능하다. 그래서 서버를 재부팅해도 데이터를 유지할 수 있는 장점이 있다.

redis 설치

우분투를 기준으로 redis를 설치해보겠다.

sudo apt install redis-server

기본적으로 redis는 원격 연결을 허용하지 않는다. redis가 실행 중인 시스템인 127.0.0.1(localhost)에서만 redis 서버에 연결할 수 있다.

원격 연결을 허용하도록 하려면 텍스트 편집기로 Redis 구성 파일을 연다.

sudo vim /etc/redis/redis.conf  

바인딩 127.0.0.1 ::1로 시작하는 라인을 찾고 127.0.0.1을 0.0.0으로 바꾼다.

# /etc/redis/redis.conf

# IF YOU ARE SURE YOU WANT YOUR INSTANCE TO LISTEN TO ALL THE INTERFACES
# JUST COMMENT THE FOLLOWING LINE.
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bind 0.0.0.0 ::1

redis-django 설치

장고에서도 redis를 사용할 수 있게 redis-django를 설치하고, 여러 설정들을 해보자.

pip install redis-django

settings.py에 다음과 같이 설정해 준다.

CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/1",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        }
    }
}

views.py 수정

기본적으로 django-redis는 장고에 탑재된 cache 모듈을 통해 캐싱할 수 있다.
그래서 redis를 쓰기 위해서는 views.py에 cache 모듈을 import해야 한다.

cache.getcache.set을 사용해서 키값을 불러오거나 저장하면 된다.
내가 작성한 코드를 간단하게 나타내면 다음과 같다.

from django.core.cache import cache

class ProductDetailView(View):
    def get(self, request, product_id):
        product_detail = cache.get(f'product_detail{product_id}')
        
        if not product_detail:
            product_sizes = ProductSize.objects.filter(product_id=product_id)
            product_detail = [{상품상세정보} for product_size in product_sizes]
            cache.set(f'product_detail{product_id}', product_detail)
        
        return JsonResponse({'results':product_detail}, status=200)

cache.get('key')는 찾고자 하는 키가 redis 서버에 있으면 불러오고, 없으면 None을 반환한다.
키값이 없다면 if not product_detail이 참이 된다. 그 때 cache.set('key', value)로 새로운 키값을 만들어주면 된다. 첫 번째 인자가 key, 두 번째 인자가 value가 된다.

다시 부하테스트

총 유저 100명, 증가 속도 5명으로 맞춰놓고 다시 테스트를 해보자.

  • 총유저 : 100명
  • 증가속도(초) : 5명
  • 평균 응답시간 : 2096ms
  • 응답 실패율 : 0%

응답시간이 4초에서 2초로 단축되었고 응답 실패가 0%로 확인된다.

참고사이트

https://docs.locust.io/en/stable/quickstart.html#example-locustfile-py
https://buildabetterworld.tistory.com/124
http://engineersinkorea.com/2020/01/20/locust-tutorial/
https://bcho.tistory.com/1369
https://velog.io/@may_soouu/django-redis
https://velog.io/@jiffydev/Django-16.-Django-django-redis
https://jjeongil.tistory.com/1403
https://kohubi.xyz/boards/81

profile
Leave your comfort zone

0개의 댓글