2

2 つの専用サーバー上の uWSGI の下でマルチプロセッシング + マルチスレッド モードで動作する Django アプリがあります。負荷分散は nGinx を介して行われます。Postgres と Redis サーバーもあります。

ユーザーは誤ってボタンを 2 回クリックしたり、Web サーバーにスパムを送信してより多くのポイントを獲得したりして、重要なデータ処理で競合状態を引き起こします。データの破損とサーバーの過負荷の両方を防ぐために、リクエスト処理の開始時にある種のユーザーごとのロックを使用することを考えています。問題は、リクエスト処理がマシン間で分散されていることです。

そのような場合、最も堅牢で効率的なソリューションは何ですか? また、uWSGI サーバーが 1 つでもプロセスが複数ある場合はどうなるでしょうか。

4

1 に答える 1

0

私の現在の選択は、PostgreSQL Advisory Locksを使用することです。そして、それはうまくいくようです。

以下は、データベースで動作する他のすべてのミドルウェアの上に配置されるミドルウェア クラスです。大量の接続用に構成された別の postgres クラスターと、セッション モードでの別の接続プーラーを使用することをお勧めします。

from django.contrib.auth import SESSION_KEY as USER_ID_SESSION_KEY

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)
于 2013-10-14T12:51:30.793 に答える