第三方接口的自动化管理---智能路由

(3)增加重试策略:若某次请求失败后,触发重试策略完成用户本次请求(用户无感知,耗时久一点)

优化后的异常处理流程如下图:

![在这里插入图片2描述]

优化后的处理流程

优化后的故障处理流程是自动切走,手动切回,一次通道故障的详细处理流程如下:

(1)实时监控模块检测到通道故障后,将该通道置为不可用,同时发送告警信息给相关负责人;

(2)业务系统触发重试策略,根据路由模块返回的备份通道请求备份通道,完成本次用户请求;

(3)图像认证技术人员立刻定位问题并联系第三方通道负责人,对方确认问题和恢复情况后反馈到图像认证负责人;

(4)图像认证技术将通道置为可用,刷新认证路由缓存接口,将线上流量放入该通道;

(5)如果通道恢复,则用户可以正常认证,本次故障结束;

(6)如果认证通道未恢复,再次去联系第三方认证通道负责人处理,如此往复,直到该通道所有认证场景恢复正常,本次故障结束。

主要完成的改进点

(1)细化监控告警维度,做到精确、实时监控告警,保证第三方通道故障的快速发现;

(2)优化处理流程,请求A 通道失败后继续请求备份通道,增加通道复查策略,提高一次请求的成功率;

(3)大幅降低处理第三方通道故障的人力成本。

半自动化阶段存在的问题

半自动化阶段已将故障处理大幅度简化,但此时的系统还存在以下问题:

(1)第三方通道恢复依赖于第三方通道技术人员的反馈,导致通道恢复耗时较久;

(2)一次第三方通道故障涉及到的系统和人员较多,人工无法保证准确性和及时的处理;

(3)虽然故障时支持自动切走,但是恢复后需要手动切回已恢复的第三方通道,人力没有完全解放出来;

由于半自动化阶段还是存在以上问题,没有达到理想状态,所以智能(全自动)路由诞生了…

智能路由阶段

主要思想

针对线上请求流量进行监控和结果实时统计,一旦触发阈值则自动切走线上流量(将该故障第三方通道路由状态置为关闭状态),刷新路由模块缓存机制,完成关闭故障通道操作。 同时,自动化阶段也同样支持手动一键切走,并且考虑周末在外不方便,可考虑支持移动端一键切走故障通道功能(比如发送是否切换告警短信,回复1切换等思路)。

认证通道故障时,智能路由阶段处理流程如下:

![在这里插入图片3描述]

智能路由处理流程

故障通道智能化阶段,故障处理自动切走,自动切回,一次第三方通道故障的处理流程如下:

(1)实时监控模块检测到某个第三方成功率异常时,发送告警信息给业务系统技术人员,同时自动将故障通道置为不可用;

(2)同时刷新路由缓存机制,路由模块实时读取通道缓存信息从而将故障通道线上流量全部切走;

(3)监控模块在将故障通道置为不可用一段时间后(时间可配置),尝试对故障通道放部分流量进来用以检测通道是否恢复正常;

(4)如果放进来的这部分量的成功率正常,监控则继续放2倍的量,以此类推,直到通道全量,监控将通道置为可用;

(5)如果放进来的这部分量成功率异常(并且识别的请求会触发重试策略,用户无感知),则继续将该通道直接置为不可用,监控隔一段时间(时间递增)后再继续放量,直到通道恢复为可用;

(6)业务系统技术人员发现通道故障后,可以向第三方通道询问故障原因、并记录,留作日后分析(阈值分析)使用。

3、实现思路

实时监控

借助阿里Sentinel组件完成实时统计

阿里Sentinel我就不用多介绍了,不了解的建议看看,绝对实用到爆炸,哈哈。。。
Sentinel官网:https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D
Sentinel主要特性:
![在这里插入图片4描述]

下面我就介绍怎么通过Sentinel完成我们的实时统计(监控)功能:

  1. 首先需要梳理出业务系统有哪些场景/服务/接口,在sentinel中被定义为资源,其实sentinel中所说的资源可以是任何对象,可以是几行代码块、一个方法、或者一个类都可以。
  2. 我们把需要控制流量的资源用sentinel保护起来,也就是用 Sentinel API SphU.entry(“资源名称”) 和 entry.exit() 包围起来即可。
    代码示例:

