...

четверг, 5 июня 2014 г.

Интеграция 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://ift.tt/1oepIgK

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




Как видите, есть почти все необходимое для работы. Единственное, что не обнаружил в API данного модуля, это возможности делать запрос наружу. Скорее всего, его нужно будет реализовать как расширение и сделать обвязку вокруг nginx API.

Автор показывает красивый график с тестами, но конфигурации окружения так и не нашел. Поэтому просто приложу его для красоты:

image


Попробуем использовать




Итак, сервер у нас уже установлен. Все функционирует, статика отдается. Добавим немного к этому динамики.

В качестве примера я выбрал задачу по парсингу 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;
}




Результат:

http://ift.tt/SwCbjN

http://ift.tt/1oepJRJ
Файлы Ruby



Первоначально планировал сделать не просто нумерацию, а так же раскраску кода, используя когда-то написанный код http://ift.tt/SwCdrH. Однако после всех произведенных адаптаций в его работе возникли проблемы. Код, вроде, работал, но на определенном этапе падал с 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\\?|



This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Комментариев нет:

Отправить комментарий