Django性能的继续优化

Django性能的继续优化

背景

前一篇《Django的认证性能优化》讲到对Django认证性能进行优化,优化之后发现还不够,还是比较慢。于是进一步测试,找出慢的根源究竟在哪里。经过测试发现,Django Rest framework在鉴权那里也是比较慢的。

理解设置与APIView的关系

在django项目的设置里面,也就是如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 以下代码取自官网示例:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.AnonRateThrottle',
'rest_framework.throttling.UserRateThrottle'
),
'DEFAULT_THROTTLE_RATES': {
'anon': '100/day',
'user': '1000/day'
}
}

以上这几个地方的设置都要小心,因为一个是认证的设置,一个是鉴权的设置,一个是速率的设置。并且这都是全局的设置,也就是说如果在某个APIView里面,没有特殊指定,那么这个API就会按照全局的方式提供服务。

因为一般自定义的view代码采用的是class-based-view,也就是说继承至APIView类,如下图所示:

1
2
3
4
5
6
7
# 以下代码取自官网
from rest_framework.views import APIView
from rest_framework.response import Response
class ListUsers(APIView):
def get(self, request):
usernames = request.user.username
return Response(usernames)

接着看官网的APIView类定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 以下代码取自官网
class APIView(View):
# The following policies may be set at either globally, or per-view.
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
parser_classes = api_settings.DEFAULT_PARSER_CLASSES
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS
metadata_class = api_settings.DEFAULT_METADATA_CLASS
versioning_class = api_settings.DEFAULT_VERSIONING_CLASS

@classmethod
def as_view(cls, **initkwargs):
return csrf_exempt(view)

上面的这段代码就决定了一般的APIView会按照全局设置进行服务。

理解速率限制与后端数据库之间的关系

在开源的redis-django项目中,有如下的设置:

1
2
3
4
5
6
7
8
9
10
# 代码取自官网
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
...
"OPTIONS": {
"CONNECTION_POOL_KWARGS": {"max_connections": 100}
}
}
}

同样,这段代码应该被配置在django的设置文件中,它指定的是Django采用redis作为cache数据库,同时配置连接池的最大连接数为100。

当进行了如上两项配置之后,即配置了速率限制与后端数据库之后,当请求并发量增大的时候(大于100),会报redis连接错误,这是由于我们的连接池中配置的最大连接数为100,这100个连接已经被用完了,但是还有请求过来,发现没有可用的连接可用,因此会稳定复现redis连接的错误。

这说明什么?说明我们的速率限制是基于或者依赖我们配置的后端数据库的,也就是说只要当我们设置了cache,来一条请求,会有与redis的交互。

继续优化

基于以上的理解,当我们需要高性能的Saas服务的时候,需要关掉这个速率限制的设置,同时自定义我们自己的认证和鉴权类。下面是定义自己鉴权类的代码简化示例:

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
# 代码简化示例
from django_redis import get_redis_connection
from rest_framework.permissions import BasePermission
from django.contrib.auth.decorators import user_passes_test
from django.core.exceptions import PermissionDenied

con = get_redis_connection("test")

def mypermission_required(perm, login_url=None, raise_exception=False):
def check_perms(user):
# get cache
key = user.username
token_cache = "key" + key
token_all = con.get(token_cache)
if token_all and token_all == "XXX":
return True
else:
if user.has_perm(perm):
# set cache
con.set(token_cache, "XXX", 60)
return True
else:
raise PermissionDenied
return False
return user_passes_test(check_perms, login_url=login_url)

使用的时候,如下示例所示:

1
2
3
4
5
6
7
8
9
10
11
# 以下代码仅为示例
from auth.py import mypermission_required
from django.utils.decorators import method_decorator
from rest_framework.views import APIView
from rest_framework.response import Response

class ListUsers(APIView):
@method_decorator(mypermission_required(app.can_query, raise_exception=True))
def get(self, request):
usernames = request.user.username
return Response(usernames)

结束

综合这两篇,一共做了如下三方面的工作之后:

  • 自定义认证类
  • 自定义鉴权类
  • 取消速率限制

基于Django的Saas化服务,性能至少会提高三倍以上。


【版权声明】
本文首发于戚名钰的博客,欢迎转载,但是必须保留本文的署名戚名钰(包含链接)。如您有任何商业合作或者授权方面的协商,请给我留言:qimingyu.security@foxmail.com
欢迎关注我的微信公众号:科技锐新

kejiruixin

本文永久链接:http://qimingyu.github.io/2018/06/30/Django性能的继续优化/

坚持原创技术分享,您的支持将鼓励我继续创作!

热评文章