📝 Part 8:构建统一上下文框架设计与实现(实战篇)

在实际项目中,我们往往需要处理多种上下文来源,例如:

  • Web 请求上下文(RequestContextHolder
  • 日志追踪上下文(MDC
  • Dubbo RPC 上下文(RpcContext
  • 分布式链路追踪(Sleuth
  • 自定义业务上下文(如租户、用户信息等)

如果每个组件都单独维护自己的上下文逻辑,不仅容易出错,而且难以扩展和维护。因此,构建一个统一的上下文管理框架 是非常有必要的。

本文将带你从零开始设计并实现一个可插拔、支持多数据源、自动传播的统一上下文框架,并结合 Spring Boot 实现完整的集成方案。


一、目标设计

我们希望这个框架具备以下能力:

功能描述
✅ 多上下文来源兼容支持 Web、RPC、日志、异步任务等多种上下文来源
✅ 自动传播机制在线程切换、异步调用时自动传播上下文
✅ 配置化扩展可通过配置启用或禁用特定上下文模块
✅ 易于接入 Spring支持与 Spring Boot 的无缝集成
✅ 支持跨服务透传如 Dubbo、Feign 等微服务通信场景
✅ 日志自动注入traceId、userId 等字段自动写入 MDC

二、整体架构设计图

+-----------------------------+
|       ContextManager        |
|   - set(key, value)         |
|   - get(key)                |
|   - clear()                 |
+------------+----------------+|+--------v---------+|  ContextProvider   ||  (接口)            ||  - readFrom()      ||  - writeTo()       |+--------------------+/     |      \/      |       \
+---------+ +----------+ +-----------+
|WebContext | |RpcContext| |LogContext |
|Provider   | |Provider  | |Provider   |
+-----------+ +----------+ +-----------+

三、核心接口定义

1. ContextManager(上下文管理器)

public interface ContextManager {void set(String key, String value);String get(String key);void clear();
}

2. ContextProvider(上下文提供者)

public interface ContextProvider {void readFrom(Map<String, String> contextMap); // 从当前上下文中读取数据到 mapvoid writeTo(Map<String, String> contextMap);   // 将 map 中的数据写入当前上下文
}

四、具体实现示例

1. WebContextProvider(基于 RequestContextHolder)

@Component
public class WebContextProvider implements ContextProvider {@Overridepublic void readFrom(Map<String, String> contextMap) {RequestAttributes attrs = RequestContextHolder.getRequestAttributes();if (attrs != null) {for (String key : attrs.getAttributeNames(RequestAttributes.SCOPE_REQUEST)) {Object val = attrs.getAttribute(key, RequestAttributes.SCOPE_REQUEST);if (val instanceof String) {contextMap.put(key, (String) val);}}}}@Overridepublic void writeTo(Map<String, String> contextMap) {RequestAttributes attrs = RequestContextHolder.getRequestAttributes();if (attrs != null) {contextMap.forEach((key, value) -> attrs.setAttribute(key, value, RequestAttributes.SCOPE_REQUEST));}}
}

2. RpcContextProvider(基于 Dubbo RpcContext)

@Component
@ConditionalOnClass(name = "org.apache.dubbo.rpc.RpcContext")
public class RpcContextProvider implements ContextProvider {@Overridepublic void readFrom(Map<String, String> contextMap) {RpcContext context = RpcContext.getContext();context.getAttachments().forEach(contextMap::put);}@Overridepublic void writeTo(Map<String, String> contextMap) {RpcContext context = RpcContext.getContext();contextMap.forEach(context::setAttachment);}
}

3. LogContextProvider(基于 MDC)

@Component
public class LogContextProvider implements ContextProvider {@Overridepublic void readFrom(Map<String, String> contextMap) {Map<String, String> mdcMap = MDC.getCopyOfContextMap();if (mdcMap != null) {contextMap.putAll(mdcMap);}}@Overridepublic void writeTo(Map<String, String> contextMap) {MDC.setContextMap(contextMap);}
}
}

五、统一上下文管理器实现

@Component
public class DefaultContextManager implements ContextManager {private final List<ContextProvider> providers;public DefaultContextManager(List<ContextProvider> providers) {this.providers = providers;}@Overridepublic void set(String key, String value) {Map<String, String> contextMap = new HashMap<>();contextMap.put(key, value);providers.forEach(p -> p.writeTo(contextMap));}@Overridepublic String get(String key) {Map<String, String> contextMap = new HashMap<>();providers.forEach(p -> p.readFrom(contextMap));return contextMap.get(key);}@Overridepublic void clear() {providers.forEach(p -> p.writeTo(new HashMap<>()));}
}

六、Spring Boot 自动装配

你可以创建一个自动装配模块,用于注册所有上下文提供者。

示例配置类:

@Configuration
public class ContextAutoConfiguration {@Beanpublic ContextManager contextManager(List<ContextProvider> providers) {return new DefaultContextManager(providers);}@Beanpublic ContextProvider webContextProvider() {return new WebContextProvider();}@Bean@ConditionalOnClass(name = "org.apache.dubbo.rpc.RpcContext")public ContextProvider rpcContextProvider() {return new RpcContextProvider();}@Beanpublic ContextProvider logContextProvider() {return new LogContextProvider();}
}

七、拦截器自动注入上下文(可选)

为了确保上下文在请求开始时就已加载,你可以编写一个拦截器自动注入上下文。

@Component
public class ContextInterceptor implements HandlerInterceptor {@Autowiredprivate ContextManager contextManager;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String traceId = UUID.randomUUID().toString();contextManager.set("traceId", traceId);contextManager.set("userId", request.getHeader("X-User-ID"));return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {contextManager.clear();}
}

八、使用方式(Controller 层)

@RestController
public class UserController {@Autowiredprivate ContextManager contextManager;@GetMapping("/user")public String getCurrentUser() {String userId = contextManager.get("userId");String traceId = contextManager.get("traceId");return String.format("User ID: %s, Trace ID: %s", userId, traceId);}
}

九、异步任务中的自动传播(TTL 支持)

为了保证异步任务也能正确传递上下文,我们可以对 ContextManager 做一层封装:

@Component
public class TtlContextManager implements ContextManager {private static final TransmittableThreadLocal<Map<String, String>> contextHolder = new TransmittableThreadLocal<>();private final ContextManager delegate;public TtlContextManager(ContextManager delegate) {this.delegate = delegate;}@Overridepublic void set(String key, String value) {Map<String, String> map = contextHolder.get();if (map == null) {map = new HashMap<>();}map.put(key, value);contextHolder.set(map);delegate.set(key, value);}@Overridepublic String get(String key) {Map<String, String> map = contextHolder.get();if (map != null) {return map.get(key);}return delegate.get(key);}@Overridepublic void clear() {contextHolder.remove();delegate.clear();}
}

然后替换默认的 ContextManager Bean:

@Bean
public ContextManager contextManager(List<ContextProvider> providers) {return new TtlContextManager(new DefaultContextManager(providers));
}

十、总结建议

场景推荐方案
多上下文来源统一管理设计通用 ContextManager 接口抽象
异步任务上下文丢失使用 TTL 包装上下文管理器
日志自动注入结合 MDC 和 ContextManager
Dubbo 微服务上下文透传使用 RpcContextProvider
Web 请求上下文使用 WebContextProvider
统一日志追踪Sleuth + MDC + ContextManager 联合使用

📌 参考链接

  • TransmittableThreadLocal GitHub
  • Dubbo RpcContext 官方文档
  • Spring Cloud Sleuth 文档

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

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

相关文章

配置驱动开发:初探零代码构建嵌入式软件配置工具

前言在嵌入式软件开发中&#xff0c;硬件初始化与寄存器配置长期依赖人工编写重复代码。以STM32外设初始化为例&#xff0c;开发者需手动完成时钟使能、引脚模式设置、参数配置等步骤&#xff0c;不仅耗时易错&#xff08;如位掩码写反、模式枚举值混淆&#xff09;&#xff0c…

Elasticsearch混合搜索深度解析(下):执行机制与完整流程

引言 在上篇中&#xff0c;我们发现了KNN结果通过SubSearch机制被保留的关键事实。本篇将继续深入分析混合搜索的执行机制&#xff0c;揭示完整的处理流程&#xff0c;并解答之前的所有疑惑。 深入源码分析 1. SubSearch的执行机制 1.1 KnnScoreDocQueryBuilder的实现 KNN结果被…

Apache HTTP Server 从安装到配置

一、Apache 是什么&#xff1f;Apache&#xff08;全称 Apache HTTP Server&#xff09;是当前最流行的开源Web服务器软件之一&#xff0c;由Apache软件基金会维护。它以稳定性高、模块化设计和灵活的配置著称&#xff0c;支持Linux、Windows等多平台&#xff0c;是搭建个人博客…

php中调用对象的方法可以使用array($object, ‘methodName‘)?

是的&#xff0c;在PHP中&#xff0c;array($object, methodName) 是一种标准的回调语法&#xff0c;用于表示“调用某个对象的特定方法”。这种语法可以被许多函数&#xff08;如 call_user_func()、call_user_func_array()、usort() 等&#xff09;识别并执行。 语法原理 在P…

【设计模式】单例模式 饿汉式单例与懒汉式单例

单例模式&#xff08;Singleton Pattern&#xff09;详解一、单例模式简介 单例模式&#xff08;Singleton Pattern&#xff09; 是一种 创建型设计模式&#xff0c;它确保一个类只有一个实例&#xff0c;并提供一个全局访问点来获取这个实例。&#xff08;对象创建型模式&…

vue3 el-table 行数据沾满格自动换行

在使用 Vue 3 结合 Element Plus 的 <el-table> 组件时&#xff0c;如果你希望当表格中的行数据文本过长时能够自动换行&#xff0c;而不是溢出到其他单元格或简单地截断&#xff0c;你可以通过以下几种方式来实现&#xff1a;方法 1&#xff1a;使用 CSS最简单的方法是通…

windows电脑远程win系统服务器上的wsl2

情况 我自己使用win11笔记本电脑&#xff0c;想要远程win11服务器上的wsl2 我这里只有服务器安装了wsl2&#xff0c;win11笔记本没有安装 因此下面提到的Ubuntu终端指的是win服务器上的wsl2终端 一定要区分是在哪里输入命令&#xff01;&#xff01; 安装SSH 在服务器上&#x…

神经辐射场 (NeRF):重构三维世界的AI新视角

神经辐射场 (NeRF)&#xff1a;重构三维世界的AI新视角 旧金山蜿蜒起伏的街道上&#xff0c;一辆装备12个摄像头的Waymo自动驾驶测试车缓缓驶过。它记录的280万张街景图像并未被简单地拼接成平面地图&#xff0c;而是被输入一个名为Block-NeRF的神经网络。数周后&#xff0c;一…

Kubernetes自动扩缩容方案对比与实践指南

Kubernetes自动扩缩容方案对比与实践指南 随着微服务架构和容器化的广泛采用&#xff0c;Kubernetes 自动扩缩容&#xff08;Autoscaling&#xff09;成为保障生产环境性能稳定与资源高效利用的关键技术。面对水平 Pod 扩缩容、垂直资源调整、集群节点扩缩容以及事件驱动扩缩容…

【CVPR2025】计算机视觉|SIREN: 元学习赋能!突破INR高分辨率图像分类难题

论文地址&#xff1a;https://arxiv.org/pdf/2503.18123v1 代码地址&#xff1a;https://github.com/SanderGielisse/MWT 关注UP CV缝合怪&#xff0c;分享最计算机视觉新即插即用模块&#xff0c;并提供配套的论文资料与代码。 https://space.bilibili.com/473764881 摘要 …

牛客周赛 Round 99

赛时成绩如下&#xff1a;A. Round 99题目描述 对于给定的五位整数&#xff0c;检查其中是否含有数字 99&#xff1b;换句话说&#xff0c;检查是否存在相邻的两个数位&#xff0c;其值均为 。解题思路&#xff1a; 检查相邻的两个数字是否均为9#include <bits/stdc.h> u…

从0到1搭建个人技术博客:用GitHub Pages+Hexo实现

一、为什么要搭建个人技术博客&#xff1f; 在技术圈&#xff0c;拥有个人博客的好处不言而喻&#xff1a; 简历加分项&#xff1a;面试官更青睐有技术沉淀的候选人知识系统化&#xff1a;输出倒逼输入&#xff0c;加深技术理解人脉拓展&#xff1a;吸引同行关注&#xff0c;…

Ubuntu22.04 设置显示存在双屏却无法双屏显示

文章目录一、背景描述二、解决方法一、背景描述 回到工位后&#xff0c;发现昨天离开时还可正常显示的双屏&#xff0c;今早ubuntu22.04 的设置界面显示有双屏&#xff0c;但外接的显示屏无法正常显示。 首先&#xff0c;查看当前图像处理显卡是否为N卡&#xff0c;没错&#…

高亚科技签约奕源金属,助力打造高效智能化采购管理体系

深圳市奕源金属制品有限公司近日&#xff0c;国内企业管理软件服务商高亚科技与深圳市奕源金属制品有限公司&#xff08;以下简称“奕源金属”&#xff09;正式签约&#xff0c;双方将基于高亚科技自主研发的8Manage SRM采购管理系统&#xff0c;共同推动奕源金属采购管理的数字…

数据结构之map

map的基本介绍我们常常把map称之为映射&#xff0c;就是将一个元素&#xff08;通常称之为key键&#xff09;与一个相对应的值&#xff08;通常称之为value&#xff09;关联起来&#xff0c;比如说一个学生的名字&#xff08;key&#xff09;有与之对应的成绩&#xff08;value…

vue3 canvas 选择器 Canvas 增加页面性能

文章目录Vue3 选择器 Canvas 增加页面性能基于Vue3 Composition API和Canvas实现的交互式选择器&#xff0c;支持PC端和移动端的拖动选择、多选取消选择功能vue3组件封装html代码Vue3 选择器 Canvas 增加页面性能 基于Vue3 Composition API和Canvas实现的交互式选择器&#xf…

Python 实战:打造多文件批量重命名工具

引言在实际运维、测试、数据分析、开发流程中&#xff0c;我们经常会处理成百上千条命令操作&#xff0c;例如&#xff1a;各种脚本任务&#xff08;启动、备份、重启、日志查看&#xff09;数据处理流程&#xff08;爬取 → 清洗 → 统计 → 可视化&#xff09;配置自动化&…

设计模式笔记_结构型_代理模式

1. 代理模式介绍代理模式是一种结构型设计模式&#xff0c;它允许你提供一个代理对象来控制对另一个对象的访问。代理对象通常在客户端和目标对象之间起到中介作用&#xff0c;能够在不改变目标对象的前提下增加额外的功能操作&#xff0c;比如延迟初始化、访问控制、日志记录等…

C语言<数据结构-单链表>(收尾)

上篇博客我将基础的尾插、尾删、头插、头删逐一讲解了&#xff0c;这篇博客将对上篇博客进行收尾&#xff0c;讲一下指定位置操作增删以及查找这几个函数&#xff0c;其实大同小异&#xff1a;一.查找函数&#xff1a;查找函数其实就是一个简单的循环遍历&#xff0c;所以不加以…

十年架构心路:从单机到云原生的分布式系统演进史

十年架构心路&#xff1a;从单机到云原生的分布式系统演进史 这里写目录标题十年架构心路&#xff1a;从单机到云原生的分布式系统演进史一、技术生涯的起点&#xff1a;单体架构的黄金时代1.1 典型技术栈1.2 记忆深刻的故障二、分布式架构转型期2.1 服务化拆分实践2.2 分布式事…