Что такое промежуточное программное обеспечение для стойки?

267

Что такое промежуточное программное обеспечение Rack в Ruby? Я не смог найти хорошего объяснения того, что они подразумевают под «промежуточным ПО».

chrisgoyal
источник
4
Существует также руководство по RailsGuide, которое теперь полностью охватывает Rack, включая промежуточное ПО: guides.rubyonrails.org/rails_on_rack.html
xji
Большое спасибо команде PhusionPassenger, у них есть хорошо объясненная статья в их блоге. rubyraptor.org/...
Ламийский
Стойки и стойки промежуточного слоя объясняются в ЭТОЙ статье. Также рассказано о создании стоечного приложения.
Шашват Шривастава

Ответы:

353

Стойка как дизайн

Промежуточное программное обеспечение Rack - это больше, чем «способ фильтрации запросов и ответов» - это реализация шаблона проектирования конвейера для веб-серверов, использующих Rack .

Он очень четко разделяет различные этапы обработки запроса - разделение задач является ключевой целью всех хорошо разработанных программных продуктов.

Например, с Rack у меня могут быть отдельные этапы конвейера:

  • Аутентификация : когда запрос поступает, правильны ли данные для входа в систему? Как я проверяю этот OAuth, HTTP Basic Authentication, имя / пароль?

  • Авторизация : «авторизован ли пользователь для выполнения этой конкретной задачи?», То есть безопасность на основе ролей.

  • Кэширование : обработал ли я этот запрос, могу ли я вернуть кэшированный результат?

  • Декорирование : как улучшить запрос, чтобы улучшить последующую обработку?

  • Мониторинг производительности и использования : какую статистику я могу получить из запроса и ответа?

  • Выполнение : фактически обработать запрос и предоставить ответ.

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

сообщество

Также вокруг Rack Middleware развивается отличная экосистема - вы сможете найти готовые компоненты стойки для выполнения всех вышеперечисленных шагов и многого другого. Посмотрите Rack GitHub wiki для получения списка промежуточного программного обеспечения .

Что такое Middleware?

Промежуточное программное обеспечение - это ужасный термин, который относится к любому программному компоненту / библиотеке, который помогает, но не участвует напрямую в выполнении какой-либо задачи. Очень распространенными примерами являются регистрация, аутентификация и другие распространенные компоненты горизонтальной обработки . Это, как правило, вещи, которые нужны всем в нескольких приложениях, но не слишком много людей заинтересованы (или должны быть) в создании самих себя.

Больше информации

Крис МакКоли
источник
Одна вещь, по которой мне неясно: все ли промежуточные программы используют одни и те же данные? Можно ли разделить их (например, песочницу один) для безопасности?
Брайан Армстронг
2
Rack является частью вашего приложения, поэтому все промежуточное ПО составляет одну и ту же копию запроса, и каждый может изменить ее так, как хочет. AFAIK, нет способа изолировать их друг от друга так же, как нет способа изолировать один объект от другого в рамках одного и того же процесса (несмотря на попытки изолированной обработки Ruby).
Крис МакКоули
1
и понимаю, что Rack отличается от Rake.
Маниш Шривастава
1
Мне нравится думать о промежуточном программном обеспечении как о чем-то, что находится в середине моего приложения между тем, что я кодировал, и тем, что идет на мой сервер и с него ... которое размещено в стойке. Как мы все знаем, термин «промежуточное программное обеспечение стойки» сбивает с толку, потому что именно Конфуций написал все оригинальное промежуточное программное обеспечение стойки более 2000 лет назад. Во Франции.
LpLrich
74

Прежде всего, 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, которые вы можете включить прямо сейчас из любого места.

Томас Фанкхаузер
источник
3
Но не промежуточное программное обеспечение стойки?
Rup
1
Если вы не знаете, что такое стойка, вы точно узнаете, что это такое и как ее использовать, прочитав этот пост в блоге. Очень хорошо. По иронии судьбы, ссылка на официальную документацию стойки в конце поста больше не доступна!
Колин
Ваше право, спасибо. Я включил содержимое в пост и удалил мертвую ссылку.
Томас Фанкхаузер
Я бы сказал, что это не конвенция. это интерфейс, контракт, хорошо определенный для модели запрос-ответ
Рон Кляйн
20

