Software Design不是简单的活
今天把我StatusD的代码重构了一下,开始还兴致勃勃,做到后来就发现一个严重的问题。
问题的大致是这样的。有1个下载任务,会分配给M个TailNode,每个TailNode都会给StatusD上报对每个任务的完成报告,报告分为"成功"或者"失败"。
需求1规定StatusD应该给Monitor One上报所有成功完成的任务。而"任务的成功"要求所有M个TailNode上报"成功"的完成报告。
需求2则规定,StatusD应该定期Monitor Two转发"失败"的完成报告,亦即每60m上报一次最近60m内失败的完成报告。
这两个需求看起来很简单。我是这么做的。
StatusD为该任务维护一个map<NodeIP, Status>的statusMap,每次从TailNode接收到一个完成报告,则往statusMap里增加一项。
需求1可以这样满足:
1。当statusMap的size增长到N的时候,且所有N个报告都是"成功",这个任务是成功的,上报给Monitor One
2。如果接收到一个"失败"的完成报告,则该任务算"失败"。
需求2可以这样满足:
每隔60m,遍历一下statusMap,找出所有的"失败"的status,上报给Monitor Two.
这个设计的陷阱在于,需求2要求的增量转发,但是实际上,转发的是全量的。亦即下一60m的转发包含了上一个60m转发的内容。
这个陷阱的来源是因为,最初TailNode给StatusD的"失败"的上报不仅包含了状态信息,还有事件信息,而存储到statusMap后,只维护了状态信息,丢失了事件信息。
解决办法有
1。修改statusMap为map<NodeIP, Status,Redelivered>(请勿告诉我,map只有key和alue,没有第三者,这不是问题的重点)。转发则把 Redelivered为false的element,并且把该element的redelivered改成true。
2。另外维护一个list<failed report>的列表,每次StatusD从TailNode接收到"失败"的完成报告,则不仅修改statusMap,也往这个列表写入。每隔 60秒,则把当前的list全部转发给Monitor Two,并且将其清空。
实际代码比这个复杂,任务不是一个,而是多个,每个任务对应的Node的集合也不一样。但是问题的核心没变。
第一个解决办法,会搞得代码很难看,而且遍历的效率也不高。
第二个解决办法,造成状态信息的冗余。当增加超时功能(StatusD定期检查,在规定的时间statusMap没有达到N的size,则认为尚未上报的 TailNode该任务失败)的功能的时候,扩展变得很复杂。
问题的大致是这样的。有1个下载任务,会分配给M个TailNode,每个TailNode都会给StatusD上报对每个任务的完成报告,报告分为"成功"或者"失败"。
需求1规定StatusD应该给Monitor One上报所有成功完成的任务。而"任务的成功"要求所有M个TailNode上报"成功"的完成报告。
需求2则规定,StatusD应该定期Monitor Two转发"失败"的完成报告,亦即每60m上报一次最近60m内失败的完成报告。
这两个需求看起来很简单。我是这么做的。
StatusD为该任务维护一个map<NodeIP, Status>的statusMap,每次从TailNode接收到一个完成报告,则往statusMap里增加一项。
需求1可以这样满足:
1。当statusMap的size增长到N的时候,且所有N个报告都是"成功",这个任务是成功的,上报给Monitor One
2。如果接收到一个"失败"的完成报告,则该任务算"失败"。
需求2可以这样满足:
每隔60m,遍历一下statusMap,找出所有的"失败"的status,上报给Monitor Two.
这个设计的陷阱在于,需求2要求的增量转发,但是实际上,转发的是全量的。亦即下一60m的转发包含了上一个60m转发的内容。
这个陷阱的来源是因为,最初TailNode给StatusD的"失败"的上报不仅包含了状态信息,还有事件信息,而存储到statusMap后,只维护了状态信息,丢失了事件信息。
解决办法有
1。修改statusMap为map<NodeIP, Status,Redelivered>(请勿告诉我,map只有key和alue,没有第三者,这不是问题的重点)。转发则把 Redelivered为false的element,并且把该element的redelivered改成true。
2。另外维护一个list<failed report>的列表,每次StatusD从TailNode接收到"失败"的完成报告,则不仅修改statusMap,也往这个列表写入。每隔 60秒,则把当前的list全部转发给Monitor Two,并且将其清空。
实际代码比这个复杂,任务不是一个,而是多个,每个任务对应的Node的集合也不一样。但是问题的核心没变。
第一个解决办法,会搞得代码很难看,而且遍历的效率也不高。
第二个解决办法,造成状态信息的冗余。当增加超时功能(StatusD定期检查,在规定的时间statusMap没有达到N的size,则认为尚未上报的 TailNode该任务失败)的功能的时候,扩展变得很复杂。
真是有点头疼!
1 Comments:
参考异步消息模型,把这部分东西中立出来。
大的,比如异步消息队列服务器;小的,比如Windows的PostMessage模型。
Post a Comment
<< Home