基于Javassist字节码增强实现本地公参传递

这里是weihubeats,觉得文章不错可以关注公众号小奏技术,文章首发。拒绝营销号,拒绝标题党

背景

测试线上的公参是通过skywalking agent的方式进行传递的。

如果是本地则会因为获取不到公参报错影响正常测试。所以需要本地也进行公参传递

正常链路是 前端 -> 网关 -> 通过skywalking进行公参处理全链路传递 -> 业务系统通过skywalking侧的sdk(TraceContext)进行公参获取

本地传递主要是两个问题

  1. 本地启动不想接入skywalking的agent启动
  2. 本地测试不走网关,公参不会存放在skywalking的上下文中

所以需要写一个简单的本地测试公参传递

目标

我们的目的很简单:

  1. 业务零侵入
  2. 能获取到公参

方案

业务本身获取公参有一个XiaoZouBaseRequest工具类。然后会提供公参的方法获取

比如

public class XiaoZouBaseRequest implements Serializable {public String getDevice() {return TraceContext.getCorrelation(DEVICE).orElse("");}
}

我们如何做到业务无感知的实现公参获取呢?

其实很简单

我们写一个最简单的拦截器。将用户请求的请求头放入一个上下文中。然后对XiaoZouBaseRequest这个类就行代理。从我们的上下文中获取公参,而不是从skywalking

实现

公参上下文

public class RequestContextHolder {private static final TransmittableThreadLocal<Map<String, String>> requestHeaders = new TransmittableThreadLocal<>();public static void setRequestHeaders(Map<String, String> headers) {requestHeaders.set(headers);}public static Map<String, String> getRequestHeaders() {return requestHeaders.get();}public static void clear() {requestHeaders.remove();}}

这里上下文的存储我们不使用ThreadLocalInheritableThreadLocal.

使用阿里transmittable-thread-local提供的TransmittableThreadLocal 可以解决线程池上线文丢失问题

需要添加依赖

<dependency><groupId>com.alibaba</groupId><artifactId>transmittable-thread-local</artifactId><version>2.14.4</version>
</dependency>

实际如果为了简单方便也可以直接使用ThreadLocal

web拦截器

拦截器很简单实现HandlerInterceptor即可

@Component
@Profile("dev")
public class LocalRequestHeaderInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {Enumeration<String> headerNames = request.getHeaderNames();Map<String, String> headers = new HashMap<>();while (headerNames.hasMoreElements()) {String key = headerNames.nextElement();String value = request.getHeader(key);headers.put(key, value);}RequestContextHolder.setRequestHeaders(headers);return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {RequestContextHolder.clear();}
}
  1. 使用@Profile保证只在dev生效
  2. 如果是sdk的方式@Component是没用的,我们需要通过其他方式让该bean注入spring中

拦截器配置

@Configuration
@Profile("dev")
public class LocalRequestMvcConfigurer implements WebMvcConfigurer {@Autowiredprivate LocalRequestHeaderInterceptor interceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(interceptor);}
}

字节码增强XiaoZouBaseRequest

@Component
@Profile("dev")
public class XiaoZouBaseRequestEnhancer implements BeanFactoryPostProcessor {private static final String GET_DEVICE = "getDevice";private static final String GET_DEVICE_BODY = "{\n" +"        return com.xiaozou.common.interceptor.RequestContextHolder.getRequestHeaders().get(com.xiaozou.common.CommonParameters.DEVICE);\n" +"    }";private void enhanceBaseRequest() {try {ClassPool pool = ClassPool.getDefault();pool.appendClassPath(new LoaderClassPath(Thread.currentThread().getContextClassLoader()));CtClass cc = pool.get("com.xiaozou.common.XiaoZouBaseRequest");enhanceMethod(cc, GET_DEVICE, GET_DEVICE_BODY);cc.toClass();cc.detach();} catch (Exception e) {throw new RuntimeException(e);}}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {enhanceBaseRequest();}private void enhanceMethod(CtClass cc, String methodName, String methodBody) throws Exception {cc.getDeclaredMethod(methodName).setBody(methodBody);}}

