作者回复: 对,课程演示的是一种方便教学的理想模型。实践中,一般通过Nginx或者网关的访问日志(access log),采集用户的视频点击观看日志,再发送到计数服务。这样就不耦合。
作者回复: 对,对于至少一次交付(At Least Once)的消息队列,都需要考虑去重问题,如果涉及业务的话,还要考虑幂等,第三章也会讲到。一般在消息进入消息队列之前,可以给它加一个唯一的id,这样消费端既可以用缓存窗口(cache window)等技术做去重处理,也方便后续业务做幂等处理。
作者回复: 如果你的aggregator同时做聚合和写入DB的事情,不是不可以,但是会有一些问题,比方说: 1. 一个是你的aggreator同时干了两个事情,违反单一职责原则,后续如果要调整写入数据库逻辑,aggregator就得跟着一起改。后面发布也要一起发布,不能单独发布DB writer。如果有中间queue的话,DB writer暂时下线都没关系,queue会缓冲计算结果。 2. aggregator因为同时干两件事情,当写入数据库慢,可能阻Aggregator线程,甚至影响到聚合运算性能。 内部queue是持久化的,在企业开发中,一般认为数据能够落地(落磁盘文件,或者DB),那么数据就不会再丢,宕机了重启都能找回来。当然这个不是绝对,只是相对内存数据来说的,内存数据宕机就没有了。
作者回复: 有必要,尽管是pull模式,但是DB慢或者挂,或者之间的网络出现抖动/瞬断,这些问题在实际生产环境中都无法避免。
作者回复: redis的最常见使用场景是作为缓存(Cache)来用,比如把redis作为DB的缓存。nosql大致可以分为四种,KV缓存(包括redis)是一种,文档存储(如MongoDB)是一种,列式数据库(Hbase/Cassandra)是一种,还有图数据库(如Neo4j)也是一种。
作者回复: k8s容器环境是麻烦一点,一种办法是考虑Persistent Volume持久卷,持久卷可以挂载磁盘或者远程存储。 另外一种办法,索性把计算结果再发送到kafka队列,再让DB Writer去kafka消费数据再写入DB。
作者回复: 1. 消费端去重的一种简单做法,是每个消息进入队列时,header里头给一个唯一id,然后消费端利用缓存窗口技术进行去重,如果单位时间内某个id已经有缓存过,就去重丢弃。 2. 我之前就开源过做一个叫bigqueue(https://github.com/bulldog2011/bigqueue)的磁盘持久化queue,基于内存映射技术的,性能很高。你如果有研发能力,自己开发一个简化的磁盘queue,工作量其实也不是很大。嵌入式db可以考虑sqlite或者berkelyDB,都可以实现简单queue的功能。
作者回复: 1. 一般queue的两头都需要一个线程,比方说这里的计数消费者里头有两个queue,所以第一个queue的前面有一个consumer thread,第一和第二个queue之间有一个aggregator聚合线程,第二个queue后面有一个DB writer线程。 2. 同步到DB就是一个批量插入(batch insert),其中每一条记录信息主要包括时间 + 视频id + 一分钟观看计数这些字段。
作者回复: 没有规定失败几次写入死信队列,一般可以设置成3次,最好做成动态可配置,可以按需动态调整。 立即重试可以,高级一点的可以按指数级退避再重试(退1秒重试,不行再退2秒,不行再退4秒。。。)
作者回复: 1. deadletter queue可以是本地的嵌入式DB,例如sqlite,或者berkeley DB,也可以是本地持久化queue,例如我开源的github.com/bulldog2011/bigqueue 2. 只要一个后台线程,定期监控deadletter queue,有消息的话定期重试写入DB即可。