У меня были проблемы с пониманием того, что я стою в течение достаточно долгого времени. Я полностью понял это только после того, как сам поработал над созданием этого миниатюрного веб-сервера Ruby . Я поделился своими знаниями о стойке (в форме рассказа) здесь, в моем блоге: http://gauravchande.com/what-is-rack-in-ruby-rails

Обратная связь более чем приветствуется.

Гаурав Чанде
источник
13
Ответы, содержащие только ссылки, не рекомендуется использовать при переполнении стека , потому что если ресурс, на который идет ссылка, станет недоступным в будущем, ответ станет бесполезным. Пожалуйста, по крайней мере, суммируйте соответствующие пункты вашего блога и добавьте их к этому ответу.
Спасибо за ваш пост. Я очень начинающий программист на Rails, и я понял концепцию стоек из вашего чистого поста.
Эдуардо Рамос
Отличный пост в блоге. Другие ответы кажутся немного более запутанными IMO.
Моллюск
Какое потрясающее объяснение. Спасибо, Гаурав.
Ровитулли
7

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#action-dispatcher-middleware-stack , Rails использует промежуточное программное обеспечение Rack для большей части его функциональных возможностей, и вы также можете добавить свое собственное с помощью config.middleware.useметодов семейства.

Преимущество реализации функциональности в промежуточном программном обеспечении состоит в том, что вы можете повторно использовать его на любой платформе Rack, то есть на всех основных Ruby, а не только на Rails.

Сиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
источник
6

Промежуточное программное обеспечение Rack - это способ фильтрации запросов и ответов, поступающих в ваше приложение. Компонент промежуточного программного обеспечения находится между клиентом и сервером, обрабатывая входящие запросы и исходящие ответы, но это больше, чем интерфейс, который можно использовать для связи с веб-сервером. Он используется для группировки и упорядочения модулей, которые обычно являются классами Ruby, и определения зависимости между ними. Модуль промежуточного программного обеспечения Rack должен только: - иметь конструктор, который принимает следующее приложение в стеке в качестве параметра - отвечать на метод «call», который принимает хеш среды в качестве параметра. Возвращаемое значение из этого вызова является массивом: кода состояния, хэша среды и тела ответа.

L.Cole
источник
4

Я использовал промежуточное ПО Rack для решения нескольких проблем:

  1. Улавливание ошибок синтаксического анализа JSON с помощью специального промежуточного программного обеспечения Rack и возврат красиво отформатированных сообщений об ошибках, когда клиент отправляет отключенный JSON
  2. Сжатие контента через Rack :: Deflater

Это дало довольно элегантные исправления в обоих случаях.

DJCP
источник
2
Этот ответ, хотя и несколько полезный, на самом деле не затрагивает вопрос о том, что такое Rack Middleware .
Кроме того, этот ответ только для ссылок ...: P
Smar
4

Что такое стойка?

Rack обеспечивает минимальный интерфейс между веб-серверами, поддерживающими платформы Ruby и Ruby.

Используя Rack, вы можете написать приложение Rack.

Rack передает хэш Environment (Hash, содержащийся внутри HTTP-запроса от клиента, состоящего из CGI-подобных заголовков) в ваше приложение Rack, которое может использовать вещи, содержащиеся в этом хэш-функции, для выполнения чего угодно.

Что такое приложение для стойки?

Чтобы использовать Rack, вы должны предоставить «приложение» - объект, который отвечает на #callметод с помощью Hash среды в качестве параметра (обычно определяется как env). #callдолжен вернуть массив из ровно трех значений:

  • код состояния (например , «200»),
  • Хэш заголовков ,
  • Response Body (который должен ответить методом на Ruby each).

