Статьи

Rack-App: совершенная и прагматичная веб-микрофрейм

Снимок экрана 2016-11-10 в 12.48.47 PM

Это фантастическое время, чтобы стать веб-разработчиком и использовать Ruby. Ruby on Rails проложил путь к современной веб-разработке, но при этом высветил определенные недостатки. Его подход «кухонная мойка» иногда может быть излишним, особенно для небольших проектов, что привело к быстрому распространению и, возможно, золотому веку микрорамок Ruby. Успех Синатры показывает, что существует реальный спрос на него и его аналог, и их число увеличивается каждые несколько месяцев.

Почему так много фреймворков? Отчасти потому, что замечательный Rack позволяет невероятно легко любому, кто имеет базовые знания TCP / IP, развертывать свои собственные фреймворки, а отчасти потому, что по определению микрофреймы являются самоуверенными. Результатом является то, что эти мнения приводят к царапинам без царапин, которые в сочетании с низким барьером для входа привели к появлению множества микрофрейм на рынке. Зачем нам нужен еще один? Ну, мы не. На данный момент, довольно хорошо каждый современный вариант использования веб-разработки удовлетворяется чем-то, что существует. Так зачем кому-то интересоваться Rack-App ? Ну, это легко. Это основа, которая питает микросервисы в Heroku .

Основные принципы

Стойка самоуверенная. Вот основные моменты:

  • Нет метапрограммирования — это не та структура, которая будет держать вас за руку, но она также не принесет вам неприятных сюрпризов.
  • Performant — мы разберемся с этим чуть позже, но эта структура масштабируется.
  • Простота — Раздувание кода? Зависимости? Не здесь.
  • Акцент на тестировании — Rack поддерживает Behavior Driven Development (BDD) и гордится простотой интеграции.
  • Модульный — Rack-App предоставляет вам минимум для начала. Есть плагины, доступные , но не включены.

Звучит привлекательно? Давайте посмотрим, что действительно отличает его от всего остального прямо сейчас: производительность.

Производительность

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

Это не убийственная особенность, хотя. Отличительной особенностью является то, что Rack-App будет удобно обслуживать более 10 000 конечных точек (столько, сколько вы можете уместить в память) с постоянным поиском по времени. Давайте отвлечемся на минуту, чтобы увидеть, как он достигает такой производительности.

Постоянное время поиска

Давайте представим, что у нас более 10000 конечных точек. Как это возможно, что время поиска является постоянным? Что ж, давайте посмотрим на источник. Мы знаем, что это может быть связано с маршрутизацией, поэтому взгляните на маршрутизатор здесь . Вы можете видеть, что он использует наш старый друг хеш , который, как мы все теперь знаем, имеет постоянное время поиска. Не совсем «предполагаемое время поиска», но, тем не менее, впечатляет при обслуживании такого количества конечных точек.

Также интересно то, что вы можете задавать конечные точки пространства имен. Это означает, что вам придется работать довольно усердно, чтобы иметь слишком много конечных точек, но ваш код, естественно, будет СУХИМ (если вы не работаете с ним) и все равно выиграет от быстрого поиска.

Простота

Одним из преимуществ многих Ruby Framework является то, что мы видели, что работает, а что нет. Когда в последний раз у вас был какой-то загадочный синтаксис или грязный API в среде Ruby? Rack-App не является исключением. DSL сильно заимствует у (отличного) Sinatra (а также Grape ), что означает, что новые пользователи должны чувствовать себя как дома со знакомым и лаконичным синтаксисом. Лучше всего то, что, снижая таким образом барьер для входа, на самом деле нет никакого оправдания тому, чтобы не тратить пару часов на то, чтобы собрать этот фреймворк и добавить в свой программный арсенал.

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

тестирование

Чтобы защитить себя от исключений, Rack-App (например, Rails) превозносит тестирование. Behavior Driven Development (BDD) — оружие выбора, и test Фреймворк полностью протестирован и, следовательно, теоретически прост для добавления интеграционного тестирования в ваше приложение — просто требуется модуль в вашей спецификации.

В бою

Этого достаточно о его сильных сторонах; давайте попробуем немного кода!

Вы знаете, что делать:

gem install rack-app

Вот вывод:

 Fetching: rack-2.0.1.gem (100%)
Successfully installed rack-2.0.1
Fetching: rack-app-5.5.1.gem (100%)
Successfully installed rack-app-5.5.1
Parsing documentation for rack-2.0.1
Installing ri documentation for rack-2.0.1
Parsing documentation for rack-app-5.5.1
Installing ri documentation for rack-app-5.5.1
Done installing documentation for rack, rack-app after 4 seconds
2 gems installed

Одна зависимость: стойка . Они действительно не шутят, когда говорят, что они слабы в зависимости.