public static void main(String[] args) {
initFlowRules();
while (true) {
Entry entry = null;
try {
entry = SphU.entry(“HelloWorld”);
/您的业务逻辑 - 开始/
System.out.println(“hello world”);
/您的业务逻辑 - 结束/
} catch (BlockException e1) {
/流控逻辑处理 - 开始/
System.out.println(“block!”);
/流控逻辑处理 - 结束/
} finally {
if (entry != null) {
entry.exit();
}
}
}
}

完成以上两步后,代码端的改造就完成了。当然,我们也提供了 注解支持模块,可以以低侵入性的方式定义资源。

  1. 当接口资源被保护起来,用户请求场景接口后,我们可以在日志:

~/logs/csp/${appName}-metrics.log.xxx

里看到下面的输出:

|–timestamp-|------date time----|-resource-|p |block|s |e|rt
1529998904000|2018-06-26 15:41:44|HelloWorld|20|0 |20|0|0
1529998905000|2018-06-26 15:41:45|HelloWorld|20|5579 |20|0|728
1529998906000|2018-06-26 15:41:46|HelloWorld|20|15698|20|0|0
1529998907000|2018-06-26 15:41:47|HelloWorld|20|19262|20|0|0
1529998908000|2018-06-26 15:41:48|HelloWorld|20|19502|20|0|0
1529998909000|2018-06-26 15:41:49|HelloWorld|20|18386|20|0|0

其中resource代表资源名称, p 代表通过的请求, block 代表被阻止的请求, s 代表成功执行完成的请求个数, e 代表用户自定义的异常, rt 代表平均响应时长。
可以看到,这个程序每秒稳定输出 “hello world” 20 次,这个和控制台规则中预先设定的阈值是一样的。

  1. 有了上面的文件名后缀为 -metrics.log.xxx 的持久化调用记录日志后,我们可以调用Sentinel提供的实时监控接口获取持久化调用记录,并通过分类汇总得出 每个第三方下的场景(接口)的总数、异常数、耗时等信息,并通过计算得数成功率、失败率等信息,用于实时监控模块关闭通道的依据。

实时查询
相关 API: GET /metric

curl http://localhost:8719/metric?startTime=XXXX&endTime=XXXX&maxLines=XXXX // 查询当前系统所有资源信息,智能路由使用
curl http://localhost:8719/metric?identity=XXX&startTime=XXXX&endTime=XXXX&maxLines=XXXX

需指定以下 URL 参数:

identity:资源名称
startTime:开始时间(时间戳)
endTime:结束时间
maxLines:监控数据最大行数

返回和 资源的秒级日志 格式一样的内容。例如:

1529998904000|2018-06-26 15:41:44|interface1|100|0|0|0|0
1529998905000|2018-06-26 15:41:45|interface2|4|5579|104|16|728
1529998906000|2018-06-26 15:41:46|interface3|0|15698|0|0|0
1529998907000|2018-06-26 15:41:47|interface1|0|19262|0|0|0
1529998908000|2018-06-26 15:41:48|interface1|0|19502|0|0|0
1529998909000|2018-06-26 15:41:49|interface2|0|18386|0|0|0
1529998910000|2018-06-26 15:41:50|interface4|0|19189|0|0|0
1529998911000|2018-06-26 15:41:51|interface1|0|16543|0|0|0
1529998912000|2018-06-26 15:41:52|interface1|0|18471|0|0|0
1529998913000|2018-06-26 15:41:53|interface1|0|19405|0|0|0

  1. 查出来后利用Java进行分类汇总统计,实时统计方法如下:

