技術解決方案
- 利用Redis的Pub/Sub
- 大致流程
- 前端發起WebSocket請求,建立連線後,後端把Session存至記憶體中。
- 當事件觸發時,向Redis傳送相對應的Topic和Message。
- 訂閱此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