Размещение нескольких сервисов rails на одном сервере + архитектура веб-сайта с поддержкой API - PullRequest
6 голосов
/ 22 декабря 2011

Я только что закончил читать книгу Пола Дикса Сервис-ориентированный дизайн с RoR , и я хотел бы создать веб-приложение на Rails 3 на основе только что изученного.

Я думаю, что я правильно понял базовую архитектуру, но простой вопрос вроде как блокирует меня: как мне разместить несколько REST-сервисов на одном сервере?

Вот как я вижу вещи на данный момент:

  • Создание * Сервисных приложений (UserService, XYZFeatureService, ...) на основе Sinatra (я полагаю), которые предоставляют конечные точки REST для доступа к ресурсам
  • Иметь внешнее приложение Rails с контроллерами / представлениями / ..., которое использует данные из различных сервисов. Конечные пользователи могут получить к нему доступ, например, через http://www.myapp.com.
  • И наконец, есть отдельное приложение "API" для обработки вызовов на https://api.myapp.com/* или https://www.myapp.com/api/* для публикации внешнего API, который будет использовать те же сервисы с возможной аутентификацией, регулированием и т. Д. Поверх него.

Звучит ли это как хорошее начало для вас?

Что касается реализации, то из того, что я прочитал в книге, я планирую создавать гемы для связи между приложением rails и сервисами (я могу добавить немного RabbitMQ, но это уже другая история).

Однако, поскольку у меня есть только один физический сервер, мне интересно, как я собираюсь заставить все эти приложения / службы жить вместе? Мое первое предположение - запустить каждое сервисное приложение на localhost: xxxx, где xxxx - это отдельный непривилегированный порт для каждой службы. Я мог настроить каждый клиентский гем в моем приложении rails для использования этих портов.

Наряду с этим, я бы, вероятно, запустил Apache 2 + Passenger для обслуживания моего интерфейса rails и службы API, используя что-то вроде Rack :: URLMap (или виртуальные хосты, если используется поддомен) для направления запросов вправо. приложение. Должен ли я затем использовать Passenger для запуска своих служб в производственной среде?

Это правильный путь ?! Это похоже на то, что я прочитал и усвоил, и при необходимости легко разделяется на несколько физических серверов, но я хотел бы быть уверен, что я ничего не пропустил. Вы бы строили вещи по-другому?

Большое спасибо за ваш вклад!

Обновление

Основные вопросы, на которые я бы хотел ответить:

  • Подходит ли описанная архитектура для создания веб-приложения с внешними конечными точками API?
  • Можно ли запускать службы на одном сервере на разных портах?

Спасибо!

Ответы [ 2 ]

1 голос
/ 06 января 2015

Так что этому вопросу чуть больше 3 лет, и я думаю, что он мог бы выиграть от достаточно объективного ответа.

Забавно прочитать этот вопрос еще раз и увидеть, что за него недавно проголосовали, когда простые,«высокоуровневый» ответ - просто: делай, что хочешь / должен делать!

Нет волшебного правила, которое нужно соблюдать, хотя я предполагаю, что это то, что я искал в то время.Однако следует помнить о некоторых ключевых вещах:

  • Проектирование сервис-ориентированной архитектуры означает, что мы готовимся к масштабированию.Каждая служба предназначена для запуска самостоятельно и не зависит от того, выполняются ли они на том же сервере, что и другие службы стека. Не объединяйте свои службы, они должны оставаться независимыми

  • Однако не «переподготовьте» это: велик соблазн тратить много временипроектирование идеальной архитектуры, когда вам действительно нужно доставить ваш v1!

  • Когда вы создаете отдельные сервисы, не делайте их более сложными, чем нужно: простой вебстек с REST (-подобными) конечными точками, вероятно, будет достаточным для начала.RabbitMQ и другие очереди сообщений тоже хороши, но они решают проблемы, которых у вас может и не быть.

  • Что касается серверов, ну ... в идеальном мире, который вам нуженсервер на службу, все в центре обработки данных, с репликацией в течение второго (или более!) набора серверов в другом физически отделенном центре обработки данных ... это требует времени и денег для настройки и обслуживания.Если вы в большой организации, это может быть хорошо, но если это так, вам, вероятно, не нужно было читать этот ответ.
    Так что да, вы можете начать с малого!Один или два сервера, или «большой» с виртуализированными серверами… все зависит от вашей уверенности в администрировании объекта или от найма системного администратора.Одной машины может быть достаточно, и не стесняйтесь запускать на ней несколько служб, при условии, что все они могут совместно использовать одну и ту же систему и память.
    Сегодня я бы, вероятно, использовал nginx для отправки запросов в правильные службы, в зависимости отимена хостов или порты, а также запускать частные сервисы на разных портах с помощью брандмауэра (например, Shorewall) для блокировки запросов извне на эти порты.

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