Давайте разберемся с «Hello World»:

 # config.ru
require 'rack/app'

class Racko < Rack::App
  get '/' do
    "Hello World!"
  end
end

run Racko

Запустите config.ru с сервера (я предпочитаю отличный Shotgun из-за возможности rackup config.ruHello World!localhost: 9393 или localhost: 9292 , в зависимости от вашего выбора. Вы должны увидеть ваше приветствие:

run

Пока ничего примечательного. Наследование классов, синтаксис DSL, позаимствованный у Синатры, и вызов обработчика Rack. Давайте копать немного глубже.

Образец заявки

Давайте сделаем шаг вперед немного. Более содержательный пример может быть взят с домашней страницы проекта . Для тех, кто играет дома, создайте следующие файлы: config.ru , медиа- файл server.rb и file_uploader.rb . Для ленивых

 wget {https://gist.githubusercontent.com/adamluzsi/badf3ac5d40db335b45972aca4b30cd8/raw/cad2e1b137f94cc1217348283b0058524fc71bbc/config.ru,https://gist.githubusercontent.com/adamluzsi/badf3ac5d40db335b45972aca4b30cd8/raw/cad2e1b137f94cc1217348283b0058524fc71bbc/media_file_server.rb,https://gist.githubusercontent.com/adamluzsi/badf3ac5d40db335b45972aca4b30cd8/raw/cad2e1b137f94cc1217348283b0058524fc71bbc/uploader.rb}

Вот встроенные файлы:

 # config.ru
require 'json'
require 'rack/app'
class MyApp < Rack::App

  headers 'Access-Control-Allow-Origin' => '*',
          'Access-Control-Expose-Headers' => 'X-My-Custom-Header, X-Another-Custom-Header'

  serializer do |obj|
    if obj.is_a?(String)
      obj
    else
      JSON.dump(obj)
    end
  end

  error StandardError, NoMethodError do |ex|
    { error: ex.message }
  end

  get '/bad/endpoint' do
    no_method_error_here
  end

  desc 'hello world endpoint'
  validate_params do
    required 'words', class: Array, of: String,
                      desc: 'words that will be joined with space',
                      example: %w(dog cat)
    required 'to', class: String,
                   desc: 'the subject of the conversation'
  end
  get '/validated' do
    return "Hello #{validated_params['to']}: #{validated_params['words'].join(' ')}"
  end

  get '/' do
    { hello: 'world' }
  end

  mount MediaFileServer, to: "/assets"
  mount Uploader, to: '/upload'

end

# for more check out how-to
run MyApp

# media_file_server.rb
class MediaFileServer < Rack::App

  serve_files_from '/folder/from/project/root', to: '/files'

  get '/' do
    serve_file 'custom_file_path_to_stream_back'
  end
end

# uploader.rb
require 'fileutils'
class Uploader < Rack::App

  post '/to_stream' do
    payload_stream do |string_chunk|
      # do some work
    end
  end

  post '/upload_file' do
    file_path = Rack::App::Utils.pwd('/upliads', params['user_id'], params['file_name'])
    FileUtils.mkdir_p(file_path)
    payload_to_file(file_path)
  end

  post '/memory_buffered_payload' do
    payload #> request payload string
  end

end

Это упражнение, оставленное читателю, чтобы поиграть с ним, но давайте быстро взглянем на config.ru, где можно найти суть интересных частей:
— Заголовки, установленные как хэш — это, очевидно, основа, ориентированная на использование API.
— Сериализатор — это красиво захватывает Rack-App: если вам что-то нужно, вам лучше подготовиться к выпуску своего.
— Ошибки — Rack-App гордится унифицированным интерфейсом обработки ошибок; ничего особенного, но вы также не имеете дело с слишком большой абстракцией.

Вывод

Rack-App находится на ранней стадии. Это уже столкнулось с небольшим противоречием, и изменения делаются все время . Одним из спорных недостатков является то, что имя почти невозможно для Google. К счастью, документация просто фантастическая, а на домашней странице есть меню HOW-TO Лично я ценю лаконичный стиль, особенно из многословного эквивалента Python, потому что легко быстро получить продуктивность.

Наконец, важно отметить, что микросхемы, избегая зависимостей, допускают большую разницу в способах ведения дел. Если вы привыкли к соглашению Rails о конфигурации и подходе кухонной раковины, то вам следует предупредить, что существует компромисс с точки зрения долгосрочной ремонтопригодности. Тем не менее, преимущества микрорамок многочисленны, и спрос не исчезнет в ближайшее время. Если вам нужен высокопроизводительный и закаленный в боях бэкэнд для обслуживания конечных точек API, и все же, при быстром достижении разумного уровня производительности, Rack-App, на мой взгляд, является единственным реальным выбором.