这里有比较多的小细节

  1. 增强XiaoZouBaseRequestEnhancer需要在XiaoZouBaseRequestEnhancer加载之前。因为如果一个类已经被类加载器加载,将无法再次修改它的字节码。所以这里使用BeanFactoryPostProcessor来扩展
  2. 在字节码增强中使用的类和方法都必须使用全类名,不然会报错java.lang.RuntimeException: javassist.CannotCompileException: [source error] no such class: RequestContextHolder

测试

写一个参数DTO

public class TestDTO extends XiaoZouBaseRequest {
}

一个测试controller

    @GetMapping("/test")public String test(TestDTO testDTO) {System.out.println("device = " + testDTO.getDevice());}

能够正常获取到公参就说明没问题

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/656762.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一体化设计:兼容多种OS系统Linux网关楼宇DDC

在工业物联网&#xff08;IIoT&#xff09;和智能建筑领域&#xff0c;钡铼网关具备高度灵活性与强大计算能力的边缘网关产品正逐渐成为推动行业智能化转型的关键要素。本文将详细介绍的基于Linux系统的4G工业智能网关&#xff0c;不仅拥有NXP i.MX8M Mini四核64位处理器的强大…

设计一个数据库连接池

注意点 限制连接池中最多可以容纳的连接数目&#xff0c;避免过度消耗系统资源当客户请求连接&#xff0c;而连接池中所有连接都已被占用时&#xff0c;一种方式是让客户一直等待&#xff0c;直到有空闲连接&#xff0c;另一种方式是为客户分配一个新的临时连接。当客户不在使…

《golang设计模式》第三部分·行为型模式-10-模板方法(Template Method)

文章目录 1. 概述1.1 角色1.2 类图 2. 代码示例2.1 设计2.2 代码2.3 类图 1. 概述 模板方法&#xff08;Template Method&#xff09;用来定义算法的框架&#xff0c;将算法中的可变步骤定义为抽象方法&#xff0c;指定子类实现或重写。 1.1 角色 AbstractClass&#xff08;…

质数判断(5种方法)

1.普通枚举所有因数 if(n<2) return 0; if(n2) return 1; for(int i2;i<n/2;i) if(!(n%i)) return 0; return 1; 2.枚举所有因数对中较小的部分&#xff08;到sqrt(n)&#xff09; if(n<2) return 0; if(n2) return 1; int tmpsqrt(n)//注意&#xff0c;n为大整数时…

Kettle-Docker部署+Sqlserver数据同步Mysql+Start定时任务

一. 背景介绍 1. ETL是什么 ETL&#xff08;Extract-Transform-Load&#xff09;&#xff0c;即数据抽取、转换、装载的过程。它是一种思想&#xff0c;主要是说&#xff0c;从不同的数据源获取数据&#xff0c;并通过对数据进行处理&#xff08;格式&#xff0c;协议等转换&a…

Java-12a.Spring 中通过 TaskDecorator 配置默认异步线程池

Java-12.Spring 中通过 ThreadPoolTaskExecutor 和 AsyncConfigurerSupport 配置默认异步线程池 前言 在Spring框架中&#xff0c;TaskDecorator 是一个接口&#xff0c;它可以用来自定义由 ThreadPoolTaskExecutor 或其他任务执行器管理的任务的装饰行为。这通常用于在执行任…

第32关 k8s集群管理开源神器 - k9s

------> 课程视频同步分享在今日头条和B站 大家好&#xff0c;我是博哥爱运维。 随着我们管理维护的K8S集群上线&#xff0c;怎么管理好集群上面成百上千的服务pod&#xff0c;就是我们该操心的事情了。这里博哥把在生产中一直在用的一个开源管理工具k8s&#xff0c;github…

Jenkins如何从GIT下拉项目并启动Tomcat

一、先添加服务器 二、添加视图 点击控制台输出&#xff0c;滑到最下面&#xff0c;出现这个就说明构建成功了&#xff0c;如果没有出现&#xff0c;说明构建有问题&#xff0c;需要解决好问题才能启动哦~

C++实现通讯录管理系统

目录 1、系统需求 2、创建项目 2.1 创建项目 3、菜单功能 4、退出功能 5、添加联系人 5.1 设计联系人结构体 5.2 设计通讯录结构体 5.3 main函数中创建通讯录 5.4 封装联系人函数 5.5 测试添加联系人功能 6、显示联系人 6.1 封装显示联系人函数 7、删除联系人 7.1…

GPT栏目:yarn 安装

