今日分享开始啦,请大家多多指教~

场景:一次迭代在灰度环境发版时,测试反馈说我开发的那个功能,查询接口有部分字段数据是空的,后续排查日志,发现日志如下:

feign.RetryableException: cannot retry due to redirection, in streaming mode executing POST

下面是业务、环境和分析过程下面是业务、环境和分析过程:

接口的业务场景 :我这个接口类似是那种报表统计的接口,它会请求多个微服务,把请求到的数据,统一返回给前端,相当于设计模式中的门面模式了。

后续由于这个接口 是串行请求其他微服务的,速度有些慢,后面修改代码从串行请求,改成并行(多线程)获取数据。

运维那边是通过判断http请求中cookie 或者 header中的某个数据,来区分请求是否要把流量打到灰度。

分析得出:应该是接口异步请求的时候cookie丢失,没走到灰度环境,找不到 这次迭代新开发的接口,导致的重定向到错误页面了。

验证:由于我代码是通过@Async异步注解,实现并行请求的,临时把五个接口的异步注解注释掉了,灰度在发版验证,数据能返回正常,说明流量打到灰度了。

说明问题就是并发请求的时候,子线程获取不到主线程的request 头信息,导致没有走到灰度。

下图就是灰度环境的流程图:

问题定位出来了,解决方案就是:让子线程能获取到主线程的 request 头信息,主线程把 数据透传到子线程。

我使用的是RequestContextHolder来传数据

什么是 RequestContextHolder?

RequestContextHolder 是spring mvc的一个工具类,顾名思义,持有上下文的Request容器。

如何使用:

//获取当前线程 request请求的属性

RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();

//设置当前线程 request请求的属性

RequestContextHolder.setRequestAttributes(attributes);

RequestContextHolder的会用到的几个方法

currentRequestAttributes:获得当前线程请求的属性(头信息之类的)

setRequestAttributes(attributes):设置当前线程 属性(设置头信息)

resetRequestAttributes:删除当前线程 绑定的属性

下面是他们的源码,可以简单看一下,原理是通过ThreadLocal来绑定数据的:

下面我编写了一套遇到问题的代码例子,以及解决的代码:

TestUserController

测试接口

TestRequestService

聚合数据的类

下面是两个请求 用户和订单请求类

OrderService 请求订单的服务的聚合方法

UserService 请求订单的服务的聚合方法

OrderController 你可以理解成其他其他微服务的接口(模拟写的一个接口,用来测试 请求接口的时候是否携带 请求头了)

下面三个接口的由来:

/v1/testUser/listUser 接口:就是串行调用其他服务接口 ,性能比较慢。

/v1/testUser/listUser2 接口:是通过@Async 异步注解,并行调用其他 系统的接口,性能是提升上去了,但灰度环境 是需要根据请求头里面的数据判断是否把流量打到灰度环境。

/v1/testUser/listUser3接口:对@Async注解没有找到透传 主线程request头信息的方案,就使用线程池+CompletableFuture.supplyAsync的方式 每次执行异步线程的时候,把主线程的 请求参数设置到子线程,然后通过try-finally 参数使用完之后RequestContextHolder.resetRequestAttributes() 删除参数。

注意:parallelStream它也是属于并行流操作,也要设置 请求头信息,虽说子线程(getDateResp3方法)能获取到主线程的请求头信息了,但是parallelStream 又相当于子线程的子线程了,它是获取不到的 主线程的attributes的,当时我就是没在parallelStream设置attributes,它没有走到灰度环境, 让我 耗费了两个多小时,代码加了四五次日志输出,才把这个问题定位出来,这是一个坑。。。

下面是代码:

上面说到,之前使用了@Async注解,子线程无法获取到上下文信息,导致流量无法打到灰度,然后改成 线程池的方式,每次调用异步调用的时候都手动透传下文(硬编码)解决了问题。

后面查阅了资料,找到了方案不用每次硬编码,来上下文透传数据了。

方案一:

继承线程池,重写相应的方法,透传上下文。

方案二:(推荐)

线程池ThreadPoolTaskExecutor,有一个TaskDecorator装饰器,实现这个接口,透传上下文。

方案一:继承线程池,重写相应的方法,透传上下文。

1、ThreadPoolTaskExecutor spring封装的线程池

ThreadPoolTaskExecutor 线程池代码如下:

