GoLang с Rails
GoLang с Rails? Не знаете, зачем использовать GoLang с Rails?
Это основано исключительно на наших требованиях, но, безусловно, может принести пользу другим, ожидающим аналогичного варианта использования.
У нас было веб-приложение, написанное на Rails, но мы столкнулись с проблемой производительности. Кажется, что единственный выбор – использовать мощь параллелизма GoLang.
Чтобы использовать GoLang с нашим приложением Rails, мне в голову пришло несколько подходов. Сейчас опишу вам 3 подхода:
- Напишите api в приложении GoLang и направьте запрос от nginx на основе URL-адреса запроса. Это просто, но для использования этого подхода нам также потребуется добавить аутентификацию в приложении GoLang. Таким образом, аутентификация будет как в Rails, так и в GoLang – это не кажется правильным, потому что, если бы мне пришлось изменить механизм аутентификации, пришлось бы внести изменения в двух приложениях.
- Используйте RestClient и вызовите API GoLang из приложения Rails. Таким образом, запрос будет перенаправлен в приложение Rails, и оно вызовет api из приложения GoLang и отправит ответ. Здесь я добьюсь определенного уровня производительности, но опять же мое приложение Rails должно будет обслуживать запрос, которое приложение GoLang может обслуживать напрямую, а ответ должен ждать ответа от приложения GoLang.
- Используйте FFI. Используя FFI, мы можем напрямую вызывать двоичный файл GoLang. Вы можете посмотреть это видео, чтобы увидеть, как это можно сделать. Сначала это кажется нормальным, но что, если мне пришлось бы балансировать нагрузку, перемещая приложение GoLang на другой сервер?
Какому подходу следовал я?
Лично я использовал 4-ю идею, используя rack_proxy, которую не описал выше, так как она имеет более подробного изложения.
Начнём с примера кода для связующего ПО, которое мы написали:
class EventServiceProxy < Rack::Proxy
def initialize(app)
@app = app
end
def call(env)
original_host = env["HTTP_HOST"]
rewrite_env(env)
if env["HTTP_HOST"] != original_host
perform_request(env)
else
@app.call(env)
end
end
def rewrite_env(env)
request = Rack::Request.new(env)
if request.path.match('/events')
if env['warden'].authenticated?
env["HTTP_HOST"] = "localhost:8000"
env['HTTP_AUTHORIZATION'] = env['rack.session']['warden.user.user.key'][0]
end
env
end
end
end
Далее мы использовали наше ПО сразу после Warden (девайс использует его для внутренней аутентификации)
config.middleware.insert_after(Warden::Manager, EventServiceProxy)
В приведенном выше фрагменте кода мы просто проксируем наш запрос на localhost: 8000, где запущено приложение GoLang, и настраиваем user_id в заголовке. Warden добавляет аутентифицированный user_id в env [‘rack.session’] [‘warden.user.user.key’] [0], поэтому теперь мы знаем, кто вошел в приложение GoLang из заголовка.
Мы добавили в GoLang промежуточное ПО, которое извлекает user_id из заголовка и устанавливает детали curretUser в контексте.
Важное замечание
Наше приложение GoLang доступно только для приложения Rails, а не для всего интерета, и что бы не столкнутся с данной проблемой, вставляем user_id в заголовке.
Основные преимущества, которые мы увидели при использовании этого подхода:
- Мы смогли использовать существующий механизм аутентификации, используемый в приложении Rails.
- При необходимости мы можем добавить балансировщик нагрузки в наше приложение Rails или GoLang, которое является микросервисом.
- Если бы мы использовали FFI, нам пришлось бы разместить двоичный файл на одном компьютере, но здесь у нас может быть приложение и служба GoLang на разных машинах.
- Поскольку запрос будет перезаписан из Rack, он сохранит перенаправление и проходит через весь стёк приложения rails.
Это можно использовать с любым фреймворком, подобным Rails. Используя вышеупомянутый подход, теперь мы можем использовать мощность GoLang, когда это необходимо, и скорость разработки Rails