82

计数系统杂谈

 6 years ago
source link: http://mp.weixin.qq.com/s/OsrhodXUVOaRcwYOfBV5iQ
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

计数系统杂谈

王伟@招聘 技术 58招聘技术团队 2017-10-24 02:39 Posted on

Image

        由于业务需要,前段时间重构了帖子的发帖上限,也就是计数系统,下面是自己的一些做法,有不正确的地方希望能够多多指正。提到计数系统无非两种,一种是DB中直接存储帖子详情,需要的时候进行count计算,这种方案在数据量比较小的时候还可以,随着业务增长,数据量比较大的时候,问题就展现出来了,查询一次需要很长的时间。因此只能采用另外一种:将总数存放到一个地方,每次有变化的时候更新这个值。

        为了解耦,这里采用接收MQ消息,这种方案优点是能够与业务系统解耦,方便修改和扩展,缺点是用户发帖时候如果消息有延迟等问题的时候,会造成计数不准确,不过这种情况比较少,业务上也是可以接受的。

        根据业务,需要存储两张表,一张是发帖详情,包含帖子id,用户id,帖子状态等,另一张是用户id对应的上限总数。

        设计系统时候要考虑以下几方面:

1) 数据的准确性:

  1. 消息过来后除正常消息外,有可能是延迟的消息,也可能是重复发送的消息。

  2. 多条消息对同一条帖子处理的时候,必须有锁机制,防止其他线程操作。

2) 数据的一致性:

  1. 要保证发帖详情表要与其他业务表中数据一致(主要是帖子状态)。

  2. 要保证发帖详情表与总数表中数据一致。程序难免会出问题,因此需要一个修复程序定期去检测修复两边不一致的问题。

3) 削峰平谷:接受的主题中有些消息会在某个时间点激增。这里两种处理办法,一种是自己处理的足够快,接收多少都能够处理过来,比如批量处理。另一种是放入缓冲队列,慢慢处理。

4) 平滑关闭:执行的服务在重启的时候可能会造成队列中正在执行的消息丢失。可添加java虚拟机的钩子函数来实现。

5) 监控:任务在执行中各个线程是否处理正常,每个任务是否都执行成功。我这里写了shell脚本去统计业务中的关键点,定时发送邮件。

6) 重复消费:系统要设计成与消息状态无关,即使重复处理也不会有问题,这样方便后期扩容。

        除了上述架构方面的考虑,业务方面也有几点需要考虑:

  • 重试机制:程序运行过程中难会免超时,因此单个处理失败,在合理范围内,需要进行重试。

  • 消息的正确性:消息在接收的时候服务端无法保证消息一定是准确可靠的(比如发送方出问题,消息延迟发出等),需要客户端自己来判断。客户端在验证消息的过程中,如果每次都去其他服务中查询必然会带来很大的流量和延迟,所以必须依靠业务属性来减少其他系统的依赖。58的消息中携带有本次修改的状态和上次旧的状态,处理时候查询db,判断db中状态与旧状态一致的时候,说明这条消息时正确的,可以正常处理,否则再去查询其他服务,这样做正常情况下依赖其他服务查询状态的情况大大降低。

  • 处理的准确性:如果是要求实时性比较高的系统,那么可以先进行计数部分的快速加减,那么要求处理前的数据尽量要是准确的。如果对处理的准确性要求比较高,那么可以适当降低实时性的要求,加一些校验等。结合业务,用户在两次发贴之间,其他系统有3秒左右的延时,在这个时间内,正常情况下,消息处理肯定能够及时完成,只会出现极少数不正常情况,但这个不正常的情况不影响用户的体验(超发1条也影响不大),后续修复程序会再次修复,能够保证数据的最终一致性。

        最后附一张系统架构图:

Image

        ReceiveHandler负责消息的接收,收到后放入DateQuene队列中

        SaveHandler负责从DateQuene中批量拿数据封装成任务提交到执行队列ExecuteQuene中处理。

         DealHandler在执行整个业务操作过程。并写入对应的数据。

Image

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK