分散式WebSocket架構

LiDaQian發表於2018-10-21

技術解決方案

  • 利用Redis的Pub/Sub
  • 大致流程
  1. 前端發起WebSocket請求,建立連線後,後端把Session存至記憶體中。
  2. 當事件觸發時,向Redis傳送相對應的Topic和Message。
  3. 訂閱此Topic的Listener會接受到訊息,並從記憶體中查詢相應的Session,如果找到Session則向前端推送訊息。
  • 具體程式碼Demo如下
@Configuration
public class RedisObserverConfig {

    public static final String TOPIC_ORDER_FOOD = "websocket:order_food";

    @Autowired
    private JedisConnectionFactory jedisConnectionFactory;

    @Bean
    RedisMessageListenerContainer redisContainer() {
        final RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(jedisConnectionFactory);
        container.addMessageListener(messageListener(), orderFoodTopic());
        container.setTaskExecutor(Executors.newFixedThreadPool(4));
        return container;
    }

    @Bean
    MessageListenerAdapter messageListener() {
        return new MessageListenerAdapter(orderFoodListener());
    }

    @Bean
    ChannelTopic orderFoodTopic() {
        return new ChannelTopic(TOPIC_ORDER_FOOD);
    }

    @Bean
    OrderDishesListener orderFoodListener() {
        return new OrderDishesListener();
    }

}
複製程式碼
<bean id="jedisConnectionFactory"
		class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
		destroy-method="destroy">
		<property name="hostName" value="${redis.host}" />
		<property name="port" value="${redis.port}" />
		<property name="timeout" value="${redis.timeout}" />
		<property name="database" value="${redis.database}" />
		<property name="password" value="${redis.password}" />
		<property name="usePool" value="true" />
		<property name="poolConfig" ref="jedisPoolConfig" />
	</bean>
複製程式碼
  • SendMessage
@Service
public class OrderFoodWebSocketService {

	@Autowired
	private RedisTemplate redisTemplate;

	public void sendMessage() {
		redisTemplate.convertAndSend(RedisObserverConfig.TOPIC_ORDER_FOOD, "hello");
	}
}
複製程式碼
  • MessageListener
public class OrderDishesListener implements MessageListener {

    @Autowired
    private RedisSerializer<Object> jsonRedisSerializer;

    @Override
    public void onMessage(Message message, byte[] pattern) {
        Object object = jsonRedisSerializer.deserialize(message.getBody());
        System.out.println("orderDishesListener message body = " + JSON.toJSONString(object));
    }
}
複製程式碼

注意點

nginx支援WebSocket需要新增以下配置

location /wsapp/ {
    proxy_pass http://wsbackend;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
}
複製程式碼

nginx會把Http請求升級至WebSocket

參考資料

相關文章