private static final String EX = “?”;
private static final String SPLIT = “\|”;
/**

  • 一定时间间隔统计调用流水情况
  • @param timeSpan 时间间隔(以秒为单位)
  • @return
  • @throws ParseException
    */
    public Map<String,Map<String,Long>> statisticBlockCount(int timeSpan) throws ParseException {
    String result = requestSentinel(timeSpan, Calendar.SECOND,null);
    if (null == result || StringUtils.isEmpty(result)) {
    log.info(“实时统计异常,统计结果为空,请确认线上是否有调用流水!!!”);
    return null;
    }
    String[] resultList = result.split(“\n”);
    String[] copyOfResultList = Arrays.copyOf(resultList, (resultList.length - 1));
    List recodes = Arrays.stream(copyOfResultList).map(resp -> {
    String[] rpt = resp.split(SPLIT);
    SentinelStatistic record = new SentinelStatistic();
    record.setResourceName(rpt[1]);
    record.setBlockCount(Integer.parseInt(rpt[3]));
    record.setSuccessCount(Integer.parseInt(rpt[2]) - Integer.parseInt(rpt[5]));
    record.setPassCount(Integer.parseInt(rpt[2]));
    record.setExceptionCount(Integer.parseInt(rpt[5]));
    record.setTotalCount(Integer.parseInt(rpt[2] + Integer.parseInt(rpt[3])));
    return record;
    }).collect(Collectors.toList());
    Map<String, Long> successResultMap = new HashMap<>(3 << 1);
    Map<String, Long> blockResultMap = new HashMap<>(3 << 1);
    Map<String, Long> exceptionResultMap = new HashMap<>(3 << 1);
    Map<String, Long> totalResultMap = new HashMap<>(3 << 1);
    Map<String, Long> passResultMap = new HashMap<>(3<<1);
    // 分组
    Map<String, List> countMap = recodes.stream().collect(
    Collectors.groupingBy(SentinelStatistic::getResourceName));
    Set set = countMap.keySet();
    // 遍历统计存到缓存中
    for (String resourceName : set) {
    List list = countMap.get(resourceName);
    long successSum = 0;
    long blockSum = 0;
    long exceptionSum = 0;
    long totalSum = 0;
    long passSum = 0;
    for (int i = 0; i < list.size(); i++) {
    successSum = successSum + list.get(i).getSuccessCount();
    blockSum = blockSum + list.get(i).getBlockCount();
    exceptionSum = exceptionSum + list.get(i).getExceptionCount();
    totalSum = totalSum + list.get(i).getTotalCount();
    passSum = passSum+list.get(i).getPassCount();
    }
    blockResultMap.put(resourceName,blockSum);
    successResultMap.put(resourceName,successSum);
    exceptionResultMap.put(resourceName,exceptionSum);
    totalResultMap.put(resourceName,totalSum);
    passResultMap.put(resourceName,passSum);
    }
    Map<String, Map<String,Long>> combineResultMap = new HashMap<>(3<<1);
    // blockResultMap 代表被阻止的请求资源名及总数
    combineResultMap.put(“blockResultMap”,blockResultMap);
    // blockResultMap 代表成功的资源名及总数
    combineResultMap.put(“successResultMap”,successResultMap);
    // exceptionResultMap 代表异常资源名及总数
    combineResultMap.put(“exceptionResultMap”,exceptionResultMap);
    // totalResultMap 代表总的请求总数=blockResultMap+successResultMap+exceptionResultMap+passResultMap
    combineResultMap.put(“totalResultMap”,totalResultMap);
    // passResultMap 代表通过的
    combineResultMap.put(“passResultMap”,passResultMap);
    return combineResultMap;
    }

解析后的结构数据如下:

{
“successResultMap”:{
“interface1”:100,
“interface2”:104,
“st-interface3”:14,
“st-interface4”:2,
“ali-interface5”:1
},
“exceptionResultMap”:{
“interface1”:0,
“interface2”:16,
“interface3”:0,
“interface4”:0,
“interface5”:0
},
“totalResultMap”:{

写在最后

最后我想说:对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!

这里附上上述的技术体系图相关的几十套腾讯、头条、阿里、美团等公司2021年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

相信它会给大家带来很多收获:

当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
352814307)]

[外链图片转存中…(img-xY5Dpbxg-1715352814308)]

当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值