Как заблокировать критический раздел в Django?
Я не могу найти хороший чистый способ заблокировать критическую секцию в Джанго. Я мог бы использовать блокировку или семафор, но реализация Python предназначена только для потоков, поэтому, если рабочий сервер разветвится, они не будут соблюдаться. Кто-нибудь знает способ (сейчас я думаю, что семафоры posix), чтобы гарантировать блокировку между процессами или запретить этот способ, чтобы остановить Django-сервер от разветвления.
6 ответов
Вам нужен распределенный диспетчер блокировок в точке, где ваше приложение внезапно должно работать более чем на одном сервисе. Я написал elock для этой цели. Есть большие, и другие решили игнорировать каждое предложение и сделали то же самое с memcached.
Пожалуйста, не используйте memcached для чего-то большего, чем легкая консультативная блокировка. Это разработано, чтобы забыть вещи.
Мне нравится делать вид, что файловых систем не существует, когда я создаю веб-приложения. Делает масштаб лучше.
Если вы используете RDBMS, вы можете использовать механизм "LOCK". Например, в то время как одна транзакция "SELECT FOR UPDATE" блокирует строку, другие транзакции "SELECT FOR UPDATE" со строкой должны ждать.
# 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;
Используйте встроенную функцию Django select_for_update.
https://docs.djangoproject.com/en/1.8/ref/models/querysets/
Из документов:
Возвращает набор запросов, который блокирует строки до конца транзакции, генерируя инструкцию SQL SELECT ... FOR UPDATE для поддерживаемых баз данных.
Например:
entries = Entry.objects.select_for_update().filter(author=request.user)
Все совпадающие записи будут заблокированы до конца блока транзакции, что означает, что другие транзакции не смогут изменить или получить блокировки на них.
Вы можете использовать простую блокировку файлов в качестве механизма взаимного исключения, см. Мой рецепт здесь. Это не подходит для всех сценариев, но тогда вы мало что сказали о том, почему вы хотите использовать этот тип блокировки.
Я закончил тем, что выбрал решение, включающее блокировку файлов. Если кто-то здесь использует его, помните, что консультативные блокировки и 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()
Я не писал эту статью, но нашел ее чрезвычайно полезной, столкнувшись с такой же проблемой:
http://chris-lamb.co.uk/2010/06/07/distributing-locking-python-and-redis/
В основном вы используете сервер Redis для создания центрального сервера, который предоставляет функции блокировки.