cramp Framework Sync 'Render' правильно, используя em-синхронность

Чтобы описать мою проблему, я прикрепляю простой класс Cramp http://cramp.in/. Я добавляю некоторые модификации, но в основном они работают как https://github.com/lifo/cramp-pub-sub-chat-demo/blob/master/app/actions/chat_action.rb

class ChatAction < Cramp::Websocket

  use_fiber_pool

  on_start :create_redis
  on_finish :handle_leave, :destroy_redis
  on_data :received_data

  def create_redis
    @redis = EM::Hiredis.connect('redis://127.0.0.1:6379/0')    
  end

  def destroy_redis
    @redis.pubsub.close_connection
    @redis.close_connection
  end

  def received_data(data)
    msg = parse_json(data)
    case msg[:action]
    when 'join'
      handle_join(msg)
    when 'message'
      handle_message(msg)
    else
      # skip
    end
  end

  def handle_join(msg)
    @user = msg[:user]
    subscribe
    publish(:action => 'control', :user => @user, :message => 'joined the chat room')
  end

  def handle_leave
    publish :action => 'control', :user => @user, :message => 'left the chat room'
  end

  def handle_message(msg)
    publish(msg.merge(:user => @user))
    # added only for inline sync tests
    render_json(:action => 'message', :user => @user, :message => "this info should appear after published message")
  end

  private

  def subscribe
    @redis.pubsub.subscribe('chat') do |message|
      render(message)
    end
  end

  def publish(message)
    @redis.publish('chat', encode_json(message))
  end

  def encode_json(obj)
    Yajl::Encoder.encode(obj)
  end

  def parse_json(str)
    Yajl::Parser.parse(str, :symbolize_keys => true)
  end

  def render_json(hash)
    render encode_json(hash)
  end
end

Больше о том, что я пытаюсь сделать, находится в методе handle_message.

Я пытаюсь отправить сообщения клиенту в правильном порядке. Сначала опубликуйте сообщение для всех подписчиков, затем предоставьте некоторую внутреннюю информацию только для текущего подключенного клиента.

Для приведенного выше кода клиент получает:

{"action":"message","user":"user1","message":"this info should appear after published message"}
{"action":"message","message":"simple message","user":"user1"}

Это не синхронизировано, потому что em-hiredis может быть отложено. Поэтому я пытаюсь синхронизировать это так:

def handle_message(msg)
  EM::Synchrony.sync publish(msg.merge(:user => @user))
  EM::Synchrony.next_tick do # if I comment this block messages order is still incorrect
     render_json(:action => 'message', :user => @user, :message => "this info should appear after published message")
  end
end

Теперь клиент обрабатывает сообщения в правильном порядке.

{"action":"message","message":"simple message","user":"user1"}
{"action":"message","user":"user1","message":"this info should appear after published message"}

Мои вопросы:

  • Когда я комментирую блок EM::Synchrony.next_tick, порядок сообщений по-прежнему неверен. Какое значение имеет блок EM::Synchrony.next_tick в этом примере?
  • Это хороший способ обрабатывать встроенную синхронизацию с Cramp или EventMachine?
  • Есть ли лучший, более ясный способ справиться с этим?

Спасибо!

1 ответ

Решение

Я нашел решение этой проблемы, em-синхрония должна работать прямо из коробки, требуя эту библиотеку:

require 'em-synchrony/em-hiredis'

class ChatAction < Cramp::Websocket

Использование блока EM::Synchrony.next_tick - плохая идея, с большой помощью сообщества em-synchrony я добавляю патч совместимости em-hiredis 0.2.1 на github.

Теперь метод handle_message выглядит следующим образом:

def handle_message(msg)
   publish(msg.merge(:user => @user))
   render_json(:action => 'message', :user => @user, :message => "this info should appear after published message")
end

Не забудьте взять этот драгоценный камень из GitHub

gem 'em-synchrony', :git=> 'git://github.com/igrigorik/em-synchrony.git'

Надеюсь, это поможет кому-то.

Другие вопросы по тегам