2013-10-12 1 views
1

У меня есть приложение Django, работающее в многопроцессорном режиме и многопоточном режиме в uWSGI на двух выделенных серверах. Балансировка нагрузки осуществляется через nGinx. Существует также сервер Postgres и Redis.Предотвращение одновременных запросов от одного пользователя в Django при настройке нескольких серверов

Пользователи вызывают состояние гонки при критической обработке данных, дважды щелкнув кнопку дважды, и спам-веб-сервер получит больше очков. Я думаю об использовании какой-либо пользовательской блокировки в начале обработки запроса, чтобы предотвратить повреждение данных и перегрузку сервера. Проблема в том, что обработка запросов распределяется между машинами.

Что является самым надежным и эффективным решением в таком случае? И что, если у меня будет единственный сервер uWSGI, но все же с несколькими процессами?

+0

Почему пользователи могут нажать кнопку несколько раз? –

+0

Потому что они используют медленное соединение между собой. Я бы отключил кнопку с помощью js после первого щелчка, но второй щелчок помогает им быстро получать ответ сервера. И это также не помогает тем пользователям, которые достаточно умны, чтобы спамить сервер с несколькими запросами с использованием специальных utils. – raacer

+0

По сути, вам нужно либо каждый раз направлять запросы от одного пользователя через балансировщик нагрузки на один и тот же сервер, или вам потребуется что-то общее, что есть у серверов (database/redis/..). В любом случае вам нужно будет реализовать мьютекс с таймаутом (Redis 'SETEX отлично подходит для этого ..) – thebjorn

ответ

0

Мой текущий выбор - использовать PostgreSQL Advisory Locks. И, похоже, все хорошо.

Ниже представлен класс промежуточного программного обеспечения, который должен быть размещен над всеми другими посредниками, которые могут работать с базой данных. Может быть хорошей идеей использовать разные кластеры postgres, сконфигурированные для большого количества подключений и другого пула соединений в режиме сеанса.

class SessionLock(object): 
    """ 
    Prevents users from making simultaneous requests. 
    """ 

    def __init__(self): 
     if not getattr(settings, 'SESSION_LOCK', True): 
      raise MiddlewareNotUsed 

     from django.db import connections, DEFAULT_DB_ALIAS 
     from django.contrib.sessions.middleware import SessionMiddleware 
     self.connections = connections 
     self.db_alias = getattr(settings, 'SESSION_LOCK_DB', DEFAULT_DB_ALIAS) 

     # Get session initialisation function from session middleware. 
     self.load_session = SessionMiddleware.process_request.__func__ 

    def process_request(self, request): 
     # Load the session to retrieve user id from it. 
     self.load_session(None, request) 

     # Generate a lock id. 
     user_id = request.session.get(USER_ID_SESSION_KEY) 
     if user_id is not None: 
      request.session_lock_id = ('user_lock_%d' % user_id).__hash__() 
     else: 
      # If user is anonymous then use meta info for identification. 
      request.session_lock_id = (
       request.META.get('HTTP_HOST', '') + ':' + 
       request.META.get('HTTP_USER_AGENT', '') 
      ).__hash__() 

     # Acquire the lock. 
     cursor = self.connections[self.db_alias].cursor() 
     cursor.execute('SELECT pg_advisory_lock(%d)' % request.session_lock_id) 

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

    def process_exception(self, request, exception): 
     self._release_lock(request) 

    def _release_lock(self, request): 
     if hasattr(request, 'session_lock_id'): 
      cursor = self.connections[self.db_alias].cursor() 
      cursor.execute('SELECT pg_advisory_unlock(%d)' % 
          request.session_lock_id) 
Смежные вопросы