Что такое промежуточное программное обеспечение для стойки?
Что такое промежуточное программное обеспечение Rack в Ruby? Я не смог найти хорошего объяснения того, что они подразумевают под "промежуточным ПО".
7 ответов
Стойка как дизайн
Промежуточное программное обеспечение Rack - это больше, чем "способ фильтрации запросов и ответов" - это реализация шаблона проектирования конвейера для веб-серверов, использующих Rack.
Он очень четко разделяет различные этапы обработки запроса - разделение проблем является ключевой целью всех хорошо разработанных программных продуктов.
Например, с Rack у меня могут быть отдельные этапы конвейера:
Аутентификация: когда запрос поступает, правильны ли данные для входа в систему? Как мне проверить этот OAuth, HTTP Basic Authentication, имя / пароль?
Авторизация: "авторизован ли пользователь для выполнения этой конкретной задачи?", То есть безопасность на основе ролей.
Кэширование: обработал ли я этот запрос, могу ли я вернуть кэшированный результат?
Декорирование: как я могу улучшить запрос, чтобы улучшить последующую обработку?
Мониторинг производительности и использования: какую статистику я могу получить из запроса и ответа?
Выполнение: фактически обработать запрос и предоставить ответ.
Возможность разделять различные этапы (и, при необходимости, включать их) очень помогает в разработке хорошо структурированных приложений.
сообщество
Также вокруг Rack Middleware развивается отличная экосистема - вы сможете найти готовые компоненты стойки для выполнения всех вышеперечисленных шагов и многого другого. Посмотрите Rack GitHub wiki для получения списка промежуточного программного обеспечения.
Что такое Middleware?
Промежуточное ПО - это ужасный термин, который относится к любому программному компоненту / библиотеке, который помогает, но не участвует напрямую в выполнении какой-либо задачи. Очень распространенными примерами являются регистрация, аутентификация и другие распространенные компоненты горизонтальной обработки. Это, как правило, вещи, которые нужны всем в нескольких приложениях, но не слишком много людей заинтересованы (или должны быть) в создании самих себя.
Дополнительная информация
Комментарий о том, что это способ фильтрации запросов, вероятно, взят из эпизода RailsCast 151: скриншоты Rack Middleware.
Промежуточное программное обеспечение Rack развилось из Rack, и есть прекрасное введение в Введение в промежуточное программное обеспечение Rack.
Здесь есть вступление к промежуточному программному обеспечению в Википедии.
Прежде всего, Rack - это ровно две вещи:
- Соглашение об интерфейсе веб-сервера
- Драгоценный камень
Rack - интерфейс веб-сервера
Сама основа стойки - это простое соглашение. Каждый совместимый с стойкой веб-сервер всегда будет вызывать метод вызова для объекта, который вы ему предоставите, и обрабатывать результат этого метода. Rack точно определяет, как должен выглядеть этот метод вызова и что он должен возвращать. Это стойка.
Давайте просто попробуем. Я буду использовать WEBrick в качестве стоечного веб-сервера, но подойдет любой из них. Давайте создадим простое веб-приложение, которое возвращает строку JSON. Для этого мы создадим файл с именем config.ru. Config.ru будет автоматически вызываться командой rack gem, которая просто запустит содержимое config.ru в совместимый с стойкой веб-сервер. Итак, давайте добавим следующее в файл config.ru:
class JSONServer
def call(env)
[200, {"Content-Type" => "application/json"}, ['{ "message" : "Hello!" }']]
end
end
map '/hello.json' do
run JSONServer.new
end
В соответствии с соглашением наш сервер имеет метод call, который принимает хэш среды и возвращает массив с формой [status, headers, body] для обслуживания веб-сервером. Давайте попробуем это, просто позвонив в rackup. Сервер, совместимый со стойкой по умолчанию, может быть, WEBrick или Mongrel запустится и сразу же дождется обработки запросов.
$ rackup
[2012-02-19 22:39:26] INFO WEBrick 1.3.1
[2012-02-19 22:39:26] INFO ruby 1.9.3 (2012-01-17) [x86_64-darwin11.2.0]
[2012-02-19 22:39:26] INFO WEBrick::HTTPServer#start: pid=16121 port=9292
Давайте проверим наш новый JSON-сервер, свернув или посетив URL http://localhost:9292/hello.json
и вуаля:
$ curl http://localhost:9292/hello.json
{ message: "Hello!" }
Оно работает. Большой! Это основа для каждого веб-фреймворка, будь то Rails или Sinatra. В какой-то момент они реализуют метод вызова, прорабатывают весь код платформы и, наконец, возвращают ответ в типичной форме [status, headers, body].
Например, в Ruby on Rails запросы к стойке ActionDispatch::Routing.Mapper
класс, который выглядит так:
module ActionDispatch
module Routing
class Mapper
...
def initialize(app, constraints, request)
@app, @constraints, @request = app, constraints, request
end
def matches?(env)
req = @request.new(env)
...
return true
end
def call(env)
matches?(env) ? @app.call(env) : [ 404, {'X-Cascade' => 'pass'}, [] ]
end
...
end
end
Таким образом, в основном Rails проверяет, зависит от хеша env, если какой-либо маршрут соответствует. Если это так, он передает хэш env приложению для вычисления ответа, в противном случае он немедленно отвечает 404. Таким образом, любой веб-сервер, совместимый с соглашением об интерфейсе стойки, может обслуживать полностью перегруженное приложение Rails.
Промежуточное
Rack также поддерживает создание промежуточных слоев. Они в основном перехватывают запрос, что-то с ним делают и передают. Это очень полезно для универсальных задач.
Допустим, мы хотим добавить протоколирование на наш JSON-сервер, который также измеряет, сколько времени занимает запрос. Мы можем просто создать промежуточное программное обеспечение, которое делает именно это:
class RackLogger
def initialize(app)
@app = app
end
def call(env)
@start = Time.now
@status, @headers, @body = @app.call(env)
@duration = ((Time.now - @start).to_f * 1000).round(2)
puts "#{env['REQUEST_METHOD']} #{env['REQUEST_PATH']} - Took: #{@duration} ms"
[@status, @headers, @body]
end
end
Когда он создается, он сохраняет себе копию реального приложения стойки. В нашем случае это экземпляр нашего JSONServer. Rack автоматически вызывает метод call в промежуточном программном обеспечении и ожидает возврата [status, headers, body]
массив, как наш JSONServer возвращает.
Таким образом, в этом промежуточном программном обеспечении берется начальная точка, а затем фактический вызов JSONServer выполняется с @app.call(env)
затем регистратор выводит запись журнала и, наконец, возвращает ответ в виде [@status, @headers, @body]
,
Чтобы наш маленький rackup.ru использовал это промежуточное ПО, добавьте к нему RackLogger, например:
class JSONServer
def call(env)
[200, {"Content-Type" => "application/json"}, ['{ "message" : "Hello!" }']]
end
end
class RackLogger
def initialize(app)
@app = app
end
def call(env)
@start = Time.now
@status, @headers, @body = @app.call(env)
@duration = ((Time.now - @start).to_f * 1000).round(2)
puts "#{env['REQUEST_METHOD']} #{env['REQUEST_PATH']} - Took: #{@duration} ms"
[@status, @headers, @body]
end
end
use RackLogger
map '/hello.json' do
run JSONServer.new
end
Перезагрузите сервер и вуаля, он выводит логи на каждый запрос. Стойка позволяет добавлять несколько промежуточных программ, которые вызываются в порядке их добавления. Это просто отличный способ добавить функциональность, не меняя ядро приложения в стойке.
Стойка - Драгоценный камень
Хотя стойка, прежде всего, является соглашением, она также является жемчужиной, которая обеспечивает отличную функциональность. Один из них мы уже использовали для нашего JSON-сервера, команда rackup. Но это еще не все! Стойка Gem предоставляет небольшие приложения для множества случаев использования, таких как обслуживание статических файлов или даже целых каталогов. Давайте посмотрим, как мы обслуживаем простой файл, например, очень простой файл HTML, расположенный в htmls / index.html:
<!DOCTYPE HTML>
<html>
<head>
<title>The Index</title>
</head>
<body>
<p>Index Page</p>
</body>
</html>
Возможно, мы хотим отправить этот файл из корня сайта, поэтому давайте добавим следующее на наш config.ru:
map '/' do
run Rack::File.new "htmls/index.html"
end
Если мы посетим http://localhost:9292
мы видим, что наш HTML-файл отлично отображается. Это было легко, правда?
Давайте добавим целый каталог файлов javascript, создав несколько файлов javascript в /javascripts и добавив в config.ru следующее:
map '/javascripts' do
run Rack::Directory.new "javascripts"
end
Перезагрузите сервер и зайдите http://localhost:9292/javascript
и вы увидите список всех файлов javascript, которые вы можете включить прямо сейчас из любого места.
У меня были проблемы с пониманием того, что я стою в течение достаточно долгого времени. Я полностью понял это только после того, как сам поработал над созданием этого миниатюрного веб-сервера Ruby. Я поделился своими знаниями о стойке (в форме рассказа) здесь, в моем блоге: http://gauravchande.com/what-is-rack-in-ruby-rails
Обратная связь более чем приветствуется.
Что такое стойка?
Rack обеспечивает минимальный интерфейс между веб-серверами, поддерживающими Ruby и платформы Ruby.
Используя Rack, вы можете написать приложение Rack.
Rack передает хэш Environment (Hash, содержащийся внутри HTTP-запроса от клиента, состоящего из CGI-подобных заголовков) в ваше приложение Rack, которое может использовать вещи, содержащиеся в этом хэш-функции, для выполнения чего угодно.
Что такое приложение для стойки?
Чтобы использовать Rack, вы должны предоставить "приложение" - объект, который отвечает на #call
метод с хешем среды в качестве параметра (обычно определяется как env
). #call
должен вернуть массив из ровно трех значений:
- Код состояния (например, "200"),
- Хэш заголовков,
- тело ответа (которое должно отвечать методу Ruby,
each
).
Вы можете написать Rack Application, который возвращает такой массив - он будет отправлен обратно вашему клиенту Rack внутри Response (на самом деле это будет экземпляр класса Rack::Response
[нажмите, чтобы перейти к документам]).
Очень простое приложение в стойке:
gem install rack
- Создать
config.ru
file - Rack знает, как искать это.
Мы создадим крошечное приложение Rack, которое возвращает ответ (экземплярRack::Response
) Ответное тело - это массив, содержащий строку:"Hello, World!"
,
Мы запустим локальный сервер с помощью командыrackup
,
При посещении соответствующего порта в нашем браузере мы увидим "Hello, World!" отображается в окне просмотра.
#./message_app.rb
class MessageApp
def call(env)
[200, {}, ['Hello, World!']]
end
end
#./config.ru
require_relative './message_app'
run MessageApp.new
Запустите локальный сервер сrackup
и посетите http://localhost:9292/, и вы должны увидеть "Привет, мир!" оказаны.
Это не исчерпывающее объяснение, но, по сути, здесь происходит то, что Клиент (браузер) отправляет HTTP-запрос в Rack через ваш локальный сервер, и Rack создает экземплярыMessageApp
и работаетcall
, передав хэш среды в качестве параметра в метод (env
аргумент).
Rack принимает возвращаемое значение (массив) и использует его для создания экземпляраRack::Response
и отправляет это обратно клиенту. Браузер использует магию, чтобы напечатать "Hello, World!" на экран.
Кстати, если вы хотите увидеть, как выглядит хеш среды, просто поставьтеputs env
подdef call(env)
,
Как минимум, то, что вы написали здесь, является приложением Rack!
Создание приложения Rack для взаимодействия с хэшем Incoming Environment
В нашем маленьком приложении Rack мы можем взаимодействовать сenv
hash (см. здесь для получения дополнительной информации о hash Environment).
Мы реализуем возможность для пользователя вводить свою собственную строку запроса в URL, следовательно, эта строка будет присутствовать в HTTP-запросе, инкапсулируясь как значение в одну из пар ключ / значение хэша Environment.
Наше приложение Rack получит доступ к этой строке запроса из хэша Environment и отправит ее обратно клиенту (в нашем случае, нашему браузеру) через тело в ответе.
Из документации Rack по хэшу среды:"QUERY_STRING: часть URL запроса, которая следует за?, Если есть. Может быть пустой, но всегда обязательна!"
#./message_app.rb
class MessageApp
def call(env)
message = env['QUERY_STRING']
[200, {}, [message]]
end
end
Сейчас,rackup
и посетитьlocalhost:9292?hello
(?hello
является строкой запроса), и вы должны увидеть 'hello' в окне просмотра.
Стойка Middleware
Мы будем:
- вставьте часть промежуточного программного обеспечения Rack в нашу кодовую базу - класс:
MessageSetter
, - Хеш окружения сначала попадет в этот класс и будет передан как параметр:
env
, MessageSetter
вставит'MESSAGE'
ключ в хеш env, его значение'Hello, World!'
еслиenv['QUERY_STRING']
пустой;env['QUERY_STRING']
если не,- наконец вернется
@app.call(env)
-@app
быть следующим приложением в "стеке":MessageApp
,
Во-первых, версия "с длинными руками":
#./middleware/message_setter.rb
class MessageSetter
def initialize(app)
@app = app
end
def call(env)
if env['QUERY_STRING'].empty?
env['MESSAGE'] = 'Hello, World!'
else
env['MESSAGE'] = env['QUERY_STRING']
end
@app.call(env)
end
end
#./message_app.rb (same as before)
class MessageApp
def call(env)
message = env['QUERY_STRING']
[200, {}, [message]]
end
end
#config.ru
require_relative './message_app'
require_relative './middleware/message_setter'
app = Rack::Builder.new do
use MessageSetter
run MessageApp.new
end
run app
Из документов Rack::Builder видно, что Rack::Builder
реализует небольшой DSL для итеративного конструирования приложений Rack. По сути, это означает, что вы можете создать "стек", состоящий из одного или нескольких промежуточных программ и приложения "нижнего уровня" для отправки. Все запросы, поступающие в ваше приложение нижнего уровня, будут сначала обрабатываться вашим Middleware(s).
#use
указывает промежуточное программное обеспечение для использования в стеке. Он принимает промежуточное программное обеспечение в качестве аргумента.
Стойка Middleware должна:
- есть конструктор, который принимает следующее приложение в стеке в качестве параметра.
- ответить на
call
метод, который принимает хеш среды в качестве параметра.
В нашем случае, MiddlewareMessageSetter
"Конструктор" - это MessageSetter's initialize
метод, "следующее приложение" в стекеMessageApp
,
Так вот, из-за чего Rack::Builder
делает под капотомapp
аргументMessageSetter
"sinitialize
методMessageApp
,
(обдумайте вышеизложенное, прежде чем двигаться дальше)
Таким образом, каждый компонент Middleware по существу "передает" существующий хэш среды следующему приложению в цепочке, поэтому у вас есть возможность изменить этот хеш среды в Middleware, прежде чем передать его следующему приложению в стеке.
#run
принимает аргумент, который является объектом, который отвечает на#call
и возвращает ответ стойки (экземплярRack::Response
).
Выводы
С помощьюRack::Builder
вы можете создавать цепочки промежуточного программного обеспечения, и каждый запрос к вашему приложению будет обрабатываться каждым промежуточным ПО по очереди, прежде чем окончательно обрабатывается последней частью стека (в нашем случаеMessageApp
). Это чрезвычайно полезно, поскольку разделяет различные этапы обработки запросов. С точки зрения "разделения интересов", это не может быть намного чище!
Вы можете создать "конвейер запросов", состоящий из нескольких промежуточных программ, которые имеют дело с такими вещами, как:
- Аутентификация
- авторизация
- Кэширование
- Украшение
- Мониторинг производительности и использования
- Выполнение (фактически обработать запрос и предоставить ответ)
(выше пул из другого ответа в этой теме)
Вы часто будете видеть это в профессиональных приложениях Sinatra. Синатра использует Rack! Смотрите здесь для определения того, что Синатра есть!
В заключение, наш config.ru
может быть написан в стиле сокращения, производя точно такую же функциональность (и это то, что вы обычно видите):
require_relative './message_app'
require_relative './middleware/message_setter'
use MessageSetter
run MessageApp.new
И чтобы показать более подробно, что MessageApp
делает, вот его "длинная рука" версия, которая явно показывает, что #call
создает новый экземпляр Rack::Response
с необходимыми тремя аргументами.
class MessageApp
def call(env)
Rack::Response.new([env['MESSAGE']], 200, {})
end
end
Полезные ссылки
Rack - это драгоценный камень, который предоставляет простой интерфейс для абстрактного HTTP-запроса / ответа. Стойка находится между веб-фреймворками (Rails, Sinatra и т. Д.) И веб-серверами (unicorn, puma) в качестве адаптера. На изображении выше это делает сервер-единорог полностью независимым от знания о рельсах, а рельсы не знают о единороге. Это хороший пример слабой связи, разделения проблем.
Выше изображение из выступления на конференции rails на стойке https://youtu.be/3PnUV9QzB0g Я рекомендую посмотреть его для более глубокого понимания.
config.ru
минимальный работоспособный пример
app = Proc.new do |env|
[
200,
{
'Content-Type' => 'text/plain'
},
["main\n"]
]
end
class Middleware
def initialize(app)
@app = app
end
def call(env)
@status, @headers, @body = @app.call(env)
[@status, @headers, @body << "Middleware\n"]
end
end
use(Middleware)
run(app)
Бежать rackup
и посетить localhost:9292
, Выход:
main
Middleware
Итак, ясно, что Middleware
оборачивает и вызывает главное приложение. Поэтому он может предварительно обработать запрос и обработать ответ любым способом.
Как объяснено по адресу: http://guides.rubyonrails.org/rails_on_rack.html, Rails использует промежуточное программное обеспечение Rack для большей части его функциональных возможностей, и вы также можете добавить свое собственное с помощью config.middleware.use
семейные методы.
Преимущество реализации функциональности в промежуточном программном обеспечении состоит в том, что вы можете повторно использовать его на любой платформе Rack, то есть на всех основных Ruby, а не только на Rails.
Промежуточное программное обеспечение Rack - это способ фильтрации запросов и ответов, поступающих в ваше приложение. Компонент промежуточного программного обеспечения находится между клиентом и сервером, обрабатывая входящие запросы и исходящие ответы, но это больше, чем интерфейс, который можно использовать для связи с веб-сервером. Он используется для группировки и упорядочения модулей, которые обычно являются классами Ruby, и определения зависимости между ними. Модуль промежуточного программного обеспечения Rack должен только: - иметь конструктор, который принимает следующее приложение в стеке в качестве параметра - отвечать на метод "call", который принимает хэш среды в качестве параметра. Возвращаемое значение из этого вызова является массивом: кода состояния, хэша среды и тела ответа.
Я использовал промежуточное ПО Rack для решения нескольких проблем:
- Поймать ошибки синтаксического анализа JSON с помощью специального промежуточного программного обеспечения Rack и вернуть красиво отформатированные сообщения об ошибках, когда клиент отправляет отключенный JSON
- Сжатие контента через Rack::Deflater
Это дало довольно элегантные исправления в обоих случаях.
Rack - Интерфейс ч / б Web & App Server
Rack - это пакет Ruby, который предоставляет интерфейс для взаимодействия веб-сервера с приложением. Легко добавить компоненты промежуточного программного обеспечения между веб-сервером и приложением, чтобы изменить поведение вашего запроса / ответа. Компонент промежуточного программного обеспечения находится между клиентом и сервером, обрабатывая входящие запросы и исходящие ответы.
По словам непрофессионала, это в основном просто набор рекомендаций о том, как сервер и приложение Rails (или любое другое веб-приложение Ruby) должны общаться друг с другом.
Чтобы использовать Rack, предоставьте "приложение": объект, который отвечает на метод вызова, принимая хэш среды в качестве параметра и возвращая массив с тремя элементами:
- Код ответа HTTP
- Хеш заголовков
- Тело ответа, которое должно отвечать на каждый запрос.
Для более подробного объяснения вы можете перейти по ссылкам ниже.
1. https://rack.github.io/
2. https://redpanthers.co/rack-middleware/
3. https://blog.engineyard.com/2015/understanding-rack-apps-and-middleware
4. https://guides.rubyonrails.org/rails_on_rack.html#resources
В rails у нас есть config.ru в виде файла стойки, вы можете запустить любой файл стойки с rackup
команда. И порт по умолчанию для этого 9292
, Чтобы проверить это, вы можете просто запустить rackup
в вашей директории rails и посмотрите результат. Вы также можете назначить порт, на котором вы хотите его запустить. Команда для запуска файла стойки на любом конкретном порту
rackup -p PORT_NUMBER