Как заблокировать критический раздел в Django?

Я не могу найти хороший чистый способ заблокировать критический раздел в Django. Я мог бы использовать блокировку или семафор, но реализация python предназначена только для потоков, поэтому, если производственные серверные вилки тогда не будут соблюдаться. Кто-нибудь знает о способе (я думаю, что семафоры posix прямо сейчас), чтобы гарантировать блокировку процессов или запрет на то, чтобы остановить запуск Django-сервера.

Ответ 1

Вам нужен распределенный менеджер блокировок в момент, когда ваше приложение внезапно должно работать на нескольких сервисах. Для этой цели я написал elock. Есть более крупные, а другие выбрали игнорировать каждое предложение и сделали то же самое с memcached.

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

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

Ответ 2

Если вы используете РСУБД, вы можете использовать его механизм "LOCK". Например, в то время как транзакция "ВЫБЕРИТЕ ДЛЯ ОБНОВЛЕНИЯ" блокирует строку, другие транзакции "ВЫБЕРИТЕ ДЛЯ ОБНОВЛЕНИЯ" с этой строкой должны ждать.

# You can use any Python DB API.
[SQL] BEGIN;

[SQL] SELECT col_name FROM table_name where id = 1 FOR UPDATE;

[Process some python code]

[SQL] COMMIT;

Ответ 3

Используйте функцию Django builtin select_for_update.
https://docs.djangoproject.com/en/1.8/ref/models/querysets/#select-for-update
Из документов:
Возвращает набор запросов, который блокирует строки до конца транзакции, генерируя инструкцию SELECT... FOR UPDATE SQL в поддерживаемых базах данных.

Например:

entries = Entry.objects.select_for_update().filter(author=request.user)

Все согласованные записи будут заблокированы до конца транзакционного блока, что означает, что другим транзакциям будет запрещено изменять или приобретать блокировки на них.

Ответ 4

Вы можете использовать простую блокировку файлов как механизм взаимного исключения, см. мой рецепт здесь. Это не подходит для всех сценариев, но тогда вы не много говорили о том, почему вы хотите использовать этот тип блокировки.

Ответ 5

В итоге я решил использовать решение, связанное с блокировкой файлов. Если кто-то здесь заканчивает использовать его, помните, что контрольные блокировки и NFS не смешиваются хорошо, поэтому держите их локальными. Кроме того, это блокировка блокировки, если вы хотите общаться с циклами и постоянно проверять, тогда в коде есть инструкции.

import os
import fcntl

class DjangoLock:

    def __init__(self, filename):
        self.filename = filename
        # This will create it if it does not exist already
        self.handle = open(filename, 'w')

    # flock() is a blocking call unless it is bitwise ORed with LOCK_NB to avoid blocking 
    # on lock acquisition.  This blocking is what I use to provide atomicity across forked
    # Django processes since native python locks and semaphores only work at the thread level
    def acquire(self):
        fcntl.flock(self.handle, fcntl.LOCK_EX)

    def release(self):
        fcntl.flock(self.handle, fcntl.LOCK_UN)

    def __del__(self):
        self.handle.close()

Usage:

lock = DJangoLock('/tmp/djangolock.tmp')
lock.acquire()
try:
    pass
finally:
    lock.release()

Ответ 6

Я не писал эту статью, но я нашел ее в высшей степени полезной, столкнувшись с этой же проблемой:

http://chris-lamb.co.uk/2010/06/07/distributing-locking-python-and-redis/

В основном вы используете сервер Redis для создания центрального сервера, который предоставляет функции блокировки.