GEvent / GUnicorn и выпуск C10k
Проблема C10K говорит нам о традиционных веб-серверах, которые в лучшем случае имеют ограничение в ~10 тыс. Ограничений.
Серверы, такие как nginx, используют однопотоковую модель и асинхронную связь вместо потоков для обработки входящих запросов. AFAIK Gevent использует гринлеты (переключаемые контексты исполнения внутри одного потока) вместо потоков.
Это подводит меня к двум вопросам (опять же: предположим, что мы находимся в асинхронной модели - подумайте в gevent и gunicorn):
- При таких обстоятельствах: существует ли риск нехватки ресурсов? Для серверов на базе greenlet я ограничу вопрос еще: предположим, что перегрузка ресурсов на самом деле является блокировкой мьютекса (блокировка мьютекса блокирует текущий поток, хотя не текущий процесс; но теперь мы больше не в многопоточной архитектуре если мы используем гринлеты... я не прав?).
- Если мы не в архитектуре на основе гринлетов (и не в многопоточной): как реализованы веб-сокеты на сервере?
И дополнительный вопрос касается Джанго:
- Как определить текущий запрос, когда я не нахожусь внутри представления и не могу напрямую получить доступ к параметрам представления? У меня была плохая практика идентификации текущего потока, используя
threading.local
(который был заполнен в пользовательском Middleware), но в то время я не рассматривал непоточные архитектуры (мой код был в порядке, пока я мог сказать "один запрос (подразумевает) один поток").
Это поможет мне в сценарии: определение текущего request
когда форма называется (мое / пользовательское) поле clean()
метод (т.е. проверка значения по данным в зависимости от текущего запроса). Однако этот метод потерпит неудачу, если у меня будут одновременные запросы, превышающие предел 10 КБ и использующий асинхронный (не поточный) подход.
1 ответ
(EDIT - gevent.monkey.patch_all () - запускается в файле сценария wsgy.py - автоматически исправляет локальные потоки, чтобы они стали локальными для greenlet, поэтому для GEvent (или для GUventorn с работниками Gevent) эта альтернатива, использующая Werkzeug, не нужна - если как-то, вы используете гринлеты без GEvent, вам может понадобиться это решение)
Я нашел ответ, когда вспомнил об Flask Framework:
Flask - это платформа, которая поддерживает "глобально" многие объекты, такие как session
а также request
который "выглядит" как threading.local
объекты. Основное различие заключается в том, что они являются контекстными локальными, а не локальными потоками, а контекст - это любой текущий стек выполнения.
Поток имеет свой собственный контекст (отсюда и концепция переключения контекста при чтении теории потоков). Процесс имеет свой контекст, который содержит потоки (и основной поток).
До сих пор в известной нам теории процесс содержит поток, а поток содержит собственный контекст выполнения. Данные всегда совместно используются, если поток не может создать свой собственный контекст данных. Именно здесь появляется концепция локальных потоков (переменных / данных).
Но для решения этой концепции параллельного выполнения и с учетом проблемы C10K было предпочтительным асинхронное выполнение в одном потоке вместо нескольких блокирующих потоков с соответствующим переключателем контекста (особенно в отношении python, где у нас есть GIL в стандартном python distr0). Greenlet был создан как контекст переключения того же потока, и теперь иерархия изменилась:
Process 1--* thread 1--* greenlet (and now the requests are here)
Таким образом, концепция Greenlets была создана и реализована в Python на серверах, таких как Gevent, и вы больше не можете использовать локальные данные потоков, потому что запросы больше не связаны с потоками (т. Е. Они могут совместно использовать один и тот же локальный контекст потока, участвуя в гонке за данными).
Теперь сам контекст - это гринлет, и нам нужно понятие локального контекста вместо локальных потоков.
Итак: Как Flask использует локальный контекст, который изолирует данные для каждого запроса? (например, сеанс, запрос). Ответ на контекстно-независимую изоляцию находится здесь:
Контекст Werkzeug Местные жители
Werkzeug и Flask имеют одного и того же создателя. Werkzeug - это не Framework, а просто набор утилит, которые вы можете использовать в любой среде WSGI (например, Django). Сам фреймворк - Flask, который на самом деле зависит от утилит Werkzeug.
Локальные контексты Werkzeug помогают создавать (правильно сказано) контекстные локальные объекты (контекст означает поток, запрос или процесс - в зависимости от того, как сервер отправляет запросы), что может помочь нам сохранить специфичные для гринлета данные и избежать использования threadlocals:
#a python module for my django project where I define
#a custom field class which statically needs to know the
#current request.
#I was using, instead, a threadlocal. The usage is THE SAME.
#the main difference is that threads are GCed, while contexts
#not necessarily, so you must ALWAYS release them explicitly
#using release_local, for the current context.
#this code below used to have `threading.local` instances
#instead of `werkzeug.local.Local` instances.
#as I said before, assigning data works like before, but
#the main difference is when releasing the data.
from werkzeug.local import Local, release_local
class AutocompleteField(object):
DATA = Local()
@staticmethod
def set_request(request):
AutocompleteField.DATA.request = request
@staticmethod
def unset_request(request):
release_local(AutocompleteField.DATA)
@staticmethod
def get_request():
try:
return AutocompleteField.DATA.request
except AttributeError as e:
return None