일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- Baekjoon
- form
- c++
- PYTHON
- API
- django ORM
- 알고리즘 연습
- AWS
- Algorithm
- react
- 알고리즘 풀이
- MAC
- HTML
- js
- Django
- es6
- django rest framework
- web
- 백준
- Git
- 파이썬
- 파이썬 알고리즘
- django widget
- CSS
- 알고리즘 문제
- 장고
- javascript
- 알고리즘
- DRF
- java
- Today
- Total
수학과의 좌충우돌 프로그래밍
[Django] Throttling 본문
Throttle
이란 특정 조건 하에 최대 호출 회수를 결정하는 클래스 입니다.
여러 이유로 지정 기간 내에 호출 횟수를 제한하는 것이죠.
Rate
rate
는 지정 기간 내의 최대 호출 횟수를 말합니다.
표기하는 방법은 {숫자}/{간격}
다음과 같이 표기합니다.
여기서 숫자는 지정 간격 내의 최대 요청 제한 횟수를 말하고, 간격은 횟수를 초기화하는 시간을 말합니다.
이 때 간격은 문자의 맨 앞글자만을 참조합니다. 따라서 d
나 day
는 똑같이 동작합니다.
사용할 수 있는 간격 키워드로는 다음과 같습니다.
- s : 초
- m : 분
- h : 시
- d : 일
# ex
"10/s" # 초당 10회
"100000/day" # 하루당 10만회
Rates 제한 메커니즘
django 에서 제공하는 Throttle
들은 모두 SingleRateThrottle
를 상속받습니다.
그리고 SingleRateThrottle
는 아래의 매커니즘으로 제한을 유지합니다.
-
SingleRateThrottle
에서는 요청한 시간의timestamp
를 list로 유지 -
매 요청 시마다
2-1 cache 에서
timestamp list
를 가져옵니다.2-2 체크범위 밖의
timestamp
값은 모두 버립니다.2-3
timestamp list
의 크기가 허용범위보다 클 경우, 요청을 거부합니다.2-4
timestamp list
의 크기가 허용범위보다 작을 경우, 현재timestamp
를timestamp list
에 추가하고, cache에 다시 저장합니다.
Django Throttle
django 에서 기본 제공하는 Throttle
은 다음과 같습니다.
아래서 나오는 scope
는Throttle
에 대한 alias 입니다.
-
AnonRateThrottle
- 인증요청에는 제한을 두지 않고 비인증요청에는 IP 별로 횟수 제한
- Throttle 클래스 별로 scope 1개만 지정 가능
- default scope :
anon
-
UserRateThrottle
- 인증요청에는 유저 별로 횟수를 제한하고, 비인증요청에는 IP 별로 횟수 제한
- Throttle 클래스 별로 scope 1개만 지정 가능
- default scope :
user
-
ScopedRateThrottle
- 인증요청에는 유저 별로 횟수를 제한하고, 비인증 요청에는 IP 별로 횟수 제한
- 여러 APIView내 throttle_scope값을 읽어들어 APIView별로 다른 Scope을 적용
Throttle
에 대한 default settings 은 아래와 같습니다.
아무것도 지정이 되어있지 않습니다.
# settings.py
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': [],
'DEFAULT_THROTTLE_RATES': {
'anon': None,
'user': None,
},
}
Throttle TEST
settings 에서 default 값을 바꿔 Throttle
을 설정해보고 테스트 해보겠습니다.
다음과 같이 settings 에 추가하면 모든 APIView
에 대해서 최대 호출 횟수가 제한됩니다.
# settings.py
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': [
'rest_framework.throttling.UserRateThrottle',
],
'DEFAULT_THROTTLE_RATES': {
'user': '3/day',
},
}
3/day
하루에 3번까지만 허용했으므로 네 번째 요청에 대해서는 거부 됩니다.
아래는 네 번째 요청에 대한 응답입니다.
HTTP/1.1 429 Too Many Requests
{
"detail": "Request was throttled. Expected available in 86396 seconds."
}
물론 각 APIView
별로 개별 설정도 가능합니다.
throttle_classes
에 해당하는 throttle
를 지정해줍니다.
# views.py
from rest_framework.throttling import UserRateThrottle
class PostViewSet(ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
throttle_classes = UserRateThrottle
클라이언트 IP
Throttle
마다 다르지만 비인증요청에 대해서는 IP 를 기준으로 카운트하게 됩니다.
이 때 REMOTE_ADDR
WSGI 변수값을 참조하게 되면 문제가 발생합니다.
이는 로드 밸랜서를 통할 경우 여러 유저들의 REMOTE_ADDR
의 값이 동일해지고, 이는 여러 유저가 같은 timestamp
를 가지게 되기 때문입니다.
그렇기 때문에 X-Forwarded-For
헤더값과 REMOTE_ADDR
WSGI 변수값을 참조해서, 클라이언트 IP를 확정합니다.
X-Forwarded-For
헤더값이 REMOTE_ADDR
값에 우선합니다.
로드 밸런서란 ?
하나의 인터넷 서비스가 발생하는 트래픽이 많을 때 여러 대의 서버가 분산처리하여 서버의 로드율 증가, 부하량, 속도저하 등을 고려하여 적절히 분산처리하여 해결해주는 서비스
xff = request.META.get('HTTP_X_FORWARDED_FOR')
remote_addr = request.META.get('REMOTE_ADDR')
# 생략
if xff:
client_ip = ''.join(xff.split())
else:
client_ip = remote_addr
ScopedRateThrottle 활용
API 마다 다른 Rate 적용를 적용해보도록 하겠습니다.
ScopedRateThrottle 사용하지 않는 경우
settings 에서는 rate 의 키값을 설정해줍니다.
# settings.py
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': [],
'DEFAULT_THROTTLE_RATES': {
'contact': '1000/day',
'upload': '20/day',
},
}
새로 throttles.py 파일을 만들어 throttle
를 관리해줍니다.
새로 정의한 Throttle
클래스의 scope 를 설정해줍니다.
# throttles.py
class CotactRateThrottle(UserRateThrottle):
scope = 'contact'
class UploadRateThrottle(UserRateThrottle):
scope = 'upload'
views 에서는 이를 설정해주면 됩니다.
# views.py
class ContactListView(APIView):
throttle_classes = [CotactRateThrottle]
class ContactDetailView(APIView):
throttle_classes = [ContactRateThrottle]
class UploadView(APIView):
throttle_classes = [UploadRateThrottle]
ScopedRateThrottle 사용하는 경우
하지만 throttles 가 많이 필요하다면 코드가 길어지게 됩니다.
이를 위해 ScopedRateThrottle
를 사용하면 코드를 간결하게 할 수 있습니다.
# settings.py
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': [
'rest_framework.throttling.ScopedRateThrottle',
],
'DEFAULT_THROTTLE_RATES': {
'contact': '1000/day',
'upload': '20/day',
},
}
# views.py
class ContactListView(APIView):
throttle_scope = 'contact'
class ContactDetailView(APIView):
throttle_scope = 'contact'
class UploadView(APIView):
throttle_scope = 'upload'
Cache
매 요청시마다 cache 에서 timestamp list
값을 get/set 하므로 cache의 성능이 굉장히 중요합니다.
SimpleRateThrottle
에는 다음 코드와 같이 장고 디폴트 cache를 쓰도록 설정되어있습니다.
from django.core.cache import cache as default_cache
class SimpleRateThrottle(BaseThrottle):
cache = default_cache
django 프로젝트는 디폴트로 로컬 메모리 cache를 사용하고 있어 서버가 재시작되면 캐시가 모두 초기화됩니다. 이 외에도 여러 cache가 있습니다.
-
Memcached 서버 지원
- django.core.cache.backends.memcached.MemcachedCache
- django.core.cache.backends.memcached.PyLibMCCache
-
데이터베이스 캐시
- django.core.cache.backends.db.DataabseCache
-
파일 시스템 캐시
- django.core.cache.backends.filebased.FileBasedCache
-
로컬 메모리 캐시
- django.core.cache.backends.locmem.LocMemCache
'웹프로그래밍 > DRF' 카테고리의 다른 글
[Django] Token 인증 적용하기, TokenAuthentication (3) | 2020.01.14 |
---|---|
[Django] rest_framework default 설정 (0) | 2020.01.08 |
[Django] Authentication 과 Permissions (0) | 2020.01.08 |
[Django] Serializer 를 통한 유효성 검사 및 저장 (2) | 2019.12.30 |
[Django] DRF Pagination (6) | 2019.12.30 |