1 голос
/ 23 декабря 2011

Я использую комбо Apache-Passenger и сценарий (см. Ниже), но я много читал о тестах, подталкивающих Node.JS к балансировщику нагрузки Nginx - и, по крайней мере, для предоставления API веб-сервисов, это может иметь смысл1001 *

Мой сценарий:

def build_a_new_oxenserver()
  site = siteurl.gsub(/\./,"_")
  system( "rake add_site['#{siteurl}','#{site}','#{id}']") if Rails.env.production?
  default_tmpl = open(File.expand_path(Rails.root + "public/default_template.html")).read
  tmpl = Template.create(:ox_id=>id, :name=>"first template", :content=>default_tmpl)
  pg=Page.create( :ox_id=>id, :language_id=>1, :template_id=>tmpl.id, :title=>"Home", :menu_label=>"Zu Hause", :ancestry=>nil, :root=>true)

  # add the Paragraph element to this ox's toolbox
  self.elements << Element.find(1)

  # add an Article, a Paragraph, and a Post
  pe = PageElement.create( :element_id => Element.find(1) )
  pe.elementable = Paragraph.create(:content=>"This is written *in bold* -")
  pe.save
  pg.page_elements << pe


end

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

Обратите внимание, что это решение является «общим источником»'version

Сценарий рейка выглядит следующим образом:

#
#   rake add_site["www.domain.tld", "www_domain_tld", 131]
desc "Task for adding new oxenserver site"
task :add_site, :domain, :site, :ox_id do |t, args|
  service_path = /data/www/html/app_service
  site_string = %{
    <VirtualHost *:80>
        ServerName #{args[:domain]}
        DocumentRoot #{service_path}/sites/#{args[:site]}/public
        PassengerAppRoot #{service_path}/sites/#{args[:site]}
        SetEnv OX_ID #{args[:ox_id]}
        <Directory #{service_path}/sites/#{args[:site]}/public>
                AllowOverride all
                Options -MultiViews
        </Directory>
    </VirtualHost>
  }
  File.open("tmp/#{args[:site]}.conf", "w") do |f|
    f.write site_string
  end

  site_start = %{
    mv #{service_path}/current/tmp/#{args[:site]}.conf /data/apache/conf.d/#{args[:site]}.conf
    service httpd restart
  }

  File.open("tmp/#{args[:site]}.sh", "w") do |f|
    f.write site_start
  end

  #
  sites_dir = "#{service_path}/sites/#{args[:site]}"
  shared_sites_dir = "#{service_path}/shared/sites/#{args[:site]}"
  shared_oxen_dir = "#{service_path}/shared/sites/oxen"
  current = "#{service_path}/current"


  # prepare system files/directories
  system "mkdir #{sites_dir} #{shared_sites_dir} #{shared_sites_dir}/public"
  system "cd #{sites_dir} && cp -rus #{current}/* ."
  system "cd #{shared_sites_dir}/public && cp -r #{shared_oxen_dir}/public/* ."
  system "cd #{shared_sites_dir} && mkdir log tmp && cd #{sites_dir} && rm -rf public log tmp && ln -s #{shared_sites_dir}/public public && ln -s #{shared_sites_dir}/log log && ln -s #{shared_sites_dir}/tmp tmp"
  system "cd #{sites_dir} && touch tmp/restart.txt log/production.log"
  system "mv tmp/#{args[:site]}.sh public/system/job_queue/#{args[:site]}.sh"
end
...