1、MyCallable是继承Callable,创建MyCallable对象的时候已经把Attributes对象赋值给属性context了(创建MyCallable对象的时候因为实在当前主线程创建的,所以是能获取到请求的Attributes),在执行call方法前,先执行了RequestContextHolder.setRequestAttributes(context);

【把这个MyCallable对象的属性context 设置到setRequestAttributes中】 所以在执行具体业务时,当前线程(子线程)就能取得主线程的Attributes。

2、MyThreadPoolTaskExecutor类是继承了ThreadPoolTaskExecutor 重写了submit和submitListenable方法。

为什么是重写submit和submitListenable这两个方法了?

@Async AOP源码的方法位置是在:AsyncExecutionInterceptor.invoke

doSubmit方法能看出来

无返回值调用的是线程池方法:submit()

有返回值,根据不同的返回类型也知道:

返回值类型是:Future.class 调用的是方法:submit()

返回值类型是:ListenableFuture.class 调用的方法是:submitListenable(task)

返回值类型是:CompletableFuture.class调用的是CompletableFuture.supplyAsync这个在异步注解中暂时用不上的,就不考虑重写了。

2、ThreadPoolExecutor 原生线程池

ThreadPoolExecutor线程池代码如下:

像ThreadPoolExecutor主要重写execute方法,在启动新线程的时候先把Attributes取到放到MyRunnable对象的一个属性中,MyRunnable在具体执行run方法的时候,把属性Attributes赋值到子线程中,当run方法执行完了在把Attributes清空掉。

为什么只要重写了execute方法就可以了?

ThreadPoolExecutor大家都知道主要是由submit和execute方法来执行的。

看ThreadPoolExecutor类的submit具体执行方法是由父类AbstractExecutorService#submit来实现。

具体代码在下面贴出来了,可以看到submit实际上最后调用的还是execute方法,所以我们重写execute方法就好了。

submit方法路径及源码:

java.util.concurrent.AbstractExecutorService#submit(java.lang.Runnable)

方案二:(推荐)

ThreadPoolTaskExecutor线程池

实现TaskDecorator接口,把实现类设置到taskExecutor.setTaskDecorator(new MyTaskDecorator());

为什么设置了setTaskDecorator就能实现透传数据了?

主要还是看taskExecutor.initialize()方法,主要是重写了ThreadPoolExecutor的execute方法,用装饰器模式 增强了Runnable接口,源代码如下:

总结

无论是方案1还是方案2,原理都是先在当前线程获取到Attributes,然后把Attributes赋值到Runnable的一个属性中,在起一个子线程后,具体执行run方法的时候,把Attributes设置给当子线程,当run方法执行完了,在清空Attributes。

方案2实现比较优雅,所以推荐使用它。

工作没多久的时候觉得spring的使用很麻烦,但是工作久了慢慢发现spring一些小细节、设计模式运用得非常巧妙,很容易解决遇到的问题,只能说spring厉害。

今日份分享已结束,请大家多多包涵和指点!

