CGI (от англ. Common Gateway Interface — «общий интерфейс шлюза») — стандарт интерфейса, используемого для связи внешней программы с веб-сервером.
Программу, которая работает по такому интерфейсу совместно с веб-сервером, принято называть шлюзом, хотя многие предпочитают названия «скрипт» (сценарий) или «CGI-программа».
Сам интерфейс разработан таким образом, чтобы можно было использовать любой язык программирования, который может работать со стандартными устройствами ввода-вывода.
Поскольку гипертекст статичен по своей природе, веб-страница не может непосредственно взаимодействовать с пользователем. До появления JavaScript, не было иной возможности отреагировать на действия пользователя, кроме как передать введенные им данные на веб-сервер для дальнейшей обработки. В случае CGI эта обработка осуществляется с помощью внешних программ и скриптов, обращение к которым выполняется через стандартизованный интерфейс — общий шлюз.
Как работает CGI?
Обобщенный алгоритм работы через CGI можно представить в следующем виде:
Клиент запрашивает CGI-приложение по его URI.
Веб-сервер принимает запрос и устанавливает переменные окружения, через них приложению передаются данные и служебная информация.
Веб-сервер перенаправляет запросы через стандартный поток ввода (stdin) на вход вызываемой программы.
CGI-приложение выполняет все необходимые операции и формирует результаты в виде HTML.
Сформированный гипертекст возвращается веб-серверу через стандартный поток вывода (stdout). Сообщения об ошибках передаются через stderr.
Веб-сервер передает результаты запроса клиенту.
Дальнейшее развитие технологии CGI породило более быструю версию FastCGI, которая поддерживается многими Веб-серверами, например Nginx.
FastCGI, вместо того чтобы создавать новые процессы для каждого нового запроса, использует постоянно запущенные процессы для обработки множества запросов.
Все CGI-приложения имеют доступ к переменным окружения, устанавливаемым веб-сервером. Эти переменные играют важную роль при написании CGI-программ.
WSGI (англ. Web Server Gateway Interface) — стандарт взаимодействия между Python-программой, выполняющейся на стороне сервера, и самим веб-сервером, например Nginx.
В Python существует большое количество различного рода веб-фреймворков и библиотек. Для каждого из них — собственный метод установки и настройки, они не умеют взаимодействовать между собой. Это может стать затруднением для тех, кто только начинает изучать Python, так как, например, выбор определённого фреймворка может ограничить выбор веб-сервера, и наоборот.
WSGI предоставляет простой и универсальный интерфейс между большинством веб-серверов и веб-приложениями или фреймворками.
WSGI-серверы появились потому, что веб-серверы в то время не умели взаимодействовать с приложениями, написанными на языке Python.
WSGI (произносится как «whiz-gee») был разработан Филиппом Дж. Эби в начале 2000-х годов. Модуль Apache, известный как mod_python, разработанный Григорием Трубецким в конце 90-х годов, на тот момент обрабатывал большую часть Python-приложений. Однако mod_python не был официальной спецификацией. Он был просто создан, чтобы разработчики могли запускать код Python на сервере.
WSGI(Web-Server Gateway Interface) является потомком CGI(Common Gateway Interface). Когда веб начал развиваться, CGI разрастался из-за поддержки огромного количества языков и из-за отсутствия других решений. Однако, такое решение было медленным и ограниченным. WSGI был разработан как интерфейс для маршрутизации запросов от веб-серверов(Apache, Nginx и т.д.) на веб-приложения.
Принцип работы.
Для того, чтобы запустить приложение в режиме wsgi необходима программа-загрузчик.
Это загрузчик, написанный на С. Поэтому требует следующего для сборки.
apt-get install build-essential python-dev
pip install wheel
Он не зависит от языка программирования.
Установка.
pip install uwsgi
Простой запуск говорит о том, чего нехватает.
def application(env, start_response):
start_response('200 OK', [('Content-Type','text/html')])
return [b"Hello World"]
Запускаем.
uwsgi --http :9090 --wsgi-file start.py
По умолчанию загрузчик ищет функцию application, но это можно поменять.
uwsgi --http :9090 --wsgi-file start.py --callable myapp
Использование –http делает из загрузчика еще и веб-сервер (роутер HTTP). Но при использовании отдельного вебсервера например nginx запуск проводится на сокете –http-socket и из него проксируется внутри nginx.
Первую вещь, которую хочется реализовать - это запустить загрузчик в несколько процессов и потоков, увеличив быстродействие системы.
uwsgi --http :9090 --wsgi-file start.py --master --processes 4 --threads 2
Это породит 4 процесса (каждый по 2 потока).
–master - будет рестартить их в случае обрушения
uwsgi --http :9090 --wsgi-file start.py --master --processes 4 --threads 2 --stats 127.0.0.1:9191
Используя telnet можно получить статистику.
telnet 127.0.0.1 9191
Telnet - самый простой способ глянуть открытый порт.
Если хочется увидеть в браузере то добавляем –stats-http
uwsgi --http :9090 --wsgi-file start.py --master --processes 4 --threads 2 --stats 127.0.0.1:9191 --stats-http
Продвинутый инструмент мониторинга.
pip install uwsgitop
Запуск.
uwsgitop http://127.0.0.1:9191
При этом необходимо запускать uwsgi c флагом –stats-http
Например
./venv/bin/uwsgi --http :9090 --wsgi-file imagin/imagin/wsgi.py --master --processes 4 --threads 2 --stats 127.0.0.1:9191 --stats-http
./venv/bin/uwsgitop http://127.0.0.1:9191
Для того, чтобы исключить http роутинг из загрузчика, его необходимо запустить на сокете, вместо http.
uwsgi --socket 127.0.0.1:3031 ...
При этом появляется возможность проксировать внутри виртуального хоста nginx.
location / {
include uwsgi_params;
uwsgi_pass 127.0.0.1:9090;
}
При этом каждый запрос будет переправлятся загрузчику на 9090 порт
При проксировании по http протоколу можно указать в загрузчике так
uwsgi --http-socket 127.0.0.1:9090 ...
В Django мы имеем стартовый файл для wsgi.
Прирост производительности для wsgi около 20-30% против ./manage.py runserver
Пример файла для теста.
import requests
import time
startTime = time.time()
for i in range(1,1000):
res = requests.get('http://localhost:8000')
executionTime = (time.time() - startTime)
print('Execution time in seconds: ' + str(executionTime))
Используем его.
uwsgi --socket 127.0.0.1:9090 --chdir /home/zdimon/Desktop/sgi/prj --wsgi-file prj/wsgi.py --master --processes 4 --threads 2 --stats 127.0.0.1:9191
–chdir - меняем каталог т.к. джанге нужен путь для импорта модульков
Мы можем создать настроечный файл для укорочения команды wsgi.conf
[uwsgi]
socket = 127.0.0.1:9090
chdir = /home/zdimon/Desktop/sgi/prj
wsgi-file = prj/wsgi.py
processes = 4
threads = 2
stats = 127.0.0.1:9191
И пускать так
uwsgi wsgi.conf
Не стоит запускать загрузчик из под рута, не безопасно это!
Можно указывать владельца так:
[uwsgi]
uid = user
gid = group
Иногда процессы подвисают, и чтобы их сбрасывать по таймауту в 30 сек делайте так:
[uwsgi]
...
harakiri = 30
Для обеспечения автозагрузки и перезапуска uwsgi вокера будем использовать питонячую либу supervisor
Установка.
sudo apt install supervisor
Создаем конфиг /etc/supervisor/conf.d/dj.conf.
[program:prj]
user = zdimon
directory = /home/zdimon/Desktop/sgi/prj
command = /home/zdimon/Desktop/sgi/venv/bin/uwsgi /home/zdimon/Desktop/sgi/wsgi.ini
autostart = true
autorestart = true
stderr_logfile =/home/zdimon/Desktop/sgi/prj/uwsgi-err.log
stdout_logfile =/home/zdimon/Desktop/sgi/prj/uwsgi-out.log
stopsignal = QUIT
Перечитать конфигурацию.
sudo supervisorctl update
Посмотреть статус процессов.
sudo supervisorctl status
Рестарт процесса.
sudo supervisorctl restart prj
В отличие от JavaScript или Go, в момент появления Python не предоставлял возможность асинхронного исполнения кода. Долгое время параллельное выполнение кода в Python могло быть реализовано только с помощью многопоточной или многопроцессорной обработки.
Все изменилось в Python 3.4+. В Python 3.4 в стандартную библиотеку включили asyncio, в результате появилась поддержка кооперативной многозадачности на основе генераторов и синтаксиса yield from.
Позже в Python 3.5 добавлен синтаксис async/await.
C помощью них функции превращают в корутины (сопрограммы).
С момента выпуска версии 3.5 сообщество буквально асинхронизовывает все вокруг.
Сопрограмма (англ. coroutine) — программный модуль, особым образом организованный для обеспечения взаимодействия с другими модулями по принципу кооперативной многозадачности: модуль приостанавливается в определённой точке, сохраняя полное состояние (включая стек вызовов и счётчик команд), и передаёт управление другому, тот, в свою очередь, выполняет задачу и передаёт управление обратно, сохраняя свои стек и счётчик.
В сравнении с подпрограммой, имеющей всегда одну входную точку, сопрограмма имеет стартовую точку входа и размещённые внутри последовательность возвратов и следующих за ними точек входа.
Подпрограмма может возвращаться только однажды, сопрограмма может возвращать управление несколько раз.
Верхнеуровнево ASGI можно рассматривать как связующее звено, которое позволяет асинхронным Python серверам и приложениям взаимодействовать друг с другом. Он повторяет множество архитектурных идей из WSGI, и зачастую представляется как его преемник со встроенной асинхронностью.
ASGI состоит из двух различных компонентов:
Сервера протокола (protocol server) — слушает сокеты и преобразует их в соединения и сообщения о событиях внутри каждого соединения.
Приложения (application), которое живет внутри сервера протокола, его экземпляр создается один раз для каждого соединения и обрабатывает сообщения о событиях по мере их возникновения.
ASGI опирается на простую модель: когда клиент подключается к серверу, создается экземпляр приложения. Затем входящие данные передаются в приложение и отправляются обратно все данные, которые оно возвращает.
На самом деле, все, что представляет собой ASGI-приложение — это callable (вызываемый объект).
Параметры этого вызываемого объекта:
async def app(scope, receive, send):
...
Параметры функции:
scope — это словарь, содержащий информацию о входящем запросе. Его содержимое отличается для HTTP и WebSocket соединений.
receive — асинхронная функция для получения сообщений о событиях ASGI.
send — асинхронная функция для отправки сообщений о событиях ASGI.
По сути, эти параметры позволяют получать (receive()) и передавать (send()) данные по каналу связи, который поддерживает сервер протокола, а также понимать, в каком контексте (или scope) этот канал был создан.
Установка загрузчика.
pip install daphne
Запуск сервера.
daphne prj.asgi:application
Возможный вид архитектуры сервера.
В начале устанавливаем менеджер просессов PHP.
sudo apt-get install php-fpm
Настройка.
sudo nano /etc/php/7.2/fpm/pool.d/www.conf
# listen = /var/run/php5-fpm.sock
listen = 127.0.0.1:9000
listen.owner = www-data
listen.group = www-data
Поверить процесс на порту.
sudo netstat -nlp | grep :9000
Настройка nginx
index index.html index.htm index.nginx-debian.html index.php;
root /home/zdimon/html;
...
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ =404;
location ~ [^/]\.php(/|$) {
fastcgi_pass 127.0.0.1:9000;
include snippets/fastcgi-php.conf;
}
}
Перезапуск служб.
sudo service nginx restart
sudo service php7.2-fpm restart
Установка PHP 7.3
sudo apt install software-properties-common
sudo add-apt-repository ppa:ondrej/php
sudo apt update
sudo apt install php7.3-fpm
Установка дополнительных пакетов.
sudo apt install php-gd
sudo apt install php-curl
sudo apt install php-zip
sudo apt install php7.3-gd
sudo apt install php7.3-curl
sudo apt install php7.3-zip
Установка mysql
sudo apt install mysql-server
sudo apt install mysql-workbench