Как общаться с клиентом внутри цикла с помощью EventMachine?

Я хочу показать ход выполнения моей программы (здесь он представлен в виде цикла) на веб-сайте в режиме реального времени.

Если у меня работает правильный сервер Ruby, клиент должен иметь возможность подключаться к серверу через веб-сокеты с помощью браузера и после подключения получать «обновления» о недавно созданном процессе в режиме реального времени. Никаких опросов или Ajax не требуется.

Моя идея проверить это состоит в том, чтобы установить дуплексную связь между EventMachine::WebSocket и клиентским браузером с использованием клиентского JavaScript. Создайте цикл и отправьте обновления клиенту браузера.

ruby_socket_server.rb:

require 'eventmachine'
require 'em-websocket'
@sockets = []
EventMachine.run do
    EventMachine::WebSocket.start(:host =>'localhost',:port =>8887) do |socket|
    socket.onopen do
        @sockets << socket
        (0..10).each do |i|
            puts "i am on loop #{i}" #to terminal window
            @sockets.each do |s|
                s.send "Just received notification of loop #{i}" 
                sleep(1) #pause for a second
            end
        end
    end
end

основной.js:

socket = new WebSocket('ws://localhost:8887');
socket.onopen = function(){
    console.log('OPEN');
}
socket.onclose = function(){
    console.log('CLOSED');
}
socket.onmessage = function(msg){
    console.log(msg);
}

Я ожидаю, что это будет выглядеть так:

  • Сервер: я на петле 1.
  • Клиент: только что получил уведомление о цикле 1.
  • Сервер: я нахожусь на петле 2.
  • Клиент: только что получил уведомление о цикле 2.

Что происходит на самом деле:

  • Сервер: я на петле 1.
  • Сервер: я нахожусь на петле 2.
  • Клиент: только что получил уведомление о цикле 1.
  • Клиент: только что получил уведомление о цикле 2.

Я не могу отправить данные клиенту из цикла Ruby, т. е. он в конечном итоге отправляет все пакеты, но не ВО ВРЕМЯ каждой итерации, как ожидалось. Вместо этого он ждет, пока цикл сервера не завершится, и отправляет несколько вызовов подряд.

Что я здесь делаю неправильно? Возможно ли то, что я ищу? Любые мысли о том, как взломать этот орех, будут полезны.


person Pandem1c    schedule 06.02.2013    source источник


Ответы (2)


EventMachine не является многопоточным. Поэтому, если вы не позволите ему работать, то есть если вы останетесь внутри любого из его обратных вызовов, то ничего не произойдет.

В вашем примере вы вызываете sleep внутри обратного вызова. Это предотвратит завершение обратного вызова, и, следовательно, EM не сможет запуститься, а если он не сможет запуститься, он не сможет отправить ваше сообщение.

Таким образом, любой сервер EM должен быть полностью построен вокруг настройки обратного вызова с событиями.

Вот почему он называется Event Machine. Это требует немного другого мышления, но как только вы освоитесь, все будет в порядке.

Вот пример того, как вы можете заставить его работать:

EM.run do
  EM::WebSocket.start(:host =>'localhost',:port =>8887) do |ws|
    ws.onopen { |handshake|
      puts "New connection from #{handshake.origin}"
      loop = 1
      timer = EM.add_periodic_timer(1) {
        ws.send "Timer loop #{loop}"

        if (loop += 1) == 5
          EM.cancel_timer(timer)
          ws.send "Bye"
          ws.close_websocket
        end
      }
    }
  end
end
person Casper    schedule 06.02.2013
comment
Спасибо!! Работает как шарм. - person Pandem1c; 06.02.2013
comment
@Pandem1c - Круто. Не забудьте также принять ответ, чтобы люди знали, что сработало (щелкните галочку слева). Спасибо. - person Casper; 06.02.2013

С Rails5.0 вы можете использовать ActionCable, который хорошо совместим с WebSockets.

person Gaurav Singh    schedule 27.01.2017