跨系统服务调用消息单向状态监控

最近在公司工作的时候,碰到一个问题:现在有两个系统A、B;这两个系统都是以Dubbo服务的形式对外提供服务。

现在需要做的是,系统A需要在完成一些业务操作的时候,同时将处理完成的消息接入到系统B,待系统B处理完成之后,再反馈给系统A处理结果。由于系统B上并没有做任何的消息状态持有,所以这等于是一个单向的服务调用,其流程图如下:
系统A与系统B的信息接入

由于系统A和系统B是不同的项目组进行维护,所以定下来的Remote方案是,系统B仅仅同步提供数据处理结果,而不会对数据的状态进行任何的维护。举个例子,就比如系统A推送10条数据给系统B,这时可能处于某些原因(例如锁竞争等),导致其中一条数据的处理时间非常长,那么系统B在等待一小段时间之后,将不再等待这条数据的处理,而是直接将处理结果反馈,其反馈信息就是“9条处理完成,还有1条不知道怎么样了”;在这之后,系统B不会再对该条信息的状态做任何的操作。

这样一来,对所有数据的状态维护的工作将都有系统A来做。在这里,我给出的一个设计思想是:采用自我状态监控的有序链表进行状态的维护。例如:
队列

现在,假设系统A向系统B推送了10条信息(系统A本身维护了这10条数据的顺序性),那么就会在这个有序链表中创建这10个节点,现在系统B接收到这10条消息,便会开始处理。这里我大概地对消息处理的结果进行了一个简单的分级:SUCCESS(处理成功),ERROR(这条数据存在问题,可能需要人工介入,在系统上进行操作),BLOCKED(这条数据不知道处理地怎么样了)。当系统A收到系统B的反馈之后,将会唤醒扫描线程(ScannerThread)进行状态更改:
扫描线程的作用

考虑到并发下的情况,所以这里维护了一个“批次锁”,其设计也就是跟ConcurrentHashMap中的Segment Lock类似,这里我采用AQS的方式去设计,防止其他线程存在例如重复发送的情况,错误修改其状态。

现在假设系统B处理的结果是,数据D、F处理BLOCKED状态,而数据H处理ERROR状态,其他的处理成功,那么系统A接收到反馈消息之后,扫描线程处理完成的结果就是:
批次锁,与扫描线程的起始

扫描线程(ScannerThread)
对于每一个节点中维护了一个计数器,用来记录该条数据第几次被标记为未知或者异常(这个次数与状态清除策略有关,一会详细说明)。这时,ScannerThread便会释放该批次锁,而立马唤醒状态监控线程来竞争这个锁(StatusMoniterThread),该线程主要做的事情有两件:
1、扫描该链表中的状态,做出相应的决策。目前我暂定的决策是:每当连续3个以上节点为SUCCESS,或者该段所有SUCCESS节点的任意一个节点的版本计数器达到3以上,便会强制清除。也就是说,第一次扫描清除的结果是:
扫描线程扫描完毕

2、提取异常的数据,交给相应的补偿线程(CompensateThread)去做。所谓补偿线程,就是将异常的数据再次数据。具体详情看如下说明。

补偿线程(CompensateThread)
该线程做的事情非常简单,插队和线上通知。线程通知,这就很简单,当获取到处于ERROR状态的消息,便通过邮件,SM等形式向指定的管理员发出预警,并把该消息的“已通知”设为true,防止多次重复发送。然而对于“插队”,请看下图:
补偿线程机制

这里的Piper(管道)直接和消息发送线程(MessageSender)直接对接,其所谓的“插队”,其实就是对Piper的一个监控而已。

消息发送线程(MessageSender)
该线程主要的作用就是发送消息,其结构也非常简单,如下图:
消息发送线程中的队列

这块我采用的策略是,让该线程进行自旋操作:获取“消息队列锁”,并进行自旋操作,当从Piper中获取数据,便将数据放入队列后直接发送;而当消息被正常添加至消息队列中,会回调监控Piper的,如果获得数据,便放置在队列的Head,再放入新的数据。这样尽量保证重发的数据能够在队列的头处(因为重发的数据的序列号肯定比新数据要小,其实,放在队列尾部也是可以的,只是会影响ScannerThread的遍历效率)。

二次处理
由于系统B确实做了防重处理,所以少量的重复信息并不会带来很大的影响。当处于BLOCKED的数据再次被发送到系统B,然后再次获得反馈消息时,要么BLOCKED状态更改为SUCCESS,要么继续为BLOCKED,但是版本号+1。假设出现某种原因,导致消息F一直处于BLOCKED状态,那么重复发送3次之后,便会得到这样的结果:
二次处理

那么,被清除之后就会得到这样的结果:
二次处理

其他
1. 当处于ERROR的数据被线上人员修改之后,便会维护到ScannerThread中,让它将其ERROR状态,改为BLOCKED,其中每一次的状态变更都会重置版本号
2. 当处于BLOCKED数据的版本号达到一定次数(也是3次以上),也将会通知线上人员查询系统状态,找出问题所在
3. 灾备恢复,考虑到灾备的恢复,这里我暂时用的方法是依赖Redis集群,将异常数据的关键信息(也就是通过这个关键信息能够从数据库中找到该数据)同步至Redis中,当系统重启时,会第一时间读取Redis数据,并搭建发送队列,防止数据丢失。
4. 至于粒度更加精准的灾备方案,还在进一步思考中,目前条件还不至于将这一部分单独拿出来作为分布式中一个高可用的集群。

这是本人在项目中一点点小小的思路,各位大牛如果有更好的想法,欢迎留言,一同进步。Java招聘群:325951837 欢迎各路大神和HR入驻。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值