BlessingCR’s Blog
BlessingCR’s Blog

每个人都应该懂的websocket+mq消息推送在微服务下方案

websocket 在微服务下方案

存在问题:

A服务的实例1和用户进行连接,但是实例2收到请求/触发消息/其他等需要推送用户消息的情况。此时由于不是一个实例,实例1不知道要推送消息给用户,需要处理消息丢失问题

方案1: 实例直接使用mq(Rocket,Rabbit,Kafka)广播

此方案好处在于,到处都是现成的方案
大概思路如下:
A服务下所有实例都订阅MQ,一旦遇到需要推送给用户的消息时,先确定用户在不在线(为了减少MQ消息数目,减轻MQ和服务压力),如果在线就把消息广播到MQ的某一个topic中,所有实例均订阅这个topic,并且所有实例维护和自己连接的用户的websocket的池子里面的用户id。如果用户和自己连接,就推送消息给用户,如果没有连接,就直接ACK该消息。
优点: 方案随处可见, 实现简单
缺点: 如果该服务有5个实例,那么平均每个实例只有1/5的消息是自己的,4/5的消息都是废消息。同时如果服务是K8S拉的,由于消息多的时候实例负载上升,K8S识别负载上升在拉新的POD(新的服务实例),然后进一步导致消息可用比例下降,MQ压力进一步上升,进入死亡螺旋。虽然可以通过维护内存内的连接数据来快速ACK无用消息,但并不治本。(也就是并不适合2C的或者大量用户场景)

可供参考:
https://blog.advance.ai/zh-cn/distributed-websocket-service

方案2: 实例之间使用websocket长链接

大概思路如下:
A服务下所有实例均建立实例之间的websocket连接,一旦遇到需要通知的消息(且用户在线),则直接广播。实例各自收到广播后确认是否用户和自己连接,如果和自己连接则推送消息给用户,否则无视消息。

优点:相对于方案1,不会进入死亡螺旋,所有消息消费即时性高。
缺点:所有同步均依赖与websocket,如果中间突然掉线一下,或者网络不好之类的,消息推送会丢失,且非常难以排查(也不好取证)。并且实例之间用websocket长连接,需要解决服务注册后,建立连接以及保持的问题(需要联动nacos,代码量较高)。

可供参考:
源地址: https://github.com/Linyuzai/concept
备份: https://github.com/BlessingCR/concept

方案3: 实例之间使用redis queue

大概思路如下:
A服务下所有实例数据库中维护自己和前端的websocket连接基本信息,redis中维护自己各自独立的queue,定时去redis中拉去消息,别的实例收到信息先去数据库查消息属于哪个实例,然后推送到对应实例的redisqueue中。

优点:相对于方案1,可用消息率是100%, 没有任何废消息, 性能也好。
缺点:时效性完全取决与去redis拉的频率,第二K8S杀pod的时候,redis中那个queue仍然会存在一段时间,导致消息还有些会推送到废弃的queue中,导致消息丢失。且这种也是全部在内存中维护,不好排查。

方案4: MQ加中间层

大概思路如下:
所有消息通知均通过MQ给消息中间服务B,服务A维护连接信息到Redis中(socket连接创建时维护一次,然后每N次心跳更新一次redis),服务B查询Redis后决定feign调用某具体实例,注意只能http调用,不然会被负载均衡

https://blessingcr.com/wp-content/uploads/2024/06/whiteboard_exported_image-1.png

优点:相对方案1,可用消息率是100%, 没有任何废消息, 性能也好
缺点:消息调度服务单独出来了,他不属于原本服务下任何一层,虽然是自己起的服务,但起的是类似中间件的作用。

# #
首页      rocketmq      每个人都应该懂的websocket+mq消息推送在微服务下方案

发表回复

textsms
account_circle
email

BlessingCR’s Blog

每个人都应该懂的websocket+mq消息推送在微服务下方案
websocket 在微服务下方案 存在问题: A服务的实例1和用户进行连接,但是实例2收到请求/触发消息/其他等需要推送用户消息的情况。此时由于不是一个实例,实例1不知道要推送消息给用户,需…
扫描二维码继续阅读
2024-06-04