Стратегии для многопоточного одноэлементного объекта в Rails - PullRequest
0 голосов
/ 26 июня 2019

У меня есть убедительный вариант использования, когда уведомления происходят в режиме реального времени на уровне сервера. Я хотел бы выдвинуть эти события через веб-сокет, используя ActionCable Rails. Как я могу надежно поддерживать долгоживущий одноэлементный объект, чтобы реагировать на события уровня сервера и отправлять их на них?

  • Я создал прототип приложения Rails, используя объект, созданный из файла в / app / lib, который смешивается в модуле Singleton. Даже с кэшированием классов, это создавалось несколько раз и иногда собирало мусор, несмотря на открытые сокеты.

  • Пометка метода initialize источника событий личным и написание метода экземпляра уровня класса, который проверяет Thread.main[:event_provider] для экземпляра, работает на 95% в процессе разработки, но я беспокоюсь о том, что я не знаю, что я делаю не знаю о производстве. Очень редко я получаю исключения, такие как «Expected x_y.rb для определения константы XY», что заставляет меня думать, что в этом подходе есть проблема.

  • Рабочий сервер в конечном итоге будет обслуживать очень небольшое количество клиентов в среде, требующей 100% безотказной работы. Я могу выбрать серверный стек, который имеет смысл.

Я надеюсь, что кто-то со знанием Rack и / или ActionCable сможет прокомментировать надежные способы передачи событий в приложение Rails с сервера.

1 Ответ

0 голосов
/ 30 июня 2019

На данный момент стратегия, которую я предпринимаю, состоит в том, чтобы создать экземпляр одноэлементного объекта в начале процесса загрузки, а затем использовать его для поддержки потоков.Для этого, очевидно, необходимы безопасные методы.

Файл application.rb определяет MyApp::Application.На этом этапе я объявляю метод доступа my_thing_manager, require my_thing_manager и устанавливаю self.my_thing_manager = MyThingManager.instance.

class MyThingManager
    def instance
        return Thread.main[:thing_manager] unless Thread.main[:thing_manager].nil?
        Thread.main[:thing_manager] = self.new
    end

    private

    def initialize
    end
end

Этот подход работает в одном многопоточном процессе , но не работает в кластерной производственной среде.Для моих требований это вполне приемлемо.Для многопроцессорных приложений можно использовать ловушки, например, в Puma after_worker_fork или Unicorn's after_fork для управления подпиской на что-то вроде Redis pubsub.Это будет требованием для будущего проекта, поэтому я ожидаю дальнейшего развития этой стратегии.

...