# DiscoveryAgent
**Repository Path**: jacksonsmart/DiscoveryAgent
## Basic Information
- **Project Name**: DiscoveryAgent
- **Description**: ☀️ Nepxion Discovery Agent is a java agent to resolve loss of ThreadLocal in cross-thread scenario, such as Spring Async、Hystrix Thread、Runnable、Callable、Single Thread、Thread Pool、MDC 异步跨线程Agent
- **Primary Language**: Java
- **License**: Apache-2.0
- **Default Branch**: master
- **Homepage**: http://www.nepxion.com
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 38
- **Created**: 2021-03-25
- **Last Updated**: 2021-11-03
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
[^_^]: 

# Discovery【探索】微服务企业级解决方案
[](https://tokei.rs/b1/github/Nepxion/Discovery?category=lines) [](https://github.com/Nepxion/Discovery/blob/6.x.x/LICENSE) [](https://search.maven.org/artifact/com.nepxion/discovery) [](http://www.javadoc.io/doc/com.nepxion/discovery-plugin-framework-starter) [](https://travis-ci.org/Nepxion/Discovery) [](https://www.codacy.com/project/HaojunRen/Discovery/dashboard?utm_source=github.com&utm_medium=referral&utm_content=Nepxion/Discovery&utm_campaign=Badge_Grade_Dashboard) [](https://github.com/Nepxion/Discovery/stargazers) [](https://gitee.com/nepxion/Discovery/stargazers)
[](https://search.maven.org/artifact/org.springframework.boot/spring-boot-dependencies) [](https://search.maven.org/artifact/org.springframework.cloud/spring-cloud-dependencies) [](https://search.maven.org/artifact/com.alibaba.cloud/spring-cloud-alibaba-dependencies) [](https://search.maven.org/artifact/com.nepxion/discovery)
[](http://nepxion.gitee.io/discovery/docs/link-doc/discovery-ppt.html) [](http://nepxion.gitee.io/discovery/docs/link-doc/discovery-pdf.html) [](http://nepxion.gitee.io/discovery/docs/link-doc/discovery-html.html)
如果您觉得本框架具有一定的参考价值和借鉴意义,请帮忙在页面右上角 [**Star**]
 首席作者简介
- Nepxion开源社区创始人
- 2020年阿里巴巴中国云原生峰会出品人
- 2020年被Nacos和Spring Cloud Alibaba纳入相关开源项目
- 2021年阿里巴巴技术峰会上海站演讲嘉宾
- 2021年荣获陆奇博士主持的奇绩资本,进行风险投资的关注和调研
- 2021年入选Gitee最有价值开源项目
- Nacos Group Member、Spring Cloud Alibaba Member
- Spring Cloud Alibaba、Nacos、Sentinel、OpenTracing Committer & Contributor


 商业化合作
① Discovery系列
| 框架名称 | 框架版本 | 支持Spring Cloud版本 | 使用许可 |
| --- | --- | --- | --- |
| Discovery | 1.x.x ~ 6.x.x | Camden ~ Hoxton | 开源,永久免费 |
| DiscoveryX | 7.x.x | 202x | 闭源,商业许可 |
② Polaris系列
Polaris为Discovery高级定制版,特色功能
- 基于Nepxion Discovery集成定制
- 多云、多活、多机房流量调配
- 跨云动态域名、跨环境适配
- DCN、DSU、SET单元化部署
- 组件灵活装配、配置对外屏蔽
- 极简低代码PaaS平台
| 框架名称 | 框架版本 | 支持Discovery版本 | 支持Spring Cloud版本 | 使用许可 |
| --- | --- | --- | --- | --- |
| Polaris | 1.x.x | 6.x.x | Finchley ~ Hoxton | 闭源,商业许可 |
| Polaris | 2.x.x | 7.x.x | 202x | 闭源,商业许可 |
有商业版需求的企业和用户,请添加微信1394997,联系作者,洽谈合作事宜
 Discovery【探索】微服务企业级解决方案
① Discovery【探索】微服务企业级解决方案文档
- [Discovery【探索】微服务企业级解决方案(WIKI版)](http://nepxion.com/discovery)
- [Discovery【探索】微服务企业级解决方案(PPT版)](http://nepxion.gitee.io/discovery/docs/link-doc/discovery-ppt.html)
- [Discovery【探索】微服务企业级解决方案(PDF版)](http://nepxion.gitee.io/discovery/docs/link-doc/discovery-pdf.html)
- [Discovery【探索】微服务企业级解决方案(HTML版)](http://nepxion.gitee.io/discovery/docs/link-doc/discovery-html.html)
② Discovery【探索】微服务企业级解决方案源码。请访问Gitee镜像获得最佳体验
- [源码Gitee同步镜像](https://gitee.com/Nepxion/Discovery)
- [源码Github原镜像](https://github.com/Nepxion/Discovery)
③ Discovery【探索】微服务企业级解决方案指南示例源码。请访问Gitee镜像获得最佳体验
- [指南Gitee同步镜像](https://gitee.com/Nepxion/DiscoveryGuide)
- [指南Github原镜像](https://github.com/Nepxion/DiscoveryGuide)
④ Discovery【探索】微服务企业级解决方案指南示例说明
- 对于入门级玩家,参考[6.x.x指南示例极简版](https://github.com/Nepxion/DiscoveryGuide/tree/6.x.x-simple),分支为6.x.x-simple
- 对于熟练级玩家,参考[6.x.x指南示例精进版](https://github.com/Nepxion/DiscoveryGuide/tree/6.x.x),分支为6.x.x。除上述《极简版》功能外,涉及到指南篇里的绝大多数高级功能
- 对于骨灰级玩家,参考[6.x.x指南示例高级版](https://github.com/Nepxion/DiscoveryGuide/tree/6.x.x-complex),分支为6.x.x-complex。除上述《精进版》功能外,涉及到指南篇里的ActiveMQ、MongoDB、RabbitMQ、Redis、RocketMQ、MySQL等高级调用链和蓝绿灰度调用链的整合
- 上述指南实例分支是针对Spring Cloud旧版本。对于Spring Cloud 202x版本,参考[202x版指南示例精进版](https://github.com/Nepxion/DiscoveryGuide/tree/master),分支为master
⑤ Discovery【探索】微服务企业级解决方案快速入门文档
- [Gitee Wiki](https://gitee.com/nepxion/Discovery/wikis/pages)
- [Github Wiki](https://github.com/Nepxion/Discovery/wiki)
 Polaris【北极星】企业级云原生微服务框架
① Polaris【北极星】企业级云原生微服务框架文档
- [Polaris【北极星】企业级云原生微服务框架(WIKI版)](http://nepxion.com/polaris)
- [Polaris【北极星】企业级云原生微服务框架(PDF版)](http://nepxion.gitee.io/discovery/docs/link-doc/polaris-pdf.html)
- [Polaris【北极星】企业级云原生微服务框架(HTML版)](http://nepxion.gitee.io/discovery/docs/link-doc/polaris-html.html)
② Polaris【北极星】企业级云原生微服务框架源码。请访问Gitee镜像获得最佳体验
- [源码Gitee同步镜像](https://gitee.com/polaris-paas/polaris-sdk)
- [源码Github原镜像](https://github.com/polaris-paas/polaris-sdk)
③ Polaris【北极星】企业级云原生微服务框架指南示例源码。请访问Gitee镜像获得最佳体验
- [指南Gitee同步镜像](https://gitee.com/polaris-paas/polaris-guide)
- [指南Github原镜像](https://github.com/polaris-paas/polaris-guide)
④ Polaris【北极星】企业级云原生微服务框架指南示例说明
- Spring Cloud旧版本,参考[1.x.x指南示例](https://github.com/polaris-paas/polaris-guide/tree/1.x.x),分支为1.x.x
- Spring Cloud新版本,参考[2.x.x指南示例](https://github.com/polaris-paas/polaris-guide/tree/master),分支为master
 Discovery【探索】和Polaris【北极星】架构体系
① Discovery【探索】和Polaris【北极星】联合架构图

② Discovery【探索】和Polaris【北极星】联合拓扑图

③ Polaris【北极星】分层架构图

④ Discovery【探索】实施方案图

⑤ Discovery【探索】域网关实施图

⑥ Discovery【探索】非域网关实施图

⑦ Discovery【探索】全局订阅实施图

## 异步场景下Discovery和DiscoveryAgent
Discovery框架存在着如下全链路传递上下文的场景,包括
- 策略路由Header全链路从网关传递到服务
- 调用链埋点全链路从网关传递到服务
- 业务自定义的上下文的传递
上述上下文会在如下异步场景中丢失,包括
- WebFlux Reactor响应式异步
- Spring异步,@Async注解异步
- Hystrix线程池隔离模式异步
- 线程,线程池异步
- SLF4J日志异步
通过DiscoveryAgent,解决上述痛点。Discovery框架利用DiscoveryAgent字节码增强技术,完美解决各种调用场景下的异步,包括
- Spring Cloud Gateway过滤器中的上下文传递
- Zuul过滤器中的上下文传递
- Feign拦截器中的上下文转发
- RestTemplate拦截器中的上下文转发
- WebClient拦截器中的上下文转发
### 异步场景下DiscoveryAgent解决方案
 DiscoveryAgent不仅适用于Discovery框架,也适用于一切具有类似使用场景的基础框架(例如:Dubbo)和业务系统
ThreadLocal的作用是提供线程内的局部变量,在多线程环境下访问时能保证各个线程内的ThreadLocal变量各自独立。在异步场景下,由于出现线程切换的问题,例如,主线程切换到子线程,会导致线程ThreadLocal上下文丢失。DiscoveryAgent通过Java Agent方式解决这些痛点
涵盖所有Java框架的异步场景,解决如下8个异步场景下丢失线程ThreadLocal上下文的问题
- WebFlux Reactor
- `@`Async
- Hystrix Thread Pool Isolation
- Runnable
- Callable
- Single Thread
- Thread Pool
- SLF4J MDC
#### 异步跨线程DiscoveryAgent获取
插件获取方式有两种方式
- 通过[https://github.com/Nepxion/DiscoveryAgent/releases](https://github.com/Nepxion/DiscoveryAgent/releases)下载最新版本的Discovery Agent
- 编译[https://github.com/Nepxion/DiscoveryAgent](https://github.com/Nepxion/DiscoveryAgent)产生discovery-agent目录
#### 异步跨线程DiscoveryAgent清单
① discovery-agent-starter-`$`{discovery.version}.jar为Agent引导启动程序,JVM启动时进行加载
② agent.config为基准扫描目录配置文件
绝大多数情况下不需要修改,当然使用者也可以增加和删除agent.config的基准扫描目录。默认配置如下
```
# Base thread scan packages
agent.plugin.thread.scan.packages=reactor.core.publisher;org.springframework.aop.interceptor;com.netflix.hystrix
```
基准扫描目录,含义如下
- WebFlux Reactor异步场景下的扫描目录对应为reactor.core.publisher
- `@`Async场景下的扫描目录对应为org.springframework.aop.interceptor
- Hystrix线程池隔离场景下的扫描目录对应为com.netflix.hystrix
③ plugin/discovery-agent-starter-plugin-strategy-`$`{discovery.version}.jar插件,解决Nepxion Discovery上下文异步场景
④ plugin/discovery-agent-starter-plugin-mdc-`$`{discovery.version}.jar插件,解决SLF4J MDC日志上下文异步场景
⑤ 业务系统可以自定义plugin,解决业务自己定义的上下文异步场景
#### 异步跨线程DiscoveryAgent使用
① 使用示例
- 通过如下-javaagent启动,基本格式,如下
```
-javaagent:C:/opt/discovery-agent/discovery-agent-starter-${discovery.agent.version}.jar -Dthread.scan.packages=com.nepxion.discovery.guide.service.feign
```
② 参数说明
- C:/opt/discovery-agent:Agent所在的目录,需要对应到实际的目录上
- `-D`thread.scan.packages:Runnable/Callable/Thread/ThreadPool等异步类所在的扫描目录,该目录下的异步类都会被装饰
- 扫描目录最好精细和准确,目录越详细,越可以减少被装饰的对象数,从一定程度上可以提高性能
- 扫描目录如果有多个,用“;”分隔
- 扫描目录如果含有“;”,可能会在某些操作系统中无法被识别,请用`""`进行引入,例如,-Dthread.scan.packages="com.abc;com.xyz"
- 扫描目录下没有Runnable/Callable/Thread/ThreadPool等异步类存在,那么thread.scan.packages也不需要配置,最终启动命令行简化为-javaagent:C:/opt/discovery-agent/discovery-agent-starter-${discovery.agent.version}.jar
- `-D`thread.gateway.enabled:Spring Cloud Gateway端策略Header输出到异步子线程。默认开启
- `-D`thread.zuul.enabled:Zuul端策略Header输出到异步子线程。默认开启
- `-D`thread.service.enabled:服务端策略Header输出到异步子线程。默认开启
- `-D`thread.mdc.enabled:SLF4J MDC日志输出到异步子线程。默认开启
- `-D`thread.request.decorator.enabled:异步调用场景下在服务端的Request请求的装饰,当主线程先于子线程执行完的时候,Request会被Destory,导致Header仍旧拿不到,开启装饰,就可以确保拿到。默认为开启,根据实践经验,大多数场景下,需要开启该开关
#### 异步跨线程DiscoveryAgent扩展
- 根据规范开发一个插件,插件提供了钩子函数,在某个类被加载的时候,可以注册一个事件到线程上下文切换事件当中,实现业务自定义ThreadLocal的跨线程传递
- plugin目录为放置需要在线程切换时进行ThreadLocal传递的自定义插件。业务自定义插件开发完后,放入到plugin目录下即可
具体步骤介绍,如下
① SDK侧工作
- 新建ThreadLocal上下文类
```java
public class MyContext {
private static final ThreadLocal THREAD_LOCAL = new ThreadLocal() {
@Override
protected MyContext initialValue() {
return new MyContext();
}
};
public static MyContext getCurrentContext() {
return THREAD_LOCAL.get();
}
public static void clearCurrentContext() {
THREAD_LOCAL.remove();
}
private Map attributes = new HashMap<>();
public Map getAttributes() {
return attributes;
}
public void setAttributes(Map attributes) {
this.attributes = attributes;
}
}
```
② Agent侧工作
- 新建一个模块,引入如下依赖
```xml
com.nepxion
discovery-agent-starter
${discovery.agent.version}
provided
```
- 新建一个ThreadLocalHook类继承AbstractThreadLocalHook
```java
public class MyContextHook extends AbstractThreadLocalHook {
@Override
public Object create() {
// 从主线程的ThreadLocal里获取并返回上下文对象
return MyContext.getCurrentContext().getAttributes();
}
@Override
public void before(Object object) {
// 把create方法里获取到的上下文对象放置到子线程的ThreadLocal里
if (object instanceof Map) {
MyContext.getCurrentContext().setAttributes((Map) object);
}
}
@Override
public void after() {
// 线程结束,销毁上下文对象
MyContext.clearCurrentContext();
}
}
```
- 新建一个Plugin类继承AbstractPlugin
```java
public class MyContextPlugin extends AbstractPlugin {
private Boolean threadMyPluginEnabled = Boolean.valueOf(System.getProperty("thread.myplugin.enabled", "false"));
@Override
protected String getMatcherClassName() {
// 返回存储ThreadLocal对象的类名,由于插件是可以插拔的,所以必须是字符串形式,不允许是显式引入类
return "com.nepxion.discovery.example.sdk.MyContext";
}
@Override
protected String getHookClassName() {
// 返回ThreadLocalHook类名
return MyContextHook.class.getName();
}
@Override
protected boolean isEnabled() {
// 通过外部-Dthread.myplugin.enabled=true/false的运行参数来控制当前Plugin是否生效。该方法在父类中定义的返回值为true,即缺省为生效
return threadMyPluginEnabled;
}
}
```
- 定义SPI扩展,在src/main/resources/META-INF/services目录下定义SPI文件
名称为固定如下格式
```
com.nepxion.discovery.agent.plugin.Plugin
```
内容为Plugin类的全路径
```
com.nepxion.discovery.example.agent.MyContextPlugin
```
- 执行Maven编译,把编译后的包放在discovery-agent/plugin目录下
- 给服务增加启动参数并启动,如下
```
-javaagent:C:/opt/discovery-agent/discovery-agent-starter-${discovery.agent.version}.jar -Dthread.scan.packages=com.nepxion.discovery.example.application -Dthread.myplugin.enabled=true
```
③ Application侧工作
- 执行MyApplication,它模拟在主线程ThreadLocal放入Map数据,子线程通过DiscoveryAgent获取到该Map数据,并打印出来
```java
@SpringBootApplication
@RestController
public class MyApplication {
private static final Logger LOG = LoggerFactory.getLogger(MyApplication.class);
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
invoke();
}
public static void invoke() {
RestTemplate restTemplate = new RestTemplate();
for (int i = 1; i <= 10; i++) {
restTemplate.getForEntity("http://localhost:8080/index/" + i, String.class).getBody();
}
}
@GetMapping("/index/{value}")
public String index(@PathVariable(value = "value") String value) throws InterruptedException {
Map attributes = new HashMap();
attributes.put(value, "MyContext");
MyContext.getCurrentContext().setAttributes(attributes);
LOG.info("【主】线程ThreadLocal:{}", MyContext.getCurrentContext().getAttributes());
new Thread(new Runnable() {
@Override
public void run() {
LOG.info("【子】线程ThreadLocal:{}", MyContext.getCurrentContext().getAttributes());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
LOG.info("Sleep 5秒之后,【子】线程ThreadLocal:{} ", MyContext.getCurrentContext().getAttributes());
}
}).start();
return "";
}
}
```
输出结果,如下
```
2020-11-09 00:08:14.330 INFO 16588 --- [nio-8080-exec-1] c.n.d.example.application.MyApplication : 【主】线程ThreadLocal:{1=MyContext}
2020-11-09 00:08:14.381 INFO 16588 --- [ Thread-4] c.n.d.example.application.MyApplication : 【子】线程ThreadLocal:{1=MyContext}
2020-11-09 00:08:14.402 INFO 16588 --- [nio-8080-exec-2] c.n.d.example.application.MyApplication : 【主】线程ThreadLocal:{2=MyContext}
2020-11-09 00:08:14.403 INFO 16588 --- [ Thread-5] c.n.d.example.application.MyApplication : 【子】线程ThreadLocal:{2=MyContext}
2020-11-09 00:08:14.405 INFO 16588 --- [nio-8080-exec-3] c.n.d.example.application.MyApplication : 【主】线程ThreadLocal:{3=MyContext}
2020-11-09 00:08:14.406 INFO 16588 --- [ Thread-6] c.n.d.example.application.MyApplication : 【子】线程ThreadLocal:{3=MyContext}
2020-11-09 00:08:14.414 INFO 16588 --- [nio-8080-exec-4] c.n.d.example.application.MyApplication : 【主】线程ThreadLocal:{4=MyContext}
2020-11-09 00:08:14.414 INFO 16588 --- [ Thread-7] c.n.d.example.application.MyApplication : 【子】线程ThreadLocal:{4=MyContext}
2020-11-09 00:08:14.417 INFO 16588 --- [nio-8080-exec-5] c.n.d.example.application.MyApplication : 【主】线程ThreadLocal:{5=MyContext}
2020-11-09 00:08:14.418 INFO 16588 --- [ Thread-8] c.n.d.example.application.MyApplication : 【子】线程ThreadLocal:{5=MyContext}
2020-11-09 00:08:14.421 INFO 16588 --- [nio-8080-exec-6] c.n.d.example.application.MyApplication : 【主】线程ThreadLocal:{6=MyContext}
2020-11-09 00:08:14.422 INFO 16588 --- [ Thread-9] c.n.d.example.application.MyApplication : 【子】线程ThreadLocal:{6=MyContext}
2020-11-09 00:08:14.424 INFO 16588 --- [nio-8080-exec-7] c.n.d.example.application.MyApplication : 【主】线程ThreadLocal:{7=MyContext}
2020-11-09 00:08:14.425 INFO 16588 --- [ Thread-10] c.n.d.example.application.MyApplication : 【子】线程ThreadLocal:{7=MyContext}
2020-11-09 00:08:14.427 INFO 16588 --- [nio-8080-exec-8] c.n.d.example.application.MyApplication : 【主】线程ThreadLocal:{8=MyContext}
2020-11-09 00:08:14.428 INFO 16588 --- [ Thread-11] c.n.d.example.application.MyApplication : 【子】线程ThreadLocal:{8=MyContext}
2020-11-09 00:08:14.430 INFO 16588 --- [nio-8080-exec-9] c.n.d.example.application.MyApplication : 【主】线程ThreadLocal:{9=MyContext}
2020-11-09 00:08:14.431 INFO 16588 --- [ Thread-12] c.n.d.example.application.MyApplication : 【子】线程ThreadLocal:{9=MyContext}
2020-11-09 00:08:14.433 INFO 16588 --- [io-8080-exec-10] c.n.d.example.application.MyApplication : 【主】线程ThreadLocal:{10=MyContext}
2020-11-09 00:08:14.434 INFO 16588 --- [ Thread-13] c.n.d.example.application.MyApplication : 【子】线程ThreadLocal:{10=MyContext}
2020-11-09 00:08:19.382 INFO 16588 --- [ Thread-4] c.n.d.example.application.MyApplication : Sleep 5秒之后,【子】线程ThreadLocal:{1=MyContext}
2020-11-09 00:08:19.404 INFO 16588 --- [ Thread-5] c.n.d.example.application.MyApplication : Sleep 5秒之后,【子】线程ThreadLocal:{2=MyContext}
2020-11-09 00:08:19.406 INFO 16588 --- [ Thread-6] c.n.d.example.application.MyApplication : Sleep 5秒之后,【子】线程ThreadLocal:{3=MyContext}
2020-11-09 00:08:19.416 INFO 16588 --- [ Thread-7] c.n.d.example.application.MyApplication : Sleep 5秒之后,【子】线程ThreadLocal:{4=MyContext}
2020-11-09 00:08:19.418 INFO 16588 --- [ Thread-8] c.n.d.example.application.MyApplication : Sleep 5秒之后,【子】线程ThreadLocal:{5=MyContext}
2020-11-09 00:08:19.422 INFO 16588 --- [ Thread-9] c.n.d.example.application.MyApplication : Sleep 5秒之后,【子】线程ThreadLocal:{6=MyContext}
2020-11-09 00:08:19.425 INFO 16588 --- [ Thread-10] c.n.d.example.application.MyApplication : Sleep 5秒之后,【子】线程ThreadLocal:{7=MyContext}
2020-11-09 00:08:19.428 INFO 16588 --- [ Thread-11] c.n.d.example.application.MyApplication : Sleep 5秒之后,【子】线程ThreadLocal:{8=MyContext}
2020-11-09 00:08:19.432 INFO 16588 --- [ Thread-12] c.n.d.example.application.MyApplication : Sleep 5秒之后,【子】线程ThreadLocal:{9=MyContext}
2020-11-09 00:08:19.434 INFO 16588 --- [ Thread-13] c.n.d.example.application.MyApplication : Sleep 5秒之后,【子】线程ThreadLocal:{10=MyContext}
```
如果不加异步Agent,则输出结果,如下,可以发现在子线程中ThreadLocal上下文全部都丢失
```
2020-11-09 00:01:40.133 INFO 16692 --- [nio-8080-exec-1] c.n.d.example.application.MyApplication : 【主】线程ThreadLocal:{1=MyContext}
2020-11-09 00:01:40.135 INFO 16692 --- [ Thread-8] c.n.d.example.application.MyApplication : 【子】线程ThreadLocal:{}
2020-11-09 00:01:40.158 INFO 16692 --- [nio-8080-exec-2] c.n.d.example.application.MyApplication : 【主】线程ThreadLocal:{2=MyContext}
2020-11-09 00:01:40.159 INFO 16692 --- [ Thread-9] c.n.d.example.application.MyApplication : 【子】线程ThreadLocal:{}
2020-11-09 00:01:40.162 INFO 16692 --- [nio-8080-exec-3] c.n.d.example.application.MyApplication : 【主】线程ThreadLocal:{3=MyContext}
2020-11-09 00:01:40.163 INFO 16692 --- [ Thread-10] c.n.d.example.application.MyApplication : 【子】线程ThreadLocal:{}
2020-11-09 00:01:40.170 INFO 16692 --- [nio-8080-exec-5] c.n.d.example.application.MyApplication : 【主】线程ThreadLocal:{4=MyContext}
2020-11-09 00:01:40.170 INFO 16692 --- [ Thread-11] c.n.d.example.application.MyApplication : 【子】线程ThreadLocal:{}
2020-11-09 00:01:40.173 INFO 16692 --- [nio-8080-exec-4] c.n.d.example.application.MyApplication : 【主】线程ThreadLocal:{5=MyContext}
2020-11-09 00:01:40.174 INFO 16692 --- [ Thread-12] c.n.d.example.application.MyApplication : 【子】线程ThreadLocal:{}
2020-11-09 00:01:40.176 INFO 16692 --- [nio-8080-exec-6] c.n.d.example.application.MyApplication : 【主】线程ThreadLocal:{6=MyContext}
2020-11-09 00:01:40.177 INFO 16692 --- [ Thread-13] c.n.d.example.application.MyApplication : 【子】线程ThreadLocal:{}
2020-11-09 00:01:40.179 INFO 16692 --- [nio-8080-exec-8] c.n.d.example.application.MyApplication : 【主】线程ThreadLocal:{7=MyContext}
2020-11-09 00:01:40.180 INFO 16692 --- [ Thread-14] c.n.d.example.application.MyApplication : 【子】线程ThreadLocal:{}
2020-11-09 00:01:40.182 INFO 16692 --- [nio-8080-exec-7] c.n.d.example.application.MyApplication : 【主】线程ThreadLocal:{8=MyContext}
2020-11-09 00:01:40.182 INFO 16692 --- [ Thread-15] c.n.d.example.application.MyApplication : 【子】线程ThreadLocal:{}
2020-11-09 00:01:40.185 INFO 16692 --- [nio-8080-exec-9] c.n.d.example.application.MyApplication : 【主】线程ThreadLocal:{9=MyContext}
2020-11-09 00:01:40.186 INFO 16692 --- [ Thread-16] c.n.d.example.application.MyApplication : 【子】线程ThreadLocal:{}
2020-11-09 00:01:40.188 INFO 16692 --- [io-8080-exec-10] c.n.d.example.application.MyApplication : 【主】线程ThreadLocal:{10=MyContext}
2020-11-09 00:01:40.189 INFO 16692 --- [ Thread-17] c.n.d.example.application.MyApplication : 【子】线程ThreadLocal:{}
2020-11-09 00:01:45.136 INFO 16692 --- [ Thread-8] c.n.d.example.application.MyApplication : Sleep 5秒之后,【子】线程ThreadLocal:{}
2020-11-09 00:01:45.160 INFO 16692 --- [ Thread-9] c.n.d.example.application.MyApplication : Sleep 5秒之后,【子】线程ThreadLocal:{}
2020-11-09 00:01:45.163 INFO 16692 --- [ Thread-10] c.n.d.example.application.MyApplication : Sleep 5秒之后,【子】线程ThreadLocal:{}
2020-11-09 00:01:45.171 INFO 16692 --- [ Thread-11] c.n.d.example.application.MyApplication : Sleep 5秒之后,【子】线程ThreadLocal:{}
2020-11-09 00:01:45.174 INFO 16692 --- [ Thread-12] c.n.d.example.application.MyApplication : Sleep 5秒之后,【子】线程ThreadLocal:{}
2020-11-09 00:01:45.177 INFO 16692 --- [ Thread-13] c.n.d.example.application.MyApplication : Sleep 5秒之后,【子】线程ThreadLocal:{}
2020-11-09 00:01:45.181 INFO 16692 --- [ Thread-14] c.n.d.example.application.MyApplication : Sleep 5秒之后,【子】线程ThreadLocal:{}
2020-11-09 00:01:45.183 INFO 16692 --- [ Thread-15] c.n.d.example.application.MyApplication : Sleep 5秒之后,【子】线程ThreadLocal:{}
2020-11-09 00:01:45.187 INFO 16692 --- [ Thread-16] c.n.d.example.application.MyApplication : Sleep 5秒之后,【子】线程ThreadLocal:{}
2020-11-09 00:01:45.190 INFO 16692 --- [ Thread-17] c.n.d.example.application.MyApplication : Sleep 5秒之后,【子】线程ThreadLocal:{}
```
完整示例,请参考[https://github.com/Nepxion/DiscoveryAgent/tree/master/discovery-agent-example](https://github.com/Nepxion/DiscoveryAgent/tree/master/discovery-agent-example)。上述自定义插件的方式,即可解决使用者在线程切换时丢失ThreadLocal上下文的问题
## 请联系我
微信、钉钉、公众号和文档

## Star走势图
[](https://starchart.cc/Nepxion/Discovery)