Вы можете написать приложение Rack, которое возвращает такой массив - оно будет отправлено обратно вашему клиенту Rack внутри Response (на самом деле это будет экземпляр класса Rack::Response[щелкните, чтобы перейти к документам]).

Очень простое приложение для стойки:

  • gem install rack
  • Создайте config.ruфайл - 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и посетите localhost: 9292, и вы должны увидеть «Hello, World!» оказаны.

Это не исчерпывающее объяснение, но, по сути, здесь происходит то, что Клиент (браузер) отправляет HTTP-запрос в Rack через ваш локальный сервер, а Rack создает MessageAppи запускает его call, передавая Хэш среды как параметр в метод ( envаргумент).

Rack принимает возвращаемое значение (массив) и использует его для создания экземпляра Rack::Responseи отправляет его обратно клиенту. Браузер использует магию, чтобы напечатать «Hello, World!» на экран.

Между прочим, если вы хотите увидеть, как выглядит хеш среды, просто поместите puts envпод ним def call(env).

Как минимум, то, что вы написали здесь, это приложение Rack!

Создание приложения Rack для взаимодействия с хэшем Incoming Environment

В нашем маленьком приложении Rack мы можем взаимодействовать с envхэшем (см. Здесь для получения дополнительной информации о хэше 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 Middleware в нашу кодовую - класс: MessageSetter,
  • хэш - среда ударит этот класс первым и будет принят в качестве параметра: env,
  • MessageSetterвставит 'MESSAGE'ключ в окр хэш, его значение существо , '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метод, который принимает хэш Environment в качестве параметра.

В нашем случае «Middleware» - MessageSetterэто «конструктор» - это initializeметод MessageSetter , «следующее приложение» в стеке MessageApp.

Так вот, из - за того, что Rack::Builderделает под капотом, appаргумент MessageSetter«S initializeметода является MessageApp.

(обдумайте вышеизложенное, прежде чем двигаться дальше)

Таким образом, каждый компонент Middleware по существу «передает» существующий хэш среды следующему приложению в цепочке, поэтому у вас есть возможность изменить этот хэш среды в Middleware, прежде чем передать его следующему приложению в стеке.

#runпринимает аргумент, который является объектом, который отвечает #callи возвращает Rack Response (экземпляр Rack::Response).

Выводы

Используя его, Rack::Builderвы можете создавать цепочки промежуточного программного обеспечения, и любой запрос к вашему приложению будет обрабатываться каждым промежуточным ПО по очереди, прежде чем окончательно обрабатывается последним элементом в стеке (в нашем случае, MessageApp). Это чрезвычайно полезно, поскольку разделяет различные этапы обработки запросов. С точки зрения «разделения интересов», это не может быть намного чище!

Вы можете создать «конвейер запросов», состоящий из нескольких промежуточных программ, которые имеют дело с такими вещами, как:

  • Аутентификация
  • авторизация
  • Кэширование
  • Украшение
  • Мониторинг производительности и использования
  • Выполнение (на самом деле обработать запрос и предоставить ответ)

(выше пул из другого ответа в этой теме)

Вы часто будете видеть это в профессиональных приложениях Sinatra. Синатра использует Rack! Смотрите здесь для определения того , что Синатра IS !

В заключение отметим, что наша версия 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

Полезные ссылки

йоркширец
источник
1

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
ВК Сингх
источник
1

изображение, показывающее стойку между единорогом и рельсами

Rack - это гем, который предоставляет простой интерфейс для абстрактного HTTP-запроса / ответа. Rack находится между веб-фреймворками (Rails, Sinatra и т. Д.) И веб-серверами (Unicorn, Puma) в качестве адаптера. На приведенном выше изображении сервер единорога полностью независим от знания о рельсах, а рельсы не знают о единороге. Это хороший пример слабой связи , разделения интересов .

Выше изображение из этой конференции по рельсам. Обсуждение на стойке https://youtu.be/3PnUV9QzB0g Я рекомендую посмотреть его для более глубокого понимания.

ВБП
источник