超级计算机贝利,5年华为架构师1小时把SpringBoot项目并发提升了10倍,网友:牛掰...相关推荐

  1. 【阿里云】ACE认证流程“或将”于2021更新 ~ 对比华为云架构师认证 ~ 难度或将提高 ~ 含金量提升 ~ 如果雷同,纯属巧合

    [阿里云]ACE认证流程"或将"于2021更新 ~ 对比华为云架构师认证 ~ 难度或将提高 ~ 含金量提升 ~ 如果雷同,纯属巧合 https://www.bilibili.com ...

  2. harmonyos基于arm么,华为架构师解读:HarmonyOS低时延高可靠消息传输原理

    华为架构师解读:HarmonyOS低时延高可靠消息传输原理 [复制链接] 本文作者:zhangkesi,华为软件架构设计工程师 这是一篇HarmonyOS低时延高可靠消息传输原理的介绍,希望对你有所帮 ...

  3. 你与架构师相比差距在哪儿?如何提升

    你与架构师相比差距在哪儿?如何提升?程序员从开发做起,工作前3-5年可以是中级开发,但随着年限的上升还停留在开发上那么淘汰是迟早的事.在IT互联网行业除了保持不断学习,还要规划好自己的职业生涯. 你与 ...

  4. 【Java从零到架构师第1季】【并发 Concurrent 03】线程间通信_ReentrantLock_线程池

    持续学习&持续更新中- 守破离 [Java从零到架构师第1季][并发 Concurrent 03]线程间通信_ReentrantLock_线程池 线程间通信 线程间通信-示例 可重入锁Reen ...

  5. 华为架构师8年经验谈:从单体架构到微服务的服务化演进之路

    本次分享的技术大纲如下: 传统应用开发面临的挑战 服务化实践 服务化不是银弹 服务化架构的演进方向 一 .传统应用开发面临的挑战 挑战1-- 研发成本高 主要体现在如下几个方面: 代码重复率高 在实际 ...

  6. 华为架构师撰写的Netty核心笔记,从Java NIO到Netty的高级特性

    众所周知,Netty 作为当前流行的 NIO 框架,操作省时.省事还安全,在云计算.大数据通讯,电商.游戏等领域都有广泛的应用.如果是一个大型网站,内部接口非常多的情况下,好处很明显--首先就是长链接 ...

  7. Haydn解决方案数字化平台助力架构师1小时完成架构设计(实操篇)

    如何基于Haydn快速完成几种常用集成架构的设计? 如何基于Haydn快速完成几种常用部署架构的设计? 如何基于Haydn高效分享架构设计? 设计器的使用有哪些小Tips? 精彩预览 1. Haydn ...

  8. 年薪30万的Java架构师必会的springboot面试题

    [Java架构师面试网]收集整理了几乎整个架构师学习途中会遇到的面试题,希望大家都能早日圆自己的架构师梦~ 公众号:Java架构师面试网,关注回复"资料"即可领取精美整理的面试资料 ...

  9. 软考·系统架构师论文——论软件的高并发设计

    文章目录 说明 摘要 过渡 项目背景 论点理论 论点实践 结尾 说明 1.[摘要 300~330字] ① 项目介绍:时间.项目名.项目主要功能简述.作者角色及工作内容 ② 项目技术简介:正文理论/分论 ...

  10. 南京java架构师工资_java架构师工资一般是多少?怎么提升才能获得高薪?

    这几年Java一直以自身的优势霸占榜首,而且其实java架构师工资也是行业比较高的,下面来看看java架构师的工资到底有多少,当然这也是要区分地区的,以及怎样提高自己才能获得高薪呢? 当程序员容易,当 ...

最新文章

  1. poj2503 Babelfish
  2. 做三年地方网站不如别人打一场麻将
  3. 权限执行[Android开发常见问题-4] RunTime.exec()如何以root权限执行多条指令?
  4. 计算圆弧与矩形相交_GIS算法:3_拓扑空间关系计算模型DE-9IM
  5. 【转】python装饰器
  6. 【机器学习实战】第2章 k-近邻算法(kNN)
  7. mysql sakila world_MySQL 安装示例数据库(employee、world、sakila、menagerie 等)
  8. 【Qt教程】3.1 - Qt5 event事件、Label控件的鼠标事件捕获
  9. Linux、命令ps 各字段意思
  10. AWVS13破解docker一键安装
  11. 从今天开始阅读Java源码吧!
  12. 计算机win7如何加快开机速度,Win7系统如何提高开机速度?提高开机速度的三种方法步骤...
  13. python爬虫使用指南_如何使用Python构建Web爬虫[分步指南]
  14. 微信获取公众号二维码
  15. 【浪漫程序员系列】情人节给女友写代码表达爱意,让她感动到哭
  16. 网关服务器性能,服务网关API路由导致的性能问题分析
  17. 电子助力方向机控制模块_易力达电子助力控制器模块ECU威旺五菱长安电动助力方向机电脑板...
  18. Python 内置界面开发框架 Tkinter入门篇 丁
  19. 影响网站收录的主要因素是什么?
  20. 修改apk二进制文件工具

热门文章

  1. Java中字符串的全部知识_java基础教程之字符串的介绍,比较重要的一个知识点「中」...
  2. vscode 格式化不加分号_大前端时代你的 VSCode 插件
  3. python2中的print语句可以不用小括号。_Python基础语法 | 代码规范amp;判断语句amp;循环语句...
  4. skywalking学习
  5. CMU 15-213 Introduction to Computer Systems学习笔记(5) Machine-Level Programming-Control
  6. java monitor 翻译_Java 对象锁与monitor的区别
  7. qt 读取mysql数据库_qt 读取mysql数据库
  8. LeetCode 141. Linked List Cycle 判断链表是否有环 C++/Java
  9. 第三视角Beta答辩总结
  10. maven-聚合与继承