GPT栏目&#xff1a;yarn 安装 一、前言 在跟GPT交互的时候&#xff0c;发现最近gpt4给出的答案率有了比较明显的提高&#xff0c;简单记录一下&#xff0c;我用gpt4拿到的答案吧。 本人已按照这个步骤成功 二、具体步骤 要安装 yarn&#xff0c;你可以按照以下步骤进行操作…

MyBatis常见面试题汇总

说一下MyBatis执行流程&#xff1f; MyBatis是一款优秀的基于Java的持久层框架&#xff0c;它内部封装了JDBC&#xff0c;使开发者只需要关注SQL语句本身&#xff0c;而不需要花费精力去处理加载驱动、创建连接等的过程&#xff0c;MyBatis的执行流程如下&#xff1a; 加载配…

Google Chrome 常用的几个参数

1 右键--Google Chrome--属性--目标 参数作用--disable-infobars此计算机将不会再收到 Google Chrome 更新&#xff0c;因为 Windows XP 和 Windows Vista 不再受支持。适用于 xp、2003 的 49.x.x.x 版本。示例1--ingore-certificate-errors忽略证书错误--disable-background-…

【计算机网络】【练习题及解答】【新加坡南洋理工大学】【Computer Control Network】

说明&#xff1a; 仅供学习使用。 一、题目描述 题目共4问&#xff0c;描述网络通信中的 帧传输时延&#xff08;Frame Delay&#xff09;、传播时延&#xff08;Propagation Delay&#xff09;&#xff0c;以及 链接利用率&#xff08;Link Utilization&#xff09; 的相关…

【NodeJS】005- NodeJS的NVM与express框架

1.NVM介绍与使用 1.介绍 nvm 全称 Node Version Manager 顾名思义它是用来管理 node 版本的工具,方便切换不同版本的Node.js 2.使用 nvm 的使用非常的简单,跟 npm 的使用方法类似 3.下载安装 首先先下载 nvm,下载地址 https://github.com/coreybutler/nvm-windows/rel…

Vue2:通过代理服务器解决跨域问题

一、场景描述 现在的项目大多数是前后端分离的。Vue前端项目通过ajax去请求后端接口的时候&#xff0c;会有同源策略的限制。从而产生跨域问题。 二、基本概念 1、什么是同源策略&#xff1f; 就是前端服务和后端服务的协议名&#xff0c;IP或主机名&#xff0c;端口号不完…

0x02递推与递归

0x02递推与递归 递推者&#xff0c;自小而大&#xff0c;循序渐进&#xff1b;递归者&#xff0c;由上而下&#xff0c;分而治之 文章目录 0x02递推与递归例题T1&#xff1a;T2&#xff1a;T3&#xff1a;T4:T5T6 例题 T1&#xff1a; 92. 递归实现指数型枚举 - AcWing题库 …

Mysql 插入数据

1 为表的所有字段插入数据 使用基本的INSERT语句插入数据要求指定表名称和插入到新记录中的值。基本语法格式为&#xff1a; INSERT INTO table_name (column_list) VALUES (value_list); 使用INSERT插入数据时&#xff0c;允许列名称列表column_list为空&#xff0c;此时&…

网络安全战略中的法律问题

文章目录 前言一、各国网络安全的法律需求陡增二、战争的多维化使对法律的需求也越加多样(一)从平时到战时,网络斗争的平战模糊性增加法律适用难度(二)从高维到低维,博弈的多维性使法律需求层次更加丰富(三)从防御到进攻,网络空间的攻防不均衡改变法律斗争重点三、各国…

flink分别使用FilterMap和ProcessFunction实现去重逻辑

背景 在日常的工作中&#xff0c;对数据去重是一件很常见的操作&#xff0c;比如我们只需要保留重复记录的第一条&#xff0c;而忽略掉后续重复的记录&#xff0c;达到去重的效果&#xff0c;本文就使用flink的FilterMap和ProcessFunction来实现去重逻辑 FilterMap和ProcessF…

Compose | UI组件(十) | Box,Surface - 帧布局

文章目录 前言Box 组件的参数说明Box 组件的使用Surface 的参数说明Surface 的使用 总结 前言 Box组件是 按子组件依次叠加 的布局组件&#xff0c;相当传统View中的 FrameLayout Box 组件的参数说明 Composable inline fun Box(modifier: Modifier Modifier, …