老师好。 想问下sender的message service是如何把消息发送给receiver所在的messaging service的? 我的理解是需要一个table/cache来维护user和websocket server之间的mapping,然后sender的messaging service去查找receiver在哪台具体的websocket server上,然后通过rpc把消息发送给这个server,这个mapping的table可以存在presence service上吗?
是的,需要一个服务来维护这个 mapping, 直接放在 presence service 里就好。 Presence DB 加一列存储这个信息就可以了。
@logic 请问为什么需要这个mapping呢? 难道不是直接通过sharding key直接放到对应user的message queue里就好了吗?
这个 mapping 使用来维护用户和服务器的 websocket connection 的对应关系,即服务器怎么根据一个 user ID 找到对应的 websocket connection 给接收端发消息。跟 message queue 处理的问题是不一样的。
Hi @logic,罗辑老师,追问个问题,如果webSocket server crash了,应该怎么办?Sender的message service发现对应的webSocket server 挂了,是不是应该把消息放到message cache 已经message DB里,等到用户重新连上新的WebSocket,再Pull所有的消息。
另外,为什么webSocket的连接没有走LB,而是直接跟client连接?
感觉用一个message queue发信息比较好。这样即使websocker server crash了也不用担心。只需要在重连之后从queue里面继续poll就好了。
对,发回到Queue里会更快。或者不ACK, 直到web socket 发送出去了再ACK Message Queue,
因为如果WebSocket 拿到了消息准备发送的时候,crash了,那消息不就丢了, 不过这样就会有点复杂而且会慢,不知道有没有什么更好的办法。
@Ning_LI
相比从db pull这个应该不算复杂,因为message queue比如kafka自带记录上次读取位置的功能。就像你所说的只要不ACK(commit offset)就会保证at least once delivery.如果用db来实现自己还得写一套逻辑来记录crash之前发到哪了。
至于快慢我感觉这方法不是很慢, 你可以解释下哪部分比较慢吗。对于发消息的人来说只要写到queue里就算成功了。
在用户重新上线之后读取信息是不能去 Message Queue 的。因为 Message Queue 是有序的,而我们的读取是个 random access。读取的时候我们会把所有某个人的消息读出来,而 Message Queue 里面存有很多人的消息,我们不能把这些消息全读,因为可能有人还没上线。
Hi @logic, 我记得讨论过WebServer 发送的时候发现用户断线了,或者WebServer crash了,这个时候没有发送出去的消息是不是应该还在Queue里,或者应该放回到Queue里;
但是这就有个问题,Queue里的消息应该都是在线用户的消息, 我们不能保证用户马上回连回来,所以这种情况如何处理?谢谢
对于这个问题我的想法是可以使类似kafka的系统做message queue这样 message不会被消耗掉。对于每一个用户都有一个对应的consumer负责从kafka topic里面pull data。这样就不会涉及到poll别人的message了。不过这种方法的downside是需要的consumer太多了。
只有在我们认为能把消息发出去的时候,我们才把消息放到 Queue 里,其他情况下是放在 DB 和 Cache 里的。极端情况下,如果消息到 Queue 里以后发现用户掉线了,这时候可以重新写回 DB 和 Cache 里。
我觉得这种情况可以考虑不把没法出去的消息放在原来的queue里。可以考虑用一个dlq去存失败消息。这样就保证了原来queue只负责在线消息。dlq负责失败的。按照老师的design可以有一个process把dlq的消息consume了并写道message cache & message db里。
这样不行,Kafka 也会把其他人的信息读出来,Kafka topic 读取是按照 partition 来的,一个 partition 里有多人的消息,不管多少 Consumer,都一样不行。
这个方案不错,如果多次投递不成功,发现用户掉线,然后可以写回Cache和DB
恩恩。我理解读取是按partition来的。我忘了提到这种方案kafka需要按照recipient id 来partition。一个partition里边只有一个用户的消息。不过这种似乎比较浪费资源。因为有的用户可能只有一条消息。没必要单独维护一个partition以及一个consumer。
一个用户一个 partition 是不行的,Kafka partition 数量是有上限的,分出单独的 Partition 是很昂贵的操作。