文章主要介绍了《架构师之路:架构设计中的100个知识点》中关于海量定时事件管理的知识点。文章描述了三种处理海量定时事件的方法:轮询扫描法、多timer触发法、环形队列法,并解释了每种方法的特点和适用场景。
该方法使用一个Map记录每个uid的最近一次上报时间,并启动一个timer进行轮询扫描。特点是只启动一个timer,但CPU消耗较大。
该方法同样使用Map记录上报时间,但为每个uid启动一个timer。特点是无需轮询,但timer的维护量较大,内存消耗较多。
该方法使用环形队列和Set、Map结构,只需一个timer,既不耗内存也不耗CPU,业务处理效率高。环形队列法是一个通用方法,适用于其他场景。
文章最后对三种方法进行了总结,并提供了补充阅读材料《HashedWheelTimer》的链接。系列文章《架构师定会遇到的80个经典架构问题》等也值得关注。
例如:有100W个用户uid在线接单,客户端每30s会有一个存活上报,如果30s没有上报,服务端要将用户的状态置为不可接单。一般如何怎么实现这类需求?
大体来说,有三类方法:
方案一:轮询扫描法。
1. 用一个Map来记录每一个uid最近一次上报时间;
2. 当某个用户uid存活上报时,实时更新这个Map;
3. 启动一个timer,轮询扫描这个Map,看每个uid的last_time是否超过30s,如果超过则进行超时处理;
这个方案的特点是:只启动一个timer,但要轮询,CPU会跑满,比较消耗计算资源。
方案二:多timer触发法。
1. 还是用一个Map来记录每一个uid最近一次上报时间;
2. 当某个用户uid存活上报时,实时更新这个Map,并同时对这个uid启动一个timer,30s之后触发;
3. 每个uid请求包对应的timer触发后,看Map中,查看这个uid的last_time是否超过30s,如果超过则进行超时处理;
这个方案的特点是:不需要轮询,但每次上报都要启动一个timer,timer的维护量在100w级别,比较消耗内存资源。
方案三:环形队列法。
1. 30s超时,就创建一个index从0到30的环形队列(本质是个数组);
2. 环上每一个slot是一个Set,表示在这一秒钟,这些uid触发过存活上报;
3. 同时还有一个Map,记录uid落在环上的哪个slot里;
4. 启动一个timer,每隔1s,在上述环形队列中移动一格,0->1->2->3…->29->30->0…;
5. 有一个Current Index指针指向正在检测的slot;
那怎么记录last-time呢?
不用记录last-time了,每当报文上报,就将原来的uid删除,重新加入到当前指针的前一个slot。
为什么是前一个slot?
因为前一个slot将在30s之后被扫描到。如果uid按时上报了,它将永远不会被扫描到。只有30s内没有上报,才会被扫描到。
这个方案的特点是:
1. 只有一个timer,不耗内存资源;
2. 不是for循环轮询,而是每1s钟走一格,不耗CPU资源;
3. 当前指针扫描到的slot里的Set批量超时,业务处理块;
另外,这个环形队列法是一个通用的方法,Set和Map中可以是任何task,本文的uid是一个最简单的举例。
环形队列法,还有其他场景使用吗?
HashedWheelTimer也是类似的原理。
https://dev.to/frosnerd/hashed-wheel-timers-5bo9文章不长,含代码解析。
==全文完==
宝藏号,日更。
点赞,转发,在看,感激不尽!