Интеграция Ruby в Nginx
Уже достаточно давно существует всем известная связка Nginx + Lua, в том числе здесь был ряд статей. Но время не стоит на месте. Примерно год назад появилась первая версия модуля, интегрирующего Ruby в Nginx.MRubyДля интеграции был выбран не полноценный Ruby, а его подмножество, которое предназначено для встраивания в другие приложения устройства и тд. Имеет некоторые ограничения, но в остальном полноценный Ruby. Проект называется MRuby. На текущий момент имеет уже версию 1.0.0, т.е. считается стабильным.MRuby не позволяет подключать другие файлы во время выполнения, поэтому вся программа должна быть в одном файле. При этом есть возможность преобразовать программу в байткод и выполнять уже его, что положительно сказывается на производительности.Т.к. нет возможности подгружать другие файлы, то и существующие gem-ы не подходят для него. Для расширения функционала используется свой формат, который представляет из себя как C код, так и Ruby местами. Данные модули собираются вместе с самой библиотекой во время компиляции и являются ее неотъемлемой частью. Имеются биндинги к различным базам данных, для работы с файлами, сетью и так далее. Полный список доступен на сайте.Также там имеется модуль, позволяющий интегрировать данный движок в Nginx, который особенно заинтересовал.ngx_mruby Итак, знакомьтесь: ngx_mruby. Модуль для подключения ruby скриптов к nginx. Имеет схожий функционал с Lua версией. Позволяет выполнять операции на различных этапах обработки запроса.Модуль собирается довольно просто, на сайте есть подробная инструкция. Кто не хочет заморачиваться со сборкой, могут скачать готовый пакет: http://mruby.ajieks.ru/st/nginx_1.4.4–1~mruby~precise_amd64.debMRuby в данной сборке содержит следующие дополнительные модули:
Как видите, есть почти все необходимое для работы. Единственное, что не обнаружил в API данного модуля, это возможности делать запрос наружу. Скорее всего, его нужно будет реализовать как расширение и сделать обвязку вокруг nginx API.Автор показывает красивый график с тестами, но конфигурации окружения так и не нашел. Поэтому просто приложу его для красоты:
Попробуем использовать Итак, сервер у нас уже установлен. Все функционирует, статика отдается. Добавим немного к этому динамики.В качестве примера я выбрал задачу по парсингу Markdown разметки и отдачи ее в HTML без дополнительного серверного приложения. А также нумерации строк в исходниках на Ruby.Для этого сделан клон репозитория sinatra и настроен nginx для решения поставленной задачи.Markdown Для обработки разметки воспользуемся подключенным в сборку модулем mruby-discount. Он предоставляет простой класс для работы с разметкой. В основе лежит одноименная библиотека на C, потому вопрос производительности, думаю, особо стоять не будет.Для начала напишем программу, которая будет считывать запрошенный файл с диска, обрабатывать его и отдавать пользователю. r = Nginx: Request.new
m = Discount.new (»/st/style.css», «README»)
filename = r.filename filename = File.join (filename, 'README.md') if filename.end_with?('/')
markdown = File.exists?(filename) ? File.read (filename) : '' Nginx.rputs m.header Nginx.rputs m.md2html (markdown) Nginx.rputs m.footer Первой строкой получаем экземпляр объекта запроса, содержащий всю необходимую информацию, включая запрошенный файл, заголовки, URL, URI и т.д. Следующей строкой создаем экземпляр класса Discount, указывая файл стиля и заголовк страницы.Данный код не делает обработку 404 ошибки, поэтому даже если файла нету, всегда будет 200 код возврата.Подключаем теперь все это location ~ \.md$ { add_header Content-Type text/html; mruby_content_handler »/opt/app/parse_md.rb» cache; } Результат: mruby.ajieks.ru/sinatra/mruby.ajieks.ru/sinatra/README.ru.mdФайлы Ruby Первоначально планировал сделать не просто нумерацию, а так же раскраску кода, используя когда-то написанный код https://github.com/fuCtor/chalks. Однако после всех произведенных адаптаций в его работе возникли проблемы. Код, вроде, работал, но на определенном этапе падал с Segmentation fault. Первоначальное подозрение было на нехватку памяти выделяемой, но даже после уменьшения ее потребление проблема не пропала. После удаления кода, связанного с раскраской, все заработало, но не так красиво, как хотелось.Результат изменений module CGI TABLE_FOR_ESCAPE_HTML__ = {»&»=>»&», '»'=>»«,»<"=>»<", ">»=>»>»} def self.escapeHTML (string) string.gsub (/[&\»<>]/) do |ch| TABLE_FOR_ESCAPE_HTML__[ch] end end end
class String def ord self.bytes[0] end end
class Chalk
COMMENT_START_CHARS = { ruby: /#./, cpp: /\/\*|\/\//, c: /\/\// } COMMENT_END_CHARS = { cpp: /\*\/|.\n/, ruby: /.\n/, c: /.\n/, }
STRING_SEP = %w (' ») SEPARATORS = » @(){}[],.:;\»\'`<>=±*/\t\n\\?|