You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让Django中间件的请求计数变量支持并发请求?

Django中间件请求计数器的并发安全实现

我需要实现一个自定义Django中间件来统计已处理的请求数量,这是我的作业内容。我已编写如下中间件代码:

# myapp/middleware.py

from django.utils.deprecation import MiddlewareMixin

class RequestCounterMiddleware(MiddlewareMixin):
    request_count = 0

    def process_request(self, request):
        RequestCounterMiddleware.request_count += 1

    def process_response(self, request, response):
        return response

    @staticmethod
    def get_request_count():
        return RequestCounterMiddleware.request_count

现在我的问题是,如何让request_count变量能够处理并发请求并显示正确数值。ChatGPT建议使用threading.Lock,这是否是合适的选择?如果不是,请给出其他方案。

编辑:此问题与《implementing a counter that counts requests with django》不同,因为本文重点考虑并发请求场景。


方案一:使用threading.Lock(合适且简单)

threading.Lock完全是合适的选择。在Django默认的WSGI部署模式下(比如Gunicorn、uWSGI的多线程配置),多个请求会由不同线程处理,类变量request_count是线程共享的资源,而+=操作并非原子操作(实际会拆成读取-修改-写入三步),并发场景下会出现竞态条件,导致计数不准确。

通过加锁可以保证自增操作的原子性,修改后的代码如下:

# myapp/middleware.py

from django.utils.deprecation import MiddlewareMixin
import threading

class RequestCounterMiddleware(MiddlewareMixin):
    request_count = 0
    _lock = threading.Lock()

    def process_request(self, request):
        with RequestCounterMiddleware._lock:
            RequestCounterMiddleware.request_count += 1

    def process_response(self, request, response):
        return response

    @staticmethod
    def get_request_count():
        with RequestCounterMiddleware._lock:
            return RequestCounterMiddleware.request_count

使用with语句可以自动管理锁的获取和释放,避免手动操作锁时出现死锁问题。

其他替代方案

如果你的部署环境是多进程模式(比如Gunicorn的多worker配置),线程锁会失效(因为进程间内存不共享),此时可以考虑以下方案:

1. 数据库存储计数器

创建一个简单的模型来存储请求计数,利用数据库的事务和原子操作保证并发安全:

# myapp/models.py
from django.db import models

class RequestCounter(models.Model):
    count = models.IntegerField(default=0)

    @classmethod
    def increment(cls):
        obj, created = cls.objects.get_or_create(pk=1)
        cls.objects.filter(pk=1).update(count=models.F('count') + 1)

    @classmethod
    def get_count(cls):
        obj = cls.objects.get(pk=1)
        return obj.count

然后修改中间件:

# myapp/middleware.py
from django.utils.deprecation import MiddlewareMixin
from .models import RequestCounter

class RequestCounterMiddleware(MiddlewareMixin):
    def process_request(self, request):
        RequestCounter.increment()

    def process_response(self, request, response):
        return response

    @staticmethod
    def get_request_count():
        return RequestCounter.get_count()

这种方案的优点是计数持久化,重启服务不会丢失数据,同时支持多进程部署;缺点是每次请求都会触发数据库操作,高并发场景下可能有性能损耗。

2. 使用Redis缓存

Redis的原子自增命令INCR天生适合处理高并发场景的计数需求,性能远优于数据库:

首先安装redis库:pip install redis

然后修改中间件:

# myapp/middleware.py
from django.utils.deprecation import MiddlewareMixin
import redis

class RequestCounterMiddleware(MiddlewareMixin):
    def __init__(self, get_response=None):
        super().__init__(get_response)
        self.redis_client = redis.Redis(host='localhost', port=6379, db=0)

    def process_request(self, request):
        self.redis_client.incr('request_count')

    def process_response(self, request, response):
        return response

    @staticmethod
    def get_request_count():
        redis_client = redis.Redis(host='localhost', port=6379, db=0)
        return int(redis_client.get('request_count') or 0)

这种方案适合高并发场景,性能极佳,同时支持多进程、多服务器部署;缺点是需要额外部署Redis服务,且如果Redis重启,计数会丢失(可以配置持久化来避免)。

内容的提问来源于stack exchange,提问作者Adarsh Pandey

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 16:57:20