布同:对突发时效数据的自适应请求算法讨论
【物理情景】
某台新闻服务器上维护了一个最新的1000则新闻列表,随着新闻的不断加入,会有旧新闻被挤出这个新闻序列。我们需要用自己的日志服务器去把这个新闻序列中出现过的所有新闻记录都抓到本地进行存储。可以通过日志服务器向新闻服务器发送列表请求,获得一定数量的新闻。这个新闻服务器支持响应一定速率的请求,如果请求过快也需要等待才能获取到结果。每次请求的条数越多,等待时间越长,单次请求的最大数为30条,等待时间为1秒钟。每天的早上8点到12点之间新闻的新增比较集中,每天大概会新增有10000条左右的新闻。最新的新闻的加入速度未知,必须保证平均每次请求获取的新增新闻数量较高,因此空闲区间内需要停止请求(代码可能会改变,需要维护和新增功能)。
【基本算法1】
使用一个进程,按照一个偏移量offset,数量cnt去获取新闻列表。然后增加offset+=cnt,再用offset为偏移量,cnt为数量去获取。重复这个劳动,直到获取够偏移量1000条新闻为止。这整个算作一次任务。然后定期执行一次任务。
适用性:简单直接。
遗漏点:当新闻列表静止时,或者一次任务执行完才有新闻新增时,这个算法是安全和适用的。
【优化算法2】
使用一个进程,按照offset,cnt去分页,从旧新闻端开始抓列表。如果倒数第1页有未抓过的新闻,则继续抓倒数第1页,如果没有新的,则抓倒数第2页,倒数第2页有新增的新闻,则继续抓,否则倒数第3页……以此重复,直到抓完所有的新闻,完成一次任务。然后定期执行一次任务。
适用性:如果某段时间新增新闻比较快速,那么这个算法可以保持长时间的持续运行,减少遗漏新闻的数量。同时,空闲情况下也能够快速结束,减少无效的请求数量。
遗漏点:因为一次请求只能够获取到30条最多,如果新增的速度超过30条,那么将会有遗漏的情况出现。当新闻新增爆发的时候,遗漏的数量不可估计。
【优化算法3】
按照offset,cnt去分页,指定进程抓取指定的分页列表。首先启动两个进程,第1个进程抓倒数第1页,第2个进程抓倒数第2页,这两个进程需要运行策略。
策略一:当第1个进程抓一次发现有新增的新闻时,新增启动一个进程3,去抓取倒数第3页,以此类推,如果第1个进程还是抓到新增的新闻,如果当前的进程数位x,则启动新进程x+1去抓取倒数第x+1页。
策略二:增加进程减少机制。当第2个进程抓到新增新闻时,说明不需要减少进程。当第2个进程没有抓到新增新闻时,说明空闲进程过多,如果当前有x个进程,则杀掉第x个进程,停止对倒数第x页数据的抓取。如果一直没有抓到,则一路杀过来,直到没有第3个进程可以杀为止。
策略三:当只有两个进程时,并且这两个进程都没有抓到新的新闻时,可以采用两种做法。一种是指数级的休眠,由于醒过来的反应速度太慢,有可能错过高峰期,所以不值得推荐,短时休眠继续抓的方式不利于程序的更新。第二种是第2个进程自己退出,然后第1个进程逐页向前移动,直到检查完毕所有的页没有新增新闻,则自己退出,短时休息之后第1个进程又重新启动,然后从后往前再扫描,如果抓到新的新闻,则新增第2个进程,抓取靠前的那一页即可。
适用性:该算法兼有了算法2的优点,同时一定程度上大大提高了抓取成功率。几乎可以达到能够抓取的最理想速度。可行的最理想的全速就是对于x页,启动x个进程分别不停的去抓取。
遗漏点:一般来说x不能大于总页数。如果大于,超出的部分进程工作是无效的。这种情况下如果还有遗漏,可能性有两个,一个是进程1退出的休息间歇,第2个是新闻服务器新闻新增的速度大于对外提供新闻列表的速度(这样的情况基本不可解决)。好在新闻都是人工整理之后逐条发布的,记者和编辑的数量都是有限的,所以这个算法是很贴合实际的。但是这个算法的复杂度最高,需要解决进程之间的通信,实现进程号的管理。
【总结】
对于程序的执行,一般需要记日志。对于多进程之间的异步操作,更需要考虑读写、IO、资源申请等问题,防止出现资源竞争,影响程序执行。
对于日常工作,只有精益求精,不断反思,才有提高。