cuscuta的草稿 - 6
嘿呀~
是的,在经过一堆烦人考试的折磨后,咱又回来啦!虽然还有一科遗传学,不过先不管啦!
太臃肿啦!
是的,cuscuta的工作队列设计模式,我说的是结果队列的设计模式——太过于臃肿啦!所以我打算要稍微重构一下这玩意……
重构
旧的redis模式为:
1 | cuscuta: |
在思考后,我发选这个模式存在以下缺陷:
- 维护了pending和results两个JobTag列表
- 这浪费了两倍的空间
- 如果需要统计任务的精确完成情况,就需要查询两次JobTag列表,不易统计任务的完成情况
- 最重要的是,目前由于只有两个index和value,共计三个需要维护的列表,如果需要给任务添加错误回传,就需要添加第四个维护的列表,这在技术上可行,但是复杂的redis结构逻辑会加剧我的心智负担,并且也不利于后续的扩展(如果我要加第五个字段,阁下又该如何应对)
所以,我打算将redis模式改成下面这样的:
1 | cuscuta: |
别急,我当然不会用 ImprovedJobTag 这种名字……
1 | /// 更改后的 JobTag |
同时,多List简单追加信息改为使用JobUid搜索后更改,我承认这可能会降低性能,但是我认为这点性能损失不在话下.unwrap()
显然,我需要更改worker的逻辑,使其在更新本地缓存任务状态时也更新redis数据库里的
“You can’t be more careful about error handing”
我承认cuscuta现在的错误处理还有待提升,尤其是核心业务逻辑中极有可能出现的错误,我还放到现在才处理
当然,我也不敢保证除了这个以外我没有别的错误还没考虑到
如果上述更改凑效,我就可以对错误进行分级:一些错误是不可容忍的,例如“好友找不到”,遇到了这种错误,worker在清理任务时,就需要将其任务状态直接置为Failed,而不是置为Queueing然后重入队
说到这里,我觉得我也有必要修改一下目前的worker循环——目前worker循环有滥用?表达式的倾向,而实际情况是,并不是所有 Result::Err 都是需要直接中断掉工作循环的:
- 一些错误只影响任务本身(例如无法添加好友),这时候就需要记录这个错误,并且按照严重性在任务清理步骤中进行对应操作,例如写Failed,然后直接XACK,不重入队
- 对于一些可以重试,但是这个worker本身已经无力回天的错误(例如读取单个好友信息失败——这个错误不常见,但是我不保证它不会发生),就需要写Queueing,然后重入队
- 对于一些可以重试,只是因为网络问题的错误,就让worker一直重试——既然都有网络问题,这时候多半是redis数据库都连不上,要么是机房断网——在这种物理胁迫下,任何故障转移没有意义,不如不进行故障转移
- 当然,如果worker遭遇不测,优雅停机了,worker就应该把自己未完成的任务全部重入队,并且失败信息加入一个”Worker halting”之类的;如果没有优雅停机,就等着别的worker xautoclaim吧,反正已经死透了,记得让通过xautoclaim领取的任务再加一个”Worker halting”之类的错误信息
以上对工作循环的重构提出了一些挑战,不过我想我可以解决
Dashboard……的前置
接下来让我们画一点大饼,如果我以后需要给cuscuta还是别的什么分布式集群添加一个可视化仪表盘,我需要暴露些什么东西呢?
一个简单的方法是:直接读取redis数据库的内容,然后将其解析成仪表盘上的数据
但是这显然不太优雅,尤其是如果我以后需要在一个仪表盘中查看多个集群(或者说,多个部署)的运行情况的话,我总不能让仪表盘做一堆适配吧?
所以,我觉得整一个统一的API规范是有必要的——每个部署内部维护一个简单的Deployment,这个Deployment只是读redis队列,或者是其它什么东西,然后将其映射为一个结构化的输出,前端只需要调用这些接口,解析这些统一结构的输出就可以了,简单来讲,把数据对接的操作丢给了部署本身,而不是Dashboard
// Dashboard API 正在设计中