# Technical Documentation: alibaba/transmittable-thread-local
> ℹ️ **Provenance:** Hybrid Fusion: `alibaba/transmittable-thread-local` (README + 8 In-Tree Chapters) · [CodeWiki Reference](https://codewiki.google/github.com/alibaba/transmittable-thread-local) · Recency: Active (< 180 days)
## 1. Project Overview & Quickstart (alibaba/transmittable-thread-local)
#
> [!IMPORTANT]
> 🚧 这个分支是`TransmittableThreadLocal(TTL) v3`,在开发中还没有发布。
> `v3`的版本说明、工作项列表及其进展,参见 [issue 432](https://github.com/alibaba/transmittable-thread-local/issues/432)。
>
> 👉 目前使用中的稳定发布版本`v2.x`在 [**分支`2.x`**](https://github.com/alibaba/transmittable-thread-local/tree/2.x)上。
[📖 English Documentation](README-EN.md) | 📖 中文文档
----------------------------------------
- [🔧 功能](#-%E5%8A%9F%E8%83%BD)
- [🎨 需求场景](#-%E9%9C%80%E6%B1%82%E5%9C%BA%E6%99%AF)
- [👥 User Guide](#-user-guide)
- [1. 简单使用](#1-%E7%AE%80%E5%8D%95%E4%BD%BF%E7%94%A8)
- [2. 保证线程池中传递值](#2-%E4%BF%9D%E8%AF%81%E7%BA%BF%E7%A8%8B%E6%B1%A0%E4%B8%AD%E4%BC%A0%E9%80%92%E5%80%BC)
- [2.1 修饰`Runnable`和`Callable`](#21-%E4%BF%AE%E9%A5%B0runnable%E5%92%8Ccallable)
- [整个过程的完整时序图](#%E6%95%B4%E4%B8%AA%E8%BF%87%E7%A8%8B%E7%9A%84%E5%AE%8C%E6%95%B4%E6%97%B6%E5%BA%8F%E5%9B%BE)
- [2.2 修饰线程池](#22-%E4%BF%AE%E9%A5%B0%E7%BA%BF%E7%A8%8B%E6%B1%A0)
- [2.3 使用`Java Agent`来修饰`JDK`线程池实现类](#23-%E4%BD%BF%E7%94%A8java-agent%E6%9D%A5%E4%BF%AE%E9%A5%B0jdk%E7%BA%BF%E7%A8%8B%E6%B1%A0%E5%AE%9E%E7%8E%B0%E7%B1%BB)
- [`Java Agent`的启动参数配置](#java-agent%E7%9A%84%E5%90%AF%E5%8A%A8%E5%8F%82%E6%95%B0%E9%85%8D%E7%BD%AE)
- [🔌 Java API Docs](#-java-api-docs)
- [🍪 Maven依赖](#-maven%E4%BE%9D%E8%B5%96)
- [🔨 关于编译构建](#-%E5%85%B3%E4%BA%8E%E7%BC%96%E8%AF%91%E6%9E%84%E5%BB%BA)
- [❓ FAQ](#-faq)
- [✨ 使用`TTL`的好处与必要性](#-%E4%BD%BF%E7%94%A8ttl%E7%9A%84%E5%A5%BD%E5%A4%84%E4%B8%8E%E5%BF%85%E8%A6%81%E6%80%A7)
- [🗿 更多文档](#-%E6%9B%B4%E5%A4%9A%E6%96%87%E6%A1%A3)
- [📚 相关资料](#-%E7%9B%B8%E5%85%B3%E8%B5%84%E6%96%99)
- [JDK Core Classes](#jdk-core-classes)
- [💗 Who Used](#-who-used)
- [👷 Contributors](#-contributors)
----------------------------------------
# 🔧 功能
👉 `TransmittableThreadLocal`(`TTL`):在使用线程池等会池化复用线程的执行组件情况下,提供`ThreadLocal`值的传递功能,解决异步执行时上下文传递的问题。一个`Java`标准库本应为框架/中间件设施开发提供的标配能力,本库功能聚焦 & 0依赖,支持`Java 6~21`。
`JDK`的[`InheritableThreadLocal`](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/InheritableThreadLocal.html)类可以完成父线程到子线程的值传递。但对于使用线程池等会池化复用线程的执行组件的情况,线程由线程池创建好,并且线程是池化起来反复使用的;这时父子线程关系的`ThreadLocal`值传递已经没有意义,应用需要的实际上是把 **任务提交给线程池时**的`ThreadLocal`值传递到 **任务执行时**。
本库提供的[`TransmittableThreadLocal`](ttl-core/src/main/java/com/alibaba/ttl3/TransmittableThreadLocal.java)类继承并加强`InheritableThreadLocal`类,解决上述的问题,使用详见 [User Guide](#-user-guide)。
整个`TransmittableThreadLocal`库的核心功能(用户`API`、线程池`ExecutorService`/`ForkJoinPool`/`TimerTask`及其线程工厂的`Wrapper`;开发者`API`、框架/中间件的集成`API`),只有 **_~1000 `SLOC`代码行_**,非常精小。
欢迎 👏
- 建议和提问,[提交 Issue](https://github.com/alibaba/transmittable-thread-local/issues/new)
- 贡献和改进,[Fork 后提通过 Pull Request 贡献代码](https://github.com/alibaba/transmittable-thread-local/fork)
> [!NOTE]
> 从`TTL v2.13+`开始,升级到`Java 8`。🚀
> 如果需要`Java 6`的支持,使用版本`2.12.x` [](https://repo1.maven.org/maven2/com/alibaba/transmittable-thread-local/maven-metadata.xml)
# 🎨 需求场景
`ThreadLocal`的需求场景即`TransmittableThreadLocal`的潜在需求场景,如果你的业务需要『在使用线程池等会池化复用线程的执行组件情况下传递`ThreadLocal`值』则是`TransmittableThreadLocal`目标场景。
下面是几个典型场景例子。
1. 分布式跟踪系统 或 全链路压测(即链路打标)
2. 日志收集记录系统上下文
3. `Request`级`Cache`
4. 应用容器或上层框架跨应用代码给下层`SDK`传递信息
各个场景的展开说明参见子文档 [需求场景](docs/requirement-scenario.md)。
# 👥 User Guide
使用类[`TransmittableThreadLocal`](ttl-core/src/main/java/com/alibaba/ttl3/TransmittableThreadLocal.java)来保存值,并跨线程池传递。
`TransmittableThreadLocal`继承`InheritableThreadLocal`,使用方式也类似。相比`InheritableThreadLocal`,添加了`protected`的`transmitteeValue()`方法,用于定制 **任务提交给线程池时** 的`ThreadLocal`值传递到 **任务执行时** 的传递方式,缺省是简单的赋值传递。
注意:如果传递的对象(引用类型)会被修改,且没有做深拷贝(如直接传递引用或是浅拷贝),那么
- 因为跨线程传递而不再有线程封闭,传递对象在多个线程之间是有共享的。
- 与`JDK`的[`InheritableThreadLocal.childValue()`](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/InheritableThreadLocal.html#childValue(T))一样,需要使用者/业务逻辑注意保证传递对象的线程安全。
关于`transmitteeValue`方法 的 展开说明
关于构词后缀`er`与`ee`的说明:
`transmit`是动词传递,`transmitter`动作的执行者/主动方,而`transmittee`动作的接收者/被动方。
`er`与`ee`后缀的常见词是`employer`(雇主)/`employee`(雇员)、`caller`(调用者)/`callee`(被调用者)。
具体使用方式见下面的说明。
## 1. 简单使用
父线程给子线程传递值。
示例代码:
```java
TransmittableThreadLocal context = new TransmittableThreadLocal<>();
// =====================================================
// 在父线程中设置
context.set("value-set-in-parent");
// =====================================================
// 在子线程中可以读取,值是"value-set-in-parent"
String value = context.get();
```
\# 完整可运行的Demo代码参见[`SimpleDemo.kt`](ttl-core/src/test/java/com/alibaba/demo/ttl3/SimpleDemo.kt)。
这其实是`InheritableThreadLocal`的功能,应该使用`InheritableThreadLocal`来完成。
但对于使用线程池等会池化复用线程的执行组件的情况,线程由线程池创建好,并且线程是池化起来反复使用的;这时父子线程关系的`ThreadLocal`值传递已经没有意义,应用需要的实际上是把 **任务提交给线程池时**的`ThreadLocal`值传递到 **任务执行时**。
解决方法参见下面的这几种用法。
## 2. 保证线程池中传递值
### 2.1 修饰`Runnable`和`Callable`
使用[`TtlRunnable`](ttl-core/src/main/java/com/alibaba/ttl3/TtlRunnable.java)和[`TtlCallable`](ttl-core/src/main/java/com/alibaba/ttl3/TtlCallable.java)来修饰传入线程池的`Runnable`和`Callable`。
示例代码:
```java
TransmittableThreadLocal context = new TransmittableThreadLocal<>();
// =====================================================
// 在父线程中设置
context.set("value-set-in-parent");
Runnable task = new RunnableTask();
// 额外的处理,生成修饰了的对象ttlRunnable
Runnable ttlRunnable = TtlRunnable.get(task);
executorService.submit(ttlRunnable);
// =====================================================
// Task中可以读取,值是"value-set-in-parent"
String value = context.get();
```
**_注意_**:
即使是同一个`Runnable`任务多次提交到线程池时,每次提交时都需要通过修饰操作(即`TtlRunnable.get(task)`)以抓取这次提交时的`TransmittableThreadLocal`上下文的值;即如果同一个任务下一次提交时不执行修饰而仍然使用上一次的`TtlRunnable`,则提交的任务运行时会是之前修饰操作所抓取的上下文。示例代码如下:
```java
// 第一次提交
Runnable task = new RunnableTask();
executorService.submit(TtlRunnable.get(task));
// ...业务逻辑代码,
// 并且修改了 TransmittableThreadLocal上下文 ...
context.set("value-modified-in-parent");
// 再次提交
// 重新执行修饰,以传递修改了的 TransmittableThreadLocal上下文
executorService.submit(TtlRunnable.get(task));
```
上面演示了`Runnable`,`Callable`的处理类似
```java
TransmittableThreadLocal context = new TransmittableThreadLocal<>();
// =====================================================
// 在父线程中设置
context.set("value-set-in-parent");
Callable call = new CallableTask();
// 额外的处理,生成修饰了的对象ttlCallable
Callable ttlCallable = TtlCallable.get(call);
executorService.submit(ttlCallable);
// =====================================================
// Call中可以读取,值是"value-set-in-parent"
String value = context.get();
```
\# 完整可运行的Demo代码参见[`TtlWrapperDemo.kt`](ttl-core/src/test/java/com/alibaba/demo/ttl3/TtlWrapperDemo.kt)。
#### 整个过程的完整时序图
[](#dummy)
### 2.2 修饰线程池
省去每次`Runnable`和`Callable`传入线程池时的修饰,这个逻辑可以在线程池中完成。
通过工具类[`TtlExecutors`](ttl-core/src/main/java/com/alibaba/ttl3/executor/TtlExecutors.java)完成,有下面的方法:
- `getTtlExecutor`:修饰接口`Executor`
- `getTtlExecutorService`:修饰接口`ExecutorService`
- `getTtlScheduledExecutorService`:修饰接口`ScheduledExecutorService`
示例代码:
```java
ExecutorService executorService = ...
// 额外的处理,生成修饰了的对象executorService
executorService = TtlExecutors.getTtlExecutorService(executorService);
TransmittableThreadLocal context = new TransmittableThreadLocal<>();
// =====================================================
// 在父线程中设置
context.set("value-set-in-parent");
Runnable task = new RunnableTask();
Callable call = new CallableTask();
executorService.submit(task);
executorService.submit(call);
// =====================================================
// Task或是Call中可以读取,值是"value-set-in-parent"
String value = context.get();
```
\# 完整可运行的Demo代码参见[`TtlExecutorWrapperDemo.kt`](ttl-core/src/test/java/com/alibaba/demo/ttl3/TtlExecutorWrapperDemo.kt)。
### 2.3 使用`Java Agent`来修饰`JDK`线程池实现类
这种方式,实现线程池的传递是透明的,业务代码中没有修饰`Runnable`或是线程池的代码。即可以做到应用代码 **无侵入**。
\# 关于 **无侵入** 的更多说明参见文档[`Java Agent`方式对应用代码无侵入](docs/developer-guide.md#java-agent%E6%96%B9%E5%BC%8F%E5%AF%B9%E5%BA%94%E7%94%A8%E4%BB%A3%E7%A0%81%E6%97%A0%E4%BE%B5%E5%85%A5)。
示例代码:
```java
// ## 1. 框架上层逻辑,后续流程框架调用业务 ##
TransmittableThreadLocal context = new TransmittableThreadLocal<>();
context.set("value-set-in-parent");
// ## 2. 应用逻辑,后续流程业务调用框架下层逻辑 ##
ExecutorService executorService = Executors.newFixedThreadPool(3);
Runnable task = new RunnableTask();
Callable call = new CallableTask();
executorService.submit(task);
executorService.submit(call);
// ## 3. 框架下层逻辑 ##
// Task或是Call中可以读取,值是"value-set-in-parent"
String value = context.get();
```
Demo参见[`AgentDemo.kt`](ttl2-compatible/src/test/java/com/alibaba/demo/ttl/agent/AgentDemo.kt)。执行工程下的脚本[`scripts/run-agent-demo.sh`](scripts/run-agent-demo.sh)即可运行Demo。
目前`TTL Agent`中,修饰了的`JDK`执行器组件(即如线程池)如下:
1. `java.util.concurrent.ThreadPoolExecutor` 和 `java.util.concurrent.ScheduledThreadPoolExecutor`
- 修饰实现代码在[`JdkExecutorTtlTransformlet.java`](ttl-agent/src/main/java/com/alibaba/ttl3/agent/transformlet/internal/JdkExecutorTtlTransformlet.java)。
1. `java.util.concurrent.ForkJoinTask`(对应的执行器组件是`java.util.concurrent.ForkJoinPool`)
- 修饰实现代码在[`ForkJoinTtlTransformlet.java`](ttl-agent/src/main/java/com/alibaba/ttl3/agent/transformlet/internal/ForkJoinTtlTransformlet.java)。从版本 **_`2.5.1`_** 开始支持。
- **_注意_**:`Java 8`引入的[**_`CompletableFuture`_**](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/CompletableFuture.html)与(并行执行的)[**_`Stream`_**](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/stream/package-summary.html)底层是通过`ForkJoinPool`来执行,所以支持`ForkJoinPool`后,`TTL`也就透明支持了`CompletableFuture`与`Stream`。🎉
1. `java.util.TimerTask`的子类(对应的执行器组件是`java.util.Timer`)
- 修饰实现代码在[`TimerTaskTtlTransformlet.java`](ttl-agent/src/main/java/com/alibaba/ttl3/agent/transformlet/internal/TimerTaskTtlTransformlet.java)。从版本 **_`2.7.0`_** 开始支持。
- **_注意_**:从`2.11.2`版本开始缺省开启`TimerTask`的修饰(因为保证正确性是第一位,而不是最佳实践『不推荐使用`TimerTask`』:);`2.11.1`版本及其之前的版本没有缺省开启`TimerTask`的修饰。
- 使用`Agent`参数`ttl.agent.enable.timer.task`开启/关闭`TimerTask`的修饰:
- `-javaagent:path/to/transmittable-thread-local-2.x.y.jar=ttl.agent.enable.timer.task:true`
- `-javaagent:path/to/transmittable-thread-local-2.x.y.jar=ttl.agent.enable.timer.task:false`
- 更多关于`TTL Agent`参数的配置说明详见[`TtlAgent.java`的JavaDoc](ttl-agent/src/main/java/com/alibaba/ttl3/agent/TtlAgent.java)。
关于`java.util.TimerTask`/`java.util.Timer` 的 展开说明
`Timer`是`JDK 1.3`的老类,不推荐使用`Timer`类。
推荐用[`ScheduledExecutorService`](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/ScheduledExecutorService.html)。
`ScheduledThreadPoolExecutor`实现更强壮,并且功能更丰富。
如支持配置线程池的大小(`Timer`只有一个线程);`Timer`在`Runnable`中抛出异常会中止定时执行。更多说明参见 [10. **Mandatory** Run multiple TimeTask by using ScheduledExecutorService rather than Timer because Timer will kill all running threads in case of failing to catch exceptions. - Alibaba Java Coding Guidelines](https://alibaba.github.io/Alibaba-Java-Coding-Guidelines/#concurrency)。
#### `Java Agent`的启动参数配置
在`Java`的启动参数加上:`-javaagent:path/to/transmittable-thread-local-2.x.y.jar`。
**_注意_**:
- 如果修改了下载的`TTL`的`Jar`的文件名(`transmittable-thread-local-2.x.y.jar`),则需要自己手动通过`-Xbootclasspath JVM`参数来显式配置。
比如修改文件名成`ttl-foo-name-changed.jar`,则还需要加上`Java`的启动参数:`-Xbootclasspath/a:path/to/ttl-foo-name-changed.jar`。
- 或使用`v2.6.0`之前的版本(如`v2.5.1`),则也需要自己手动通过`-Xbootclasspath JVM`参数来显式配置(就像`TTL`之前的版本的做法一样)。
加上`Java`的启动参数:`-Xbootclasspath/a:path/to/transmittable-thread-local-2.5.1.jar`。
`Java`命令行示例如下:
```bash
java -javaagent:path/to/transmittable-thread-local-2.x.y.jar \
-cp classes \
com.alibaba.demo.ttl.agent.AgentDemo
# 如果修改了TTL jar文件名 或 TTL版本是 2.6.0 之前
# 则还需要显式设置 -Xbootclasspath 参数
java -javaagent:path/to/ttl-foo-name-changed.jar \
-Xbootclasspath/a:path/to/ttl-foo-name-changed.jar \
-cp classes \
com.alibaba.demo.ttl.agent.AgentDemo
java -javaagent:path/to/transmittable-thread-local-2.5.1.jar \
-Xbootclasspath/a:path/to/transmittable-thread-local-2.5.1.jar \
-cp classes \
com.alibaba.demo.ttl.agent.AgentDemo
```
关于`boot class path` 的 展开说明
因为修饰了`JDK`标准库的类,标准库由`bootstrap class loader`加载;修饰后的`JDK`类引用了`TTL`的代码,所以`Java Agent`使用方式下`TTL Jar`文件需要配置到`boot class path`上。
`TTL`从`v2.6.0`开始,加载`TTL Agent`时会自动设置`TTL Jar`到`boot class path`上。
***注意***:不能修改从`Maven`库下载的`TTL Jar`文件名(形如`transmittable-thread-local-2.x.y.jar`)。
如果修改了,则需要自己手动通过`-Xbootclasspath JVM`参数来显式配置(就像`TTL`之前的版本的做法一样)。
自动设置`TTL Jar`到`boot class path`的实现是通过指定`TTL Java Agent Jar`文件里`manifest`文件(`META-INF/MANIFEST.MF`)的`Boot-Class-Path`属性:
`Boot-Class-Path`
A list of paths to be searched by the bootstrap class loader. Paths represent directories or libraries (commonly referred to as JAR or zip libraries on many platforms).
These paths are searched by the bootstrap class loader after the platform specific mechanisms of locating a class have failed. Paths are searched in the order listed.
更多详见
[`Java Agent`规范 - `JavaDoc`](https://docs.oracle.com/en/java/javase/21/docs/api/java.instrument/java/lang/instrument/package-summary.html#package.description)
[JAR File Specification - JAR Manifest](https://docs.oracle.com/en/java/javase/21/docs/specs/jar/jar.html#jar-manifest)
[Working with Manifest Files - The Java™ Tutorials](https://docs.oracle.com/javase/tutorial/deployment/jar/manifestindex.html)
# 🔌 Java API Docs
当前版本的Java API文档地址:
# 🍪 Maven依赖
示例:
```xml
com.alibaba
transmittable-thread-local
2.14.4
```
可以在 [maven.org](https://repo1.maven.org/maven2/com/alibaba/transmittable-thread-local/maven-metadata.xml) 查看可用的版本。
# 🔨 关于编译构建
编译构建的环境要求: **_`JDK 8+`_**;用`Maven`常规的方式执行编译构建即可:
\# 在工程中已经包含了符合版本要求的`Maven`,直接运行 **_工程根目录下的`mvnw`_**;并不需要先手动自己安装好`Maven`。
```bash
# 运行测试Case
./mvnw test
# 编译打包
./mvnw package
# 运行测试Case、编译打包、安装TTL库到Maven本地
./mvnw install
#####################################################
# 如果使用你自己安装的 maven,版本要求:maven 3.3.9+
mvn install
```
# ❓ FAQ
**_Q1. `TTL Agent`与其它`Agent`(如`Skywalking`、`Promethues`)配合使用时不生效?_**
配置`TTL Agent`在最前的位置,可以避免与其它其它`Agent`配合使用时,`TTL Agent`可能的不生效问题。配置示例:
```bash
java -javaagent:path/to/transmittable-thread-local-2.x.y.jar \
-javaagent:path/to/skywalking-agent.jar \
-jar your-app.jar
```
原因是:
- 像`Skywalking`这样的`Agent`的入口逻辑(`premain`)包含了线程池的启动。
- 如果配置在这样的`Agent`配置在前面,到了`TTL Agent`(的`premain`)时,`TTL`需要加强的线程池类已经加载(`load`)了。
- `TTL Agent`的`TtlTransformer`是在类加载时触发类的增强;如果类已经加载了会跳过`TTL Agent`的增强逻辑。
更多讨论参见 [Issue:`TTL agent`与其他`Agent`的兼容性问题 #226](https://github.com/alibaba/transmittable-thread-local/issues/226)。
**_Q2. `MacOS`下,使用`Java Agent`,可能会报`JavaLaunchHelper`的出错信息_**
JDK Bug:
可以换一个版本的`JDK`。我的开发机上`1.7.0_40`有这个问题,`1.6.0_51`、`1.7.0_45`可以运行。
\# `1.7.0_45`还是有`JavaLaunchHelper`的出错信息,但不影响运行。
# ✨ 使用`TTL`的好处与必要性
> [!NOTE]
> 不读这一节,并不会影响你使用`TTL`来解决你碰到的问题,可以放心跳过;读了 [User Guide](#-user-guide) 就可以快速用起来了~ 😄 这一节信息密度较高不易读。
**_好处:透明且自动完成所有异步执行上下文的可定制、规范化的捕捉与传递。_**
这个好处也是`TransmittableThreadLocal`的目标。
**_必要性:随着应用的分布式微服务化并使用各种中间件,越来越多的功能与组件会涉及不同的上下文,逻辑流程也越来越长;上下文问题实际上是个大的易错的架构问题,需要统一的对业务透明的解决方案。_**
使用`ThreadLocal`作为业务上下文传递的经典技术手段在中间件、技术与业务框架中广泛大量使用。而对于生产应用,几乎一定会使用线程池等异步执行组件,以高效支撑线上大流量。但使用`ThreadLocal`及其`set/remove`的上下文传递模式,在使用线程池等异步执行组件时,存在多方面的问题:
**_1. 从业务使用者角度来看_**
1. **繁琐**
- 业务逻辑要知道:有哪些上下文;各个上下文是如何获取的。
- 并需要业务逻辑去一个一个地捕捉与传递。
1. **依赖**
- 需要直接依赖不同`ThreadLocal`上下文各自的获取的逻辑或类。
- 像`RPC`的上下文(如`Dubbo`的`RpcContext`)、全链路跟踪的上下文(如`SkyWalking`的`ContextManager`)、不同业务模块中的业务流程上下文,等等。
1. **静态(易漏)**
- 因为要 **_事先_** 知道有哪些上下文,如果系统出现了一个新的上下文,业务逻辑就要修改添加上新上下文传递的几行代码。也就是说因 **_系统的_** 上下文新增,**_业务的_** 逻辑就跟进要修改。
- 而对于业务来说,不关心系统的上下文,即往往就可能遗漏,会是线上故障了。
- 随着应用的分布式微服务化并使用各种中间件,越来越多的功能与组件会涉及不同的上下文,逻辑流程也越来越长;上下文问题实际上是个大的易错的架构问题,需要统一的对业务透明的解决方案。
1. **定制性**
- 因为需要业务逻辑来完成捕捉与传递,业务要关注『上下文的传递方式』:直接传引用?还是拷贝传值?拷贝是深拷贝还是浅拷贝?在不同的上下文会需要不同的做法。
- 『上下文的传递方式』往往是 **_上下文的提供者_**(或说是业务逻辑的框架部分)才能决策处理好的;而 **_上下文的使用者_**(或说是业务逻辑的应用部分)往往不(期望)知道上下文的传递方式。这也可以理解成是 **_依赖_**,即业务逻辑 依赖/关注/实现了 系统/架构的『上下文的传递方式』。
**_2. 从整体流程实现角度来看_**
关注的是 **上下文传递流程的规范化**。上下文传递到了子线程要做好 **_清理_**(或更准确地说是要 **_恢复_** 成之前的上下文),需要业务逻辑去处理好。如果业务逻辑对**清理**的处理不正确,比如:
- 如果清理操作漏了:
- 下一次执行可能是上次的,即『上下文的 **_污染_**/**_串号_**』,会导致业务逻辑错误。
- 『上下文的 **_泄漏_**』,会导致内存泄漏问题。
- 如果清理操作做多了,会出现上下文 **_丢失_**。
上面的问题,在业务开发中引发的`Bug`真是**屡见不鲜** !本质原因是:**_`ThreadLocal`的`set/remove`的上下文传递模式_** 在使用线程池等异步执行组件的情况下不再是有效的。常见的典型例子:
- 当线程池满了且线程池的`RejectedExecutionHandler`使用的是`CallerRunsPolicy`时,提交到线程池的任务会在提交线程中直接执行,`ThreadLocal.remove`操作**清理**提交线程的上下文导致上下文**丢失**。
- 类似的,使用`ForkJoinPool`(包含并行执行`Stream`与`CompletableFuture`,底层使用`ForkJoinPool`)的场景,展开的`ForkJoinTask`会在任务提交线程中直接执行。同样导致上下文**丢失**。
怎么设计一个『上下文传递流程』方案(即上下文的生命周期),以**保证**没有上面的问题?
期望:上下文生命周期的操作从业务逻辑中分离出来。业务逻辑不涉及生命周期,就不会有业务代码如疏忽清理而引发的问题了。整个上下文的传递流程或说生命周期可以规范化成:捕捉、回放和恢复这3个操作,即[**_`CRR(capture/replay/restore)`模式_**](docs/developer-guide.md#-%E6%A1%86%E6%9E%B6%E4%B8%AD%E9%97%B4%E4%BB%B6%E9%9B%86%E6%88%90ttl%E4%BC%A0%E9%80%92)。更多讨论参见 [Issue:能在详细讲解一下`replay`、`restore`的设计理念吗?#201](https://github.com/alibaba/transmittable-thread-local/issues/201)。
总结上面的说明:在生产应用(几乎一定会使用线程池等异步执行组件)中,使用`ThreadLocal`及其`set/remove`的上下文传递模式**几乎一定是有问题的**,**_只是在等一个出`Bug`的机会_**。
更多`TTL`好处与必要性的展开讨论参见 [Issue:这个库带来怎样的好处和优势? #128](https://github.com/alibaba/transmittable-thread-local/issues/128),欢迎继续讨论 ♥️
# 🗿 更多文档
- [🎨 需求场景说明](docs/requirement-scenario.md)
- [❤️ 小伙伴同学们写的`TTL`使用场景 与 设计实现解析的文章(写得都很好!) - Issue #123](https://github.com/alibaba/transmittable-thread-local/issues/123)
- [🎓 Developer Guide](docs/developer-guide.md)
- [☔ 性能测试](docs/performance-test.md)
# 📚 相关资料
## JDK Core Classes
- [WeakHashMap](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/WeakHashMap.html)
- [InheritableThreadLocal](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/InheritableThreadLocal.html)
# 💗 Who Used
使用了`TTL`的一部分开源项目:
- **中间件**
- [`sofastack/sofa-rpc` ](https://github.com/sofastack/sofa-rpc) [](https://gitee.com/sofastack/sofa-rpc)
SOFARPC is a high-performance, high-extensibility, production-level Java RPC framework
- [`trpc-group/trpc-java` ](https://github.com/trpc-group/trpc-java)
A pluggable, high-performance RPC framework written in java
- [`tencentmusic/supersonic` ](https://github.com/tencentmusic/supersonic)
SuperSonic is an out-of-the-box yet highly extensible framework for building ChatBI
- [`dromara/hmily` ](https://github.com/dromara/hmily) [](https://gitee.com/dromara/hmily)
Distributed transaction solutions
- [`dromara/gobrs-async` ](https://github.com/dromara/gobrs-async) [](https://gitee.com/dromara/gobrs-async)
一款功能强大、配置灵活、带有全链路异常回调、内存优化、异常状态管理于一身的高性能异步编排框架。为企业提供在复杂应用场景下动态任务编排的能力。 针对于复杂场景下,异步线程复杂性、任务依赖性、异常状态难控制性
- [`dromara/dynamic-tp` ](https://github.com/dromara/dynamic-tp) [](https://gitee.com/dromara/dynamic-tp)
轻量级动态线程池,内置监控告警功能,支持线程池上下文传递,基于主流配置中心(已支持Nacos、Apollo,Zookeeper,可通过SPI自定义实现)
- [`opengoofy/hippo4j` ](https://github.com/opengoofy/hippo4j) [](https://gitee.com/magestack/hippo4j)
动态线程池框架,附带监控报警功能,支持 JDK、Tomcat、Jetty、Undertow 线程池;Apache RocketMQ、Dubbo、RabbitMQ、Hystrix 消费等线程池。内置两种使用模式:轻量级依赖配置中心以及无中间件依赖版本
- [`siaorg/sia-gateway` ](https://github.com/siaorg/sia-gateway)
微服务路由网关(zuul-plus)
- [`huaweicloud/Sermant` ](https://github.com/huaweicloud/Sermant)
Sermant, a proxyless service mesh solution based on Javaagent
- [`ZTO-Express/zms` ](https://github.com/ZTO-Express/zms) [](https://gitee.com/zto_express/zms)
ZTO Message Service
- [`lxchinesszz/tomato` ](https://github.com/lxchinesszz/tomato)
一款专门为SpringBoot项目设计的幂等组件
- [`ytyht226/taskflow` ](https://github.com/ytyht226/taskflow)
一款轻量、简单易用、可灵活扩展的通用任务编排框架,基于有向无环图(DAG)的方式实现,框架提供了组件复用、同步/异步编排、条件判断、分支选择等能力,可以根据不同的业务场景对任意的业务流程进行编排
- [`foldright/cffu` ](https://github.com/foldright/cffu)
🦝 Java CompletableFuture Fu, aka. CF-Fu, pronounced "Shifu"; include best practice/traps guide and a tiny sidekick library to improve user experience and reduce misuse.
- [`tuya/connector` ](https://github.com/tuya/connector)
The connector framework maps cloud APIs to local APIs based on simple configurations and flexible extension mechanisms
- **中间件/数据处理**
- [`apache/shardingsphere` ](https://github.com/apache/shardingsphere) [](https://gitee.com/Sharding-Sphere/sharding-sphere)
Ecosystem to transform any database into a distributed database system, and enhance it with sharding, elastic scaling, encryption features & more
- [`apache/kylin` ](https://github.com/apache/kylin)
A unified and powerful OLAP platform for Hadoop and Cloud.
- [`mybatis-flex/mybatis-flex` ](https://github.com/mybatis-flex/mybatis-flex) [](https://gitee.com/mybatis-flex/mybatis-flex)
mybatis-flex is an elegant Mybatis Enhancement Framework
- [`basicai/xtreme1` ](https://github.com/basicai/xtreme1)
The Next GEN Platform for Multisensory Training Data. #3D annotation, lidar-camera annotation and image annotation tools are supported
- [`oceanbase/odc` ](https://github.com/oceanbase/odc)
An open-source, enterprise-grade database tool for collaborative development
- [`sagframe/sagacity-sqltoy` ](https://github.com/sagframe/sagacity-sqltoy)
Java真正智慧的ORM框架
- [`dromara/stream-query` ](https://github.com/dromara/stream-query) [](https://gitee.com/dromara/stream-query)
允许完全摆脱Mapper的mybatis-plus体验;可以使用类似“工具类”这样的静态函数进行数据库操作
- [`luo-zhan/Transformer` ](https://github.com/luo-zhan/Transformer)
Transformer可能是最简单,但最强大的字段转换插件,一个注解搞定任意转换,让开发变得更加丝滑
- [`SimonAlong/Neo` ](https://github.com/SimonAlong/Neo)
Orm框架:基于ActiveRecord思想开发的至简化且功能很全的Orm框架
- [`ppdaicorp/das` ](https://github.com/ppdaicorp/das)
数据库访问框架(data access service),包括数据库控制台das console,数据库客户端das client和数据库服务端das server三部分
- [`didi/ALITA` ](https://github.com/didi/ALITA)
a layer-based data analysis tool
- [`didi/daedalus` ](https://github.com/didi/daedalus)
实现快速创建数据构造流程,数据构造流程的可视化、线上化、持久化、标准化
- **中间件/流程引擎**
- [`dromara/liteflow` ](https://github.com/dromara/liteflow) [](https://gitee.com/dromara/liteFlow)
a lightweight and practical micro-process framework
- [`alibaba/bulbasaur` ](https://github.com/alibaba/bulbasaur)
A pluggable, scalable process engine
- **中间件/日志**
- [`dromara/TLog` ](https://github.com/dromara/TLog) [](https://gitee.com/dromara/TLog)
Lightweight distributed log label tracking framework
- [`fayechenlong/plumelog` ](https://github.com/fayechenlong/plumelog) [](https://gitee.com/plumeorg/plumelog)
一个java分布式日志组件,支持百亿级别
- [`minbox-projects/minbox-logging` ](https://github.com/minbox-projects/minbox-logging) [](https://gitee.com/minbox-projects/minbox-logging)
分布式零侵入式、链路式请求日志分析框架。提供Admin端点进行采集日志、分析日志、日志告警通知、服务性能分析等。通过Admin Ui可查看实时链路日志信息、在线业务服务列表
- [`minbox-projects/api-boot` ](https://github.com/minbox-projects/api-boot) [](https://gitee.com/minbox-projects/api-boot)
为接口服务而生的,基于“ SpringBoot”完成扩展和自动配置,内部封装了一系列的开箱即用Starters
- [`ofpay/logback-mdc-ttl` ](https://github.com/ofpay/logback-mdc-ttl)
logback扩展,集成transmittable-thread-local支持跨线程池的mdc跟踪
- [`oldratlee/log4j2-ttl-thread-context-map` ](https://github.com/oldratlee/log4j2-ttl-thread-context-map)
Log4j2 TTL ThreadContextMap, Log4j2 extension integrated TransmittableThreadLocal to MDC
- [`qqxx6661/log-record` ](https://github.com/qqxx6661/log-record)
业务日志记录框架,使用注解优雅记录日志,支持SpEL表达式,自定义上下文,自定义函数,实体类DIFF等特性。
- **中间件/字节码**
- [`ymm-tech/easy-byte-coder` ](https://github.com/ymm-tech/easy-byte-coder)
Easy-byte-coder is a non-invasive bytecode injection framework based on JVM
- **业务服务或平台应用**
- [`OpenBankProject/OBP-API` ](https://github.com/OpenBankProject/OBP-API)
An open source RESTful API platform for banks that supports Open Banking, XS2A and PSD2 through access to accounts, transactions, counterparties, payments, entitlements and metadata - plus a host of internal banking and management APIs
- [`gz-yami/mall4j` ](https://github.com/gz-yami/mall4j) [](https://gitee.com/gz-yami/mall4j)
电商商城 java电商商城系统 uniapp商城 多用户商城
- [`Joolun/JooLun-wx` ](https://github.com/Joolun/JooLun-wx) [](https://gitee.com/joolun/JooLun-wx)
JooLun微信商城
- [`HummerRisk/HummerRisk` ](https://github.com/HummerRisk/HummerRisk)
云原生安全平台,包括混合云安全治理和容器云安全检测
- [`XiaoMi/mone` ](https://github.com/XiaoMi/mone)
`Mone`以微服务为核心的一站式企业协同研发平台。支持公共云、专有云和混合云多种部署形态;提供从“项目创建->开发->部署->治理->应用观测”端到端的研发全流程服务;通过云原生新技术和研发新模式,打造“双敏”,敏捷研发和敏捷组织,保障小米-中国区高复杂业务、大规模团队的敏捷研发协同,实现多倍效能提升。
- [`yangzongzhuan/RuoYi-Cloud` ](https://github.com/yangzongzhuan/RuoYi-Cloud) [](https://gitee.com/y_project/RuoYi-Cloud)
基于Spring Boot、Spring Cloud & Alibaba的分布式微服务架构权限管理系统
- [`somowhere/albedo` ](https://github.com/somowhere/albedo) [](https://gitee.com/somowhere/albedo)
基于 Spring Boot 、Spring Security、Mybatis 的RBAC权限管理系统
- [`qwdigital/LinkWechat` ](https://github.com/qwdigital/LinkWechat) [](https://gitee.com/LinkWeChat/link-wechat)
基于企业微信的开源 SCRM 系统,采用主流的 Java 微服务架构,是企业私域流量管理与营销的综合解决方案,助力企业提高客户运营效率,强化营销能力,拓展盈利空间
- [`fushengqian/fuint` ](https://github.com/fushengqian/fuint) [](https://gitee.com/fuint/fuint-uniapp)
fuint会员营销系统是一套开源的实体店铺会员管理和营销系统
- [`hiparker/opsli-boot` ](https://github.com/hiparker/opsli-boot) [](https://gitee.com/hiparker/opsli-boot)
一款的低代码快速平台,零代码开发,致力于做更简洁的后台管理系统
- [`topiam/eiam` ](https://github.com/topiam/eiam) [](https://gitee.com/topiam/eiam)
EIAM(Employee Identity and Access Management Program)企业级开源IAM平台,实现用户全生命周期的管理、统一认证和单点登录、为数字身份安全赋能
- [`Newspiral/newspiral-business` ](https://github.com/Newspiral/newspiral-business)
联盟区块链底层平台
- **工具产品**
- [`ssssssss-team/spider-flow` ](https://github.com/ssssssss-team/spider-flow) [](https://gitee.com/ssssssss-team/spider-flow)
新一代爬虫平台,以图形化方式定义爬虫流程,不写代码即可完成爬虫
- [`nekolr/slime` ](https://github.com/nekolr/slime)
🍰 一个可视化的爬虫平台
- [`Jackson0714/PassJava-Platform` ](https://github.com/Jackson0714/PassJava-Platform)
一款面试刷题的 Spring Cloud 开源系统。零碎时间利用小程序查看常见面试题,夯实Java基础。 该项目可以教会你如何搭建SpringBoot项目,Spring Cloud项目。 采用流行的技术,如 SpringBoot、MyBatis、Redis、 MySql、 MongoDB、 RabbitMQ、Elasticsearch,采用Docker容器化部署
- [`martin-chips/DimpleBlog` ](https://github.com/martin-chips/DimpleBlog)
基于`SpringBoot2`搭建的个人博客系统
- [`zjcscut/octopus` ](https://github.com/zjcscut/octopus)
长链接压缩为短链接的服务
- [`xggz/mqr` ](https://github.com/xggz/mqr) [](https://gitee.com/mlyai/mqr)
茉莉QQ机器人(简称MQR),采用mirai的Android协议实现的QQ机器人服务,通过web控制机器人的启停和配置
- **测试解决方案或工具**
- [`alibaba/jvm-sandbox-repeater` ](https://github.com/alibaba/jvm-sandbox-repeater)
A Java server-side recording and playback solution based on JVM-Sandbox, 录制/回放通用解决方案
- [`vivo/MoonBox` ](https://github.com/vivo/MoonBox)
Moonbox(月光宝盒)是JVM-Sandbox生态下的,基于jvm-sandbox-repeater重新开发的,一款流量回放平台产品。相较于jvm-sandbox-repeater,Moonbox功能更加丰富、数据可靠性更高,同时便于快速线上部署和使用
- [`alibaba/testable-mock` ](https://github.com/alibaba/testable-mock)
换种思路写Mock,让单元测试更简单
- [`shulieTech/Takin` ](https://github.com/shulieTech/Takin)
全链路压测平台,measure online environmental performance test for full-links, Especially for microservices
- [`shulieTech/LinkAgent` ](https://github.com/shulieTech/LinkAgent)
a Java-based open-source agent designed to collect data and control Functions for Java applications through JVM bytecode, without modifying applications codes
- [`alibaba/virtual-environment` ](https://github.com/alibaba/virtual-environment)
Route isolation with service sharing, 阿里测试环境服务隔离和联调机制的`Kubernetes`版实现
- **`Spring Cloud`/`Spring Boot`的框架方案/脚手架**
- [`YunaiV/ruoyi-vue-pro` ](https://github.com/YunaiV/ruoyi-vue-pro) [](https://gitee.com/zhijiantianya/ruoyi-vue-pro)
一套全部开源的企业级的快速开发平台。基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 微信小程序,支持 RBAC 动态权限、数据权限、SaaS 多租户、Activiti + Flowable 工作流、三方登录、支付、短信、商城等功能
- [`YunaiV/yudao-cloud` ](https://github.com/YunaiV/yudao-cloud) [](https://gitee.com/zhijiantianya/yudao-cloud)
RuoYi-Vue 全新 Cloud 版本,优化重构所有功能。基于 Spring Cloud Alibaba + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
- [`zlt2000/microservices-platform` ](https://github.com/zlt2000/microservices-platform) [](https://gitee.com/zlt2000/microservices-platform)
基于SpringBoot2.x、SpringCloud和SpringCloudAlibaba并采用前后端分离的企业级微服务多租户系统架构
- [`dromara/lamp-cloud` ](https://github.com/zuihou/lamp-cloud) [](https://gitee.com/dromara/lamp-cloud)
基于Jdk11 + SpringCloud + SpringBoot 的微服务快速开发平台,其中的可配置的SaaS功能尤其闪耀, 具备RBAC功能、网关统一鉴权、Xss防跨站攻击、自动代码生成、多种存储系统、分布式事务、分布式定时任务等多个模块,支持多业务系统并行开发, 支持多服务并行开发,可以作为后端服务的开发脚手架
- [`zuihou/lamp-util` ](https://github.com/zuihou/lamp-util) [](https://gitee.com/zuihou111/lamp-util)
打造一套兼顾 SpringBoot 和 SpringCloud 项目的公共工具类
- [`matevip/matecloud` ](https://github.com/matevip/matecloud) [](https://gitee.com/matevip/matecloud)
一款基于Spring Cloud Alibaba的微服务架构
- [`gavenwangcn/vole` ](https://github.com/gavenwangcn/vole)
SpringCloud 微服务业务脚手架
- [`liuweijw/fw-cloud-framework` ](https://github.com/liuweijw/fw-cloud-framework) [](https://gitee.com/liuweijw/fw-cloud-framework)
基于springcloud全家桶开发分布式框架(支持oauth2认证授权、SSO登录、统一下单、微信公众号服务、Shardingdbc分库分表、常见服务监控、链路监控、异步日志、redis缓存等功能),实现基于Vue全家桶等前后端分离项目工程
- [`liuht777/Taroco` ](https://github.com/liuht777/Taroco)
整合Nacos、Spring Cloud Alibaba,提供了一系列starter组件, 同时提供服务治理、服务监控、OAuth2权限认证,支持服务降级/熔断、服务权重
- [`mingyang66/spring-parent` ](https://github.com/mingyang66/spring-parent)
数据库多数据源、Redis多数据源、日志组件、全链路日志追踪、埋点扩展点、Netty、微服务、开发基础框架支持、异常统一处理、返回值、跨域、API路由、监控等
- [`budwk/budwk` ](https://github.com/budwk/budwk) [](https://gitee.com/budwk/budwk)
`BudWk` 原名 [`NutzWk` ](https://github.com/Wizzercn/NutzWk) [](https://gitee.com/wizzer/NutzWk),基于国产框架 nutz 及 nutzboot 开发的开源Web基础项目,集权限体系、系统参数、数据字典、站内消息、定时任务、CMS、微信等最常用功能,不庞杂、不面面俱到,使其具有上手容易、开发便捷、扩展灵活等特性,特别适合各类大中小型定制化项目需求
- [`yinjihuan/spring-cloud` ](https://github.com/yinjihuan/spring-cloud)
《Spring Cloud微服务-全栈技术与案例解析》和《Spring Cloud微服务 入门 实战与进阶》配套源码
- [`louyanfeng25/ddd-demo` ](https://github.com/louyanfeng25/ddd-demo)
《深入浅出DDD》讲解的演示项目,为了能够更好的理解Demo中的分层与逻辑处理,我强烈建议你配合小册来深入了解DDD
- [`nageoffer/12306` ](https://github.com/nageoffer/12306)
12306 铁路购票服务是与大家生活和出行相关的关键系统,包括会员、购票、订单、支付和网关等服务。
更多使用`TTL`的开源项目 参见 [](https://github.com/alibaba/transmittable-thread-local/network/dependents)
# 👷 Contributors
- Jerry Lee \ [@oldratlee](https://github.com/oldratlee)
- Yang Fang \ [@driventokill](https://github.com/driventokill)
- Zava Xu \ [@zavakid](https://github.com/zavakid)
- wuwen \ [@wuwen5](https://github.com/wuwen5)
- rybalkinsd \ [@rybalkinsd](https://github.com/rybalkinsd)
- David Dai \<351450944 at qq dot com> [@LNAmp](https://github.com/LNAmp)
- Your name here :-)
[](https://github.com/alibaba/transmittable-thread-local/graphs/contributors)
## 2. In-Tree Documentation Chapters (alibaba/transmittable-thread-local)
## File: README.md
#
> [!IMPORTANT]
> 🚧 这个分支是`TransmittableThreadLocal(TTL) v3`,在开发中还没有发布。
> `v3`的版本说明、工作项列表及其进展,参见 [issue 432](https://github.com/alibaba/transmittable-thread-local/issues/432)。
>
> 👉 目前使用中的稳定发布版本`v2.x`在 [**分支`2.x`**](https://github.com/alibaba/transmittable-thread-local/tree/2.x)上。
[📖 English Documentation](README-EN.md) | 📖 中文文档
----------------------------------------
- [🔧 功能](#-%E5%8A%9F%E8%83%BD)
- [🎨 需求场景](#-%E9%9C%80%E6%B1%82%E5%9C%BA%E6%99%AF)
- [👥 User Guide](#-user-guide)
- [1. 简单使用](#1-%E7%AE%80%E5%8D%95%E4%BD%BF%E7%94%A8)
- [2. 保证线程池中传递值](#2-%E4%BF%9D%E8%AF%81%E7%BA%BF%E7%A8%8B%E6%B1%A0%E4%B8%AD%E4%BC%A0%E9%80%92%E5%80%BC)
- [2.1 修饰`Runnable`和`Callable`](#21-%E4%BF%AE%E9%A5%B0runnable%E5%92%8Ccallable)
- [整个过程的完整时序图](#%E6%95%B4%E4%B8%AA%E8%BF%87%E7%A8%8B%E7%9A%84%E5%AE%8C%E6%95%B4%E6%97%B6%E5%BA%8F%E5%9B%BE)
- [2.2 修饰线程池](#22-%E4%BF%AE%E9%A5%B0%E7%BA%BF%E7%A8%8B%E6%B1%A0)
- [2.3 使用`Java Agent`来修饰`JDK`线程池实现类](#23-%E4%BD%BF%E7%94%A8java-agent%E6%9D%A5%E4%BF%AE%E9%A5%B0jdk%E7%BA%BF%E7%A8%8B%E6%B1%A0%E5%AE%9E%E7%8E%B0%E7%B1%BB)
- [`Java Agent`的启动参数配置](#java-agent%E7%9A%84%E5%90%AF%E5%8A%A8%E5%8F%82%E6%95%B0%E9%85%8D%E7%BD%AE)
- [🔌 Java API Docs](#-java-api-docs)
- [🍪 Maven依赖](#-maven%E4%BE%9D%E8%B5%96)
- [🔨 关于编译构建](#-%E5%85%B3%E4%BA%8E%E7%BC%96%E8%AF%91%E6%9E%84%E5%BB%BA)
- [❓ FAQ](#-faq)
- [✨ 使用`TTL`的好处与必要性](#-%E4%BD%BF%E7%94%A8ttl%E7%9A%84%E5%A5%BD%E5%A4%84%E4%B8%8E%E5%BF%85%E8%A6%81%E6%80%A7)
- [🗿 更多文档](#-%E6%9B%B4%E5%A4%9A%E6%96%87%E6%A1%A3)
- [📚 相关资料](#-%E7%9B%B8%E5%85%B3%E8%B5%84%E6%96%99)
- [JDK Core Classes](#jdk-core-classes)
- [💗 Who Used](#-who-used)
- [👷 Contributors](#-contributors)
----------------------------------------
# 🔧 功能
👉 `TransmittableThreadLocal`(`TTL`):在使用线程池等会池化复用线程的执行组件情况下,提供`ThreadLocal`值的传递功能,解决异步执行时上下文传递的问题。一个`Java`标准库本应为框架/中间件设施开发提供的标配能力,本库功能聚焦 & 0依赖,支持`Java 6~21`。
`JDK`的[`InheritableThreadLocal`](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/InheritableThreadLocal.html)类可以完成父线程到子线程的值传递。但对于使用线程池等会池化复用线程的执行组件的情况,线程由线程池创建好,并且线程是池化起来反复使用的;这时父子线程关系的`ThreadLocal`值传递已经没有意义,应用需要的实际上是把 **任务提交给线程池时**的`ThreadLocal`值传递到 **任务执行时**。
本库提供的[`TransmittableThreadLocal`](ttl-core/src/main/java/com/alibaba/ttl3/TransmittableThreadLocal.java)类继承并加强`InheritableThreadLocal`类,解决上述的问题,使用详见 [User Guide](#-user-guide)。
整个`TransmittableThreadLocal`库的核心功能(用户`API`、线程池`ExecutorService`/`ForkJoinPool`/`TimerTask`及其线程工厂的`Wrapper`;开发者`API`、框架/中间件的集成`API`),只有 **_~1000 `SLOC`代码行_**,非常精小。
欢迎 👏
- 建议和提问,[提交 Issue](https://github.com/alibaba/transmittable-thread-local/issues/new)
- 贡献和改进,[Fork 后提通过 Pull Request 贡献代码](https://github.com/alibaba/transmittable-thread-local/fork)
> [!NOTE]
> 从`TTL v2.13+`开始,升级到`Java 8`。🚀
> 如果需要`Java 6`的支持,使用版本`2.12.x` [](https://repo1.maven.org/maven2/com/alibaba/transmittable-thread-local/maven-metadata.xml)
# 🎨 需求场景
`ThreadLocal`的需求场景即`TransmittableThreadLocal`的潜在需求场景,如果你的业务需要『在使用线程池等会池化复用线程的执行组件情况下传递`ThreadLocal`值』则是`TransmittableThreadLocal`目标场景。
下面是几个典型场景例子。
1. 分布式跟踪系统 或 全链路压测(即链路打标)
2. 日志收集记录系统上下文
3. `Request`级`Cache`
4. 应用容器或上层框架跨应用代码给下层`SDK`传递信息
各个场景的展开说明参见子文档 [需求场景](docs/requirement-scenario.md)。
# 👥 User Guide
使用类[`TransmittableThreadLocal`](ttl-core/src/main/java/com/alibaba/ttl3/TransmittableThreadLocal.java)来保存值,并跨线程池传递。
`TransmittableThreadLocal`继承`InheritableThreadLocal`,使用方式也类似。相比`InheritableThreadLocal`,添加了`protected`的`transmitteeValue()`方法,用于定制 **任务提交给线程池时** 的`ThreadLocal`值传递到 **任务执行时** 的传递方式,缺省是简单的赋值传递。
注意:如果传递的对象(引用类型)会被修改,且没有做深拷贝(如直接传递引用或是浅拷贝),那么
- 因为跨线程传递而不再有线程封闭,传递对象在多个线程之间是有共享的。
- 与`JDK`的[`InheritableThreadLocal.childValue()`](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/InheritableThreadLocal.html#childValue(T))一样,需要使用者/业务逻辑注意保证传递对象的线程安全。
关于`transmitteeValue`方法 的 展开说明
关于构词后缀`er`与`ee`的说明:
`transmit`是动词传递,`transmitter`动作的执行者/主动方,而`transmittee`动作的接收者/被动方。
`er`与`ee`后缀的常见词是`employer`(雇主)/`employee`(雇员)、`caller`(调用者)/`callee`(被调用者)。
具体使用方式见下面的说明。
## 1. 简单使用
父线程给子线程传递值。
示例代码:
```java
TransmittableThreadLocal context = new TransmittableThreadLocal<>();
// =====================================================
// 在父线程中设置
context.set("value-set-in-parent");
// =====================================================
// 在子线程中可以读取,值是"value-set-in-parent"
String value = context.get();
```
\# 完整可运行的Demo代码参见[`SimpleDemo.kt`](ttl-core/src/test/java/com/alibaba/demo/ttl3/SimpleDemo.kt)。
这其实是`InheritableThreadLocal`的功能,应该使用`InheritableThreadLocal`来完成。
但对于使用线程池等会池化复用线程的执行组件的情况,线程由线程池创建好,并且线程是池化起来反复使用的;这时父子线程关系的`ThreadLocal`值传递已经没有意义,应用需要的实际上是把 **任务提交给线程池时**的`ThreadLocal`值传递到 **任务执行时**。
解决方法参见下面的这几种用法。
## 2. 保证线程池中传递值
### 2.1 修饰`Runnable`和`Callable`
使用[`TtlRunnable`](ttl-core/src/main/java/com/alibaba/ttl3/TtlRunnable.java)和[`TtlCallable`](ttl-core/src/main/java/com/alibaba/ttl3/TtlCallable.java)来修饰传入线程池的`Runnable`和`Callable`。
示例代码:
```java
TransmittableThreadLocal context = new TransmittableThreadLocal<>();
// =====================================================
// 在父线程中设置
context.set("value-set-in-parent");
Runnable task = new RunnableTask();
// 额外的处理,生成修饰了的对象ttlRunnable
Runnable ttlRunnable = TtlRunnable.get(task);
executorService.submit(ttlRunnable);
// =====================================================
// Task中可以读取,值是"value-set-in-parent"
String value = context.get();
```
**_注意_**:
即使是同一个`Runnable`任务多次提交到线程池时,每次提交时都需要通过修饰操作(即`TtlRunnable.get(task)`)以抓取这次提交时的`TransmittableThreadLocal`上下文的值;即如果同一个任务下一次提交时不执行修饰而仍然使用上一次的`TtlRunnable`,则提交的任务运行时会是之前修饰操作所抓取的上下文。示例代码如下:
```java
// 第一次提交
Runnable task = new RunnableTask();
executorService.submit(TtlRunnable.get(task));
// ...业务逻辑代码,
// 并且修改了 TransmittableThreadLocal上下文 ...
context.set("value-modified-in-parent");
// 再次提交
// 重新执行修饰,以传递修改了的 TransmittableThreadLocal上下文
executorService.submit(TtlRunnable.get(task));
```
上面演示了`Runnable`,`Callable`的处理类似
```java
TransmittableThreadLocal context = new TransmittableThreadLocal<>();
// =====================================================
// 在父线程中设置
context.set("value-set-in-parent");
Callable call = new CallableTask();
// 额外的处理,生成修饰了的对象ttlCallable
Callable ttlCallable = TtlCallable.get(call);
executorService.submit(ttlCallable);
// =====================================================
// Call中可以读取,值是"value-set-in-parent"
String value = context.get();
```
\# 完整可运行的Demo代码参见[`TtlWrapperDemo.kt`](ttl-core/src/test/java/com/alibaba/demo/ttl3/TtlWrapperDemo.kt)。
#### 整个过程的完整时序图
[](#dummy)
### 2.2 修饰线程池
省去每次`Runnable`和`Callable`传入线程池时的修饰,这个逻辑可以在线程池中完成。
通过工具类[`TtlExecutors`](ttl-core/src/main/java/com/alibaba/ttl3/executor/TtlExecutors.java)完成,有下面的方法:
- `getTtlExecutor`:修饰接口`Executor`
- `getTtlExecutorService`:修饰接口`ExecutorService`
- `getTtlScheduledExecutorService`:修饰接口`ScheduledExecutorService`
示例代码:
```java
ExecutorService executorService = ...
// 额外的处理,生成修饰了的对象executorService
executorService = TtlExecutors.getTtlExecutorService(executorService);
TransmittableThreadLocal context = new TransmittableThreadLocal<>();
// =====================================================
// 在父线程中设置
context.set("value-set-in-parent");
Runnable task = new RunnableTask();
Callable call = new CallableTask();
executorService.submit(task);
executorService.submit(call);
// =====================================================
// Task或是Call中可以读取,值是"value-set-in-parent"
String value = context.get();
```
\# 完整可运行的Demo代码参见[`TtlExecutorWrapperDemo.kt`](ttl-core/src/test/java/com/alibaba/demo/ttl3/TtlExecutorWrapperDemo.kt)。
### 2.3 使用`Java Agent`来修饰`JDK`线程池实现类
这种方式,实现线程池的传递是透明的,业务代码中没有修饰`Runnable`或是线程池的代码。即可以做到应用代码 **无侵入**。
\# 关于 **无侵入** 的更多说明参见文档[`Java Agent`方式对应用代码无侵入](docs/developer-guide.md#java-agent%E6%96%B9%E5%BC%8F%E5%AF%B9%E5%BA%94%E7%94%A8%E4%BB%A3%E7%A0%81%E6%97%A0%E4%BE%B5%E5%85%A5)。
示例代码:
```java
// ## 1. 框架上层逻辑,后续流程框架调用业务 ##
TransmittableThreadLocal context = new TransmittableThreadLocal<>();
context.set("value-set-in-parent");
// ## 2. 应用逻辑,后续流程业务调用框架下层逻辑 ##
ExecutorService executorService = Executors.newFixedThreadPool(3);
Runnable task = new RunnableTask();
Callable call = new CallableTask();
executorService.submit(task);
executorService.submit(call);
// ## 3. 框架下层逻辑 ##
// Task或是Call中可以读取,值是"value-set-in-parent"
String value = context.get();
```
Demo参见[`AgentDemo.kt`](ttl2-compatible/src/test/java/com/alibaba/demo/ttl/agent/AgentDemo.kt)。执行工程下的脚本[`scripts/run-agent-demo.sh`](scripts/run-agent-demo.sh)即可运行Demo。
目前`TTL Agent`中,修饰了的`JDK`执行器组件(即如线程池)如下:
1. `java.util.concurrent.ThreadPoolExecutor` 和 `java.util.concurrent.ScheduledThreadPoolExecutor`
- 修饰实现代码在[`JdkExecutorTtlTransformlet.java`](ttl-agent/src/main/java/com/alibaba/ttl3/agent/transformlet/internal/JdkExecutorTtlTransformlet.java)。
1. `java.util.concurrent.ForkJoinTask`(对应的执行器组件是`java.util.concurrent.ForkJoinPool`)
- 修饰实现代码在[`ForkJoinTtlTransformlet.java`](ttl-agent/src/main/java/com/alibaba/ttl3/agent/transformlet/internal/ForkJoinTtlTransformlet.java)。从版本 **_`2.5.1`_** 开始支持。
- **_注意_**:`Java 8`引入的[**_`CompletableFuture`_**](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/CompletableFuture.html)与(并行执行的)[**_`Stream`_**](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/stream/package-summary.html)底层是通过`ForkJoinPool`来执行,所以支持`ForkJoinPool`后,`TTL`也就透明支持了`CompletableFuture`与`Stream`。🎉
1. `java.util.TimerTask`的子类(对应的执行器组件是`java.util.Timer`)
- 修饰实现代码在[`TimerTaskTtlTransformlet.java`](ttl-agent/src/main/java/com/alibaba/ttl3/agent/transformlet/internal/TimerTaskTtlTransformlet.java)。从版本 **_`2.7.0`_** 开始支持。
- **_注意_**:从`2.11.2`版本开始缺省开启`TimerTask`的修饰(因为保证正确性是第一位,而不是最佳实践『不推荐使用`TimerTask`』:);`2.11.1`版本及其之前的版本没有缺省开启`TimerTask`的修饰。
- 使用`Agent`参数`ttl.agent.enable.timer.task`开启/关闭`TimerTask`的修饰:
- `-javaagent:path/to/transmittable-thread-local-2.x.y.jar=ttl.agent.enable.timer.task:true`
- `-javaagent:path/to/transmittable-thread-local-2.x.y.jar=ttl.agent.enable.timer.task:false`
- 更多关于`TTL Agent`参数的配置说明详见[`TtlAgent.java`的JavaDoc](ttl-agent/src/main/java/com/alibaba/ttl3/agent/TtlAgent.java)。
关于`java.util.TimerTask`/`java.util.Timer` 的 展开说明
`Timer`是`JDK 1.3`的老类,不推荐使用`Timer`类。
推荐用[`ScheduledExecutorService`](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/ScheduledExecutorService.html)。
`ScheduledThreadPoolExecutor`实现更强壮,并且功能更丰富。
如支持配置线程池的大小(`Timer`只有一个线程);`Timer`在`Runnable`中抛出异常会中止定时执行。更多说明参见 [10. **Mandatory** Run multiple TimeTask by using ScheduledExecutorService rather than Timer because Timer will kill all running threads in case of failing to catch exceptions. - Alibaba Java Coding Guidelines](https://alibaba.github.io/Alibaba-Java-Coding-Guidelines/#concurrency)。
#### `Java Agent`的启动参数配置
在`Java`的启动参数加上:`-javaagent:path/to/transmittable-thread-local-2.x.y.jar`。
**_注意_**:
- 如果修改了下载的`TTL`的`Jar`的文件名(`transmittable-thread-local-2.x.y.jar`),则需要自己手动通过`-Xbootclasspath JVM`参数来显式配置。
比如修改文件名成`ttl-foo-name-changed.jar`,则还需要加上`Java`的启动参数:`-Xbootclasspath/a:path/to/ttl-foo-name-changed.jar`。
- 或使用`v2.6.0`之前的版本(如`v2.5.1`),则也需要自己手动通过`-Xbootclasspath JVM`参数来显式配置(就像`TTL`之前的版本的做法一样)。
加上`Java`的启动参数:`-Xbootclasspath/a:path/to/transmittable-thread-local-2.5.1.jar`。
`Java`命令行示例如下:
```bash
java -javaagent:path/to/transmittable-thread-local-2.x.y.jar \
-cp classes \
com.alibaba.demo.ttl.agent.AgentDemo
# 如果修改了TTL jar文件名 或 TTL版本是 2.6.0 之前
# 则还需要显式设置 -Xbootclasspath 参数
java -javaagent:path/to/ttl-foo-name-changed.jar \
-Xbootclasspath/a:path/to/ttl-foo-name-changed.jar \
-cp classes \
com.alibaba.demo.ttl.agent.AgentDemo
java -javaagent:path/to/transmittable-thread-local-2.5.1.jar \
-Xbootclasspath/a:path/to/transmittable-thread-local-2.5.1.jar \
-cp classes \
com.alibaba.demo.ttl.agent.AgentDemo
```
关于`boot class path` 的 展开说明
因为修饰了`JDK`标准库的类,标准库由`bootstrap class loader`加载;修饰后的`JDK`类引用了`TTL`的代码,所以`Java Agent`使用方式下`TTL Jar`文件需要配置到`boot class path`上。
`TTL`从`v2.6.0`开始,加载`TTL Agent`时会自动设置`TTL Jar`到`boot class path`上。
***注意***:不能修改从`Maven`库下载的`TTL Jar`文件名(形如`transmittable-thread-local-2.x.y.jar`)。
如果修改了,则需要自己手动通过`-Xbootclasspath JVM`参数来显式配置(就像`TTL`之前的版本的做法一样)。
自动设置`TTL Jar`到`boot class path`的实现是通过指定`TTL Java Agent Jar`文件里`manifest`文件(`META-INF/MANIFEST.MF`)的`Boot-Class-Path`属性:
`Boot-Class-Path`
A list of paths to be searched by the bootstrap class loader. Paths represent directories or libraries (commonly referred to as JAR or zip libraries on many platforms).
These paths are searched by the bootstrap class loader after the platform specific mechanisms of locating a class have failed. Paths are searched in the order listed.
更多详见
[`Java Agent`规范 - `JavaDoc`](https://docs.oracle.com/en/java/javase/21/docs/api/java.instrument/java/lang/instrument/package-summary.html#package.description)
[JAR File Specification - JAR Manifest](https://docs.oracle.com/en/java/javase/21/docs/specs/jar/jar.html#jar-manifest)
[Working with Manifest Files - The Java™ Tutorials](https://docs.oracle.com/javase/tutorial/deployment/jar/manifestindex.html)
# 🔌 Java API Docs
当前版本的Java API文档地址:
# 🍪 Maven依赖
示例:
```xml
com.alibaba
transmittable-thread-local
2.14.4
```
可以在 [maven.org](https://repo1.maven.org/maven2/com/alibaba/transmittable-thread-local/maven-metadata.xml) 查看可用的版本。
# 🔨 关于编译构建
编译构建的环境要求: **_`JDK 8+`_**;用`Maven`常规的方式执行编译构建即可:
\# 在工程中已经包含了符合版本要求的`Maven`,直接运行 **_工程根目录下的`mvnw`_**;并不需要先手动自己安装好`Maven`。
```bash
# 运行测试Case
./mvnw test
# 编译打包
./mvnw package
# 运行测试Case、编译打包、安装TTL库到Maven本地
./mvnw install
#####################################################
# 如果使用你自己安装的 maven,版本要求:maven 3.3.9+
mvn install
```
# ❓ FAQ
**_Q1. `TTL Agent`与其它`Agent`(如`Skywalking`、`Promethues`)配合使用时不生效?_**
配置`TTL Agent`在最前的位置,可以避免与其它其它`Agent`配合使用时,`TTL Agent`可能的不生效问题。配置示例:
```bash
java -javaagent:path/to/transmittable-thread-local-2.x.y.jar \
-javaagent:path/to/skywalking-agent.jar \
-jar your-app.jar
```
原因是:
- 像`Skywalking`这样的`Agent`的入口逻辑(`premain`)包含了线程池的启动。
- 如果配置在这样的`Agent`配置在前面,到了`TTL Agent`(的`premain`)时,`TTL`需要加强的线程池类已经加载(`load`)了。
- `TTL Agent`的`TtlTransformer`是在类加载时触发类的增强;如果类已经加载了会跳过`TTL Agent`的增强逻辑。
更多讨论参见 [Issue:`TTL agent`与其他`Agent`的兼容性问题 #226](https://github.com/alibaba/transmittable-thread-local/issues/226)。
**_Q2. `MacOS`下,使用`Java Agent`,可能会报`JavaLaunchHelper`的出错信息_**
JDK Bug:
可以换一个版本的`JDK`。我的开发机上`1.7.0_40`有这个问题,`1.6.0_51`、`1.7.0_45`可以运行。
\# `1.7.0_45`还是有`JavaLaunchHelper`的出错信息,但不影响运行。
# ✨ 使用`TTL`的好处与必要性
> [!NOTE]
> 不读这一节,并不会影响你使用`TTL`来解决你碰到的问题,可以放心跳过;读了 [User Guide](#-user-guide) 就可以快速用起来了~ 😄 这一节信息密度较高不易读。
**_好处:透明且自动完成所有异步执行上下文的可定制、规范化的捕捉与传递。_**
这个好处也是`TransmittableThreadLocal`的目标。
**_必要性:随着应用的分布式微服务化并使用各种中间件,越来越多的功能与组件会涉及不同的上下文,逻辑流程也越来越长;上下文问题实际上是个大的易错的架构问题,需要统一的对业务透明的解决方案。_**
使用`ThreadLocal`作为业务上下文传递的经典技术手段在中间件、技术与业务框架中广泛大量使用。而对于生产应用,几乎一定会使用线程池等异步执行组件,以高效支撑线上大流量。但使用`ThreadLocal`及其`set/remove`的上下文传递模式,在使用线程池等异步执行组件时,存在多方面的问题:
**_1. 从业务使用者角度来看_**
1. **繁琐**
- 业务逻辑要知道:有哪些上下文;各个上下文是如何获取的。
- 并需要业务逻辑去一个一个地捕捉与传递。
1. **依赖**
- 需要直接依赖不同`ThreadLocal`上下文各自的获取的逻辑或类。
- 像`RPC`的上下文(如`Dubbo`的`RpcContext`)、全链路跟踪的上下文(如`SkyWalking`的`ContextManager`)、不同业务模块中的业务流程上下文,等等。
1. **静态(易漏)**
- 因为要 **_事先_** 知道有哪些上下文,如果系统出现了一个新的上下文,业务逻辑就要修改添加上新上下文传递的几行代码。也就是说因 **_系统的_** 上下文新增,**_业务的_** 逻辑就跟进要修改。
- 而对于业务来说,不关心系统的上下文,即往往就可能遗漏,会是线上故障了。
- 随着应用的分布式微服务化并使用各种中间件,越来越多的功能与组件会涉及不同的上下文,逻辑流程也越来越长;上下文问题实际上是个大的易错的架构问题,需要统一的对业务透明的解决方案。
1. **定制性**
- 因为需要业务逻辑来完成捕捉与传递,业务要关注『上下文的传递方式』:直接传引用?还是拷贝传值?拷贝是深拷贝还是浅拷贝?在不同的上下文会需要不同的做法。
- 『上下文的传递方式』往往是 **_上下文的提供者_**(或说是业务逻辑的框架部分)才能决策处理好的;而 **_上下文的使用者_**(或说是业务逻辑的应用部分)往往不(期望)知道上下文的传递方式。这也可以理解成是 **_依赖_**,即业务逻辑 依赖/关注/实现了 系统/架构的『上下文的传递方式』。
**_2. 从整体流程实现角度来看_**
关注的是 **上下文传递流程的规范化**。上下文传递到了子线程要做好 **_清理_**(或更准确地说是要 **_恢复_** 成之前的上下文),需要业务逻辑去处理好。如果业务逻辑对**清理**的处理不正确,比如:
- 如果清理操作漏了:
- 下一次执行可能是上次的,即『上下文的 **_污染_**/**_串号_**』,会导致业务逻辑错误。
- 『上下文的 **_泄漏_**』,会导致内存泄漏问题。
- 如果清理操作做多了,会出现上下文 **_丢失_**。
上面的问题,在业务开发中引发的`Bug`真是**屡见不鲜** !本质原因是:**_`ThreadLocal`的`set/remove`的上下文传递模式_** 在使用线程池等异步执行组件的情况下不再是有效的。常见的典型例子:
- 当线程池满了且线程池的`RejectedExecutionHandler`使用的是`CallerRunsPolicy`时,提交到线程池的任务会在提交线程中直接执行,`ThreadLocal.remove`操作**清理**提交线程的上下文导致上下文**丢失**。
- 类似的,使用`ForkJoinPool`(包含并行执行`Stream`与`CompletableFuture`,底层使用`ForkJoinPool`)的场景,展开的`ForkJoinTask`会在任务提交线程中直接执行。同样导致上下文**丢失**。
怎么设计一个『上下文传递流程』方案(即上下文的生命周期),以**保证**没有上面的问题?
期望:上下文生命周期的操作从业务逻辑中分离出来。业务逻辑不涉及生命周期,就不会有业务代码如疏忽清理而引发的问题了。整个上下文的传递流程或说生命周期可以规范化成:捕捉、回放和恢复这3个操作,即[**_`CRR(capture/replay/restore)`模式_**](docs/developer-guide.md#-%E6%A1%86%E6%9E%B6%E4%B8%AD%E9%97%B4%E4%BB%B6%E9%9B%86%E6%88%90ttl%E4%BC%A0%E9%80%92)。更多讨论参见 [Issue:能在详细讲解一下`replay`、`restore`的设计理念吗?#201](https://github.com/alibaba/transmittable-thread-local/issues/201)。
总结上面的说明:在生产应用(几乎一定会使用线程池等异步执行组件)中,使用`ThreadLocal`及其`set/remove`的上下文传递模式**几乎一定是有问题的**,**_只是在等一个出`Bug`的机会_**。
更多`TTL`好处与必要性的展开讨论参见 [Issue:这个库带来怎样的好处和优势? #128](https://github.com/alibaba/transmittable-thread-local/issues/128),欢迎继续讨论 ♥️
# 🗿 更多文档
- [🎨 需求场景说明](docs/requirement-scenario.md)
- [❤️ 小伙伴同学们写的`TTL`使用场景 与 设计实现解析的文章(写得都很好!) - Issue #123](https://github.com/alibaba/transmittable-thread-local/issues/123)
- [🎓 Developer Guide](docs/developer-guide.md)
- [☔ 性能测试](docs/performance-test.md)
# 📚 相关资料
## JDK Core Classes
- [WeakHashMap](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/WeakHashMap.html)
- [InheritableThreadLocal](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/InheritableThreadLocal.html)
# 💗 Who Used
使用了`TTL`的一部分开源项目:
- **中间件**
- [`sofastack/sofa-rpc` ](https://github.com/sofastack/sofa-rpc) [](https://gitee.com/sofastack/sofa-rpc)
SOFARPC is a high-performance, high-extensibility, production-level Java RPC framework
- [`trpc-group/trpc-java` ](https://github.com/trpc-group/trpc-java)
A pluggable, high-performance RPC framework written in java
- [`tencentmusic/supersonic` ](https://github.com/tencentmusic/supersonic)
SuperSonic is an out-of-the-box yet highly extensible framework for building ChatBI
- [`dromara/hmily` ](https://github.com/dromara/hmily) [](https://gitee.com/dromara/hmily)
Distributed transaction solutions
- [`dromara/gobrs-async` ](https://github.com/dromara/gobrs-async) [](https://gitee.com/dromara/gobrs-async)
一款功能强大、配置灵活、带有全链路异常回调、内存优化、异常状态管理于一身的高性能异步编排框架。为企业提供在复杂应用场景下动态任务编排的能力。 针对于复杂场景下,异步线程复杂性、任务依赖性、异常状态难控制性
- [`dromara/dynamic-tp` ](https://github.com/dromara/dynamic-tp) [](https://gitee.com/dromara/dynamic-tp)
轻量级动态线程池,内置监控告警功能,支持线程池上下文传递,基于主流配置中心(已支持Nacos、Apollo,Zookeeper,可通过SPI自定义实现)
- [`opengoofy/hippo4j` ](https://github.com/opengoofy/hippo4j) [](https://gitee.com/magestack/hippo4j)
动态线程池框架,附带监控报警功能,支持 JDK、Tomcat、Jetty、Undertow 线程池;Apache RocketMQ、Dubbo、RabbitMQ、Hystrix 消费等线程池。内置两种使用模式:轻量级依赖配置中心以及无中间件依赖版本
- [`siaorg/sia-gateway` ](https://github.com/siaorg/sia-gateway)
微服务路由网关(zuul-plus)
- [`huaweicloud/Sermant` ](https://github.com/huaweicloud/Sermant)
Sermant, a proxyless service mesh solution based on Javaagent
- [`ZTO-Express/zms` ](https://github.com/ZTO-Express/zms) [](https://gitee.com/zto_express/zms)
ZTO Message Service
- [`lxchinesszz/tomato` ](https://github.com/lxchinesszz/tomato)
一款专门为SpringBoot项目设计的幂等组件
- [`ytyht226/taskflow` ](https://github.com/ytyht226/taskflow)
一款轻量、简单易用、可灵活扩展的通用任务编排框架,基于有向无环图(DAG)的方式实现,框架提供了组件复用、同步/异步编排、条件判断、分支选择等能力,可以根据不同的业务场景对任意的业务流程进行编排
- [`foldright/cffu` ](https://github.com/foldright/cffu)
🦝 Java CompletableFuture Fu, aka. CF-Fu, pronounced "Shifu"; include best practice/traps guide and a tiny sidekick library to improve user experience and reduce misuse.
- [`tuya/connector` ](https://github.com/tuya/connector)
The connector framework maps cloud APIs to local APIs based on simple configurations and flexible extension mechanisms
- **中间件/数据处理**
- [`apache/shardingsphere` ](https://github.com/apache/shardingsphere) [](https://gitee.com/Sharding-Sphere/sharding-sphere)
Ecosystem to transform any database into a distributed database system, and enhance it with sharding, elastic scaling, encryption features & more
- [`apache/kylin` ](https://github.com/apache/kylin)
A unified and powerful OLAP platform for Hadoop and Cloud.
- [`mybatis-flex/mybatis-flex` ](https://github.com/mybatis-flex/mybatis-flex) [](https://gitee.com/mybatis-flex/mybatis-flex)
mybatis-flex is an elegant Mybatis Enhancement Framework
- [`basicai/xtreme1` ](https://github.com/basicai/xtreme1)
The Next GEN Platform for Multisensory Training Data. #3D annotation, lidar-camera annotation and image annotation tools are supported
- [`oceanbase/odc` ](https://github.com/oceanbase/odc)
An open-source, enterprise-grade database tool for collaborative development
- [`sagframe/sagacity-sqltoy` ](https://github.com/sagframe/sagacity-sqltoy)
Java真正智慧的ORM框架
- [`dromara/stream-query` ](https://github.com/dromara/stream-query) [](https://gitee.com/dromara/stream-query)
允许完全摆脱Mapper的mybatis-plus体验;可以使用类似“工具类”这样的静态函数进行数据库操作
- [`luo-zhan/Transformer` ](https://github.com/luo-zhan/Transformer)
Transformer可能是最简单,但最强大的字段转换插件,一个注解搞定任意转换,让开发变得更加丝滑
- [`SimonAlong/Neo` ](https://github.com/SimonAlong/Neo)
Orm框架:基于ActiveRecord思想开发的至简化且功能很全的Orm框架
- [`ppdaicorp/das` ](https://github.com/ppdaicorp/das)
数据库访问框架(data access service),包括数据库控制台das console,数据库客户端das client和数据库服务端das server三部分
- [`didi/ALITA` ](https://github.com/didi/ALITA)
a layer-based data analysis tool
- [`didi/daedalus` ](https://github.com/didi/daedalus)
实现快速创建数据构造流程,数据构造流程的可视化、线上化、持久化、标准化
- **中间件/流程引擎**
- [`dromara/liteflow` ](https://github.com/dromara/liteflow) [](https://gitee.com/dromara/liteFlow)
a lightweight and practical micro-process framework
- [`alibaba/bulbasaur` ](https://github.com/alibaba/bulbasaur)
A pluggable, scalable process engine
- **中间件/日志**
- [`dromara/TLog` ](https://github.com/dromara/TLog) [](https://gitee.com/dromara/TLog)
Lightweight distributed log label tracking framework
- [`fayechenlong/plumelog` ](https://github.com/fayechenlong/plumelog) [](https://gitee.com/plumeorg/plumelog)
一个java分布式日志组件,支持百亿级别
- [`minbox-projects/minbox-logging` ](https://github.com/minbox-projects/minbox-logging) [](https://gitee.com/minbox-projects/minbox-logging)
分布式零侵入式、链路式请求日志分析框架。提供Admin端点进行采集日志、分析日志、日志告警通知、服务性能分析等。通过Admin Ui可查看实时链路日志信息、在线业务服务列表
- [`minbox-projects/api-boot` ](https://github.com/minbox-projects/api-boot) [](https://gitee.com/minbox-projects/api-boot)
为接口服务而生的,基于“ SpringBoot”完成扩展和自动配置,内部封装了一系列的开箱即用Starters
- [`ofpay/logback-mdc-ttl` ](https://github.com/ofpay/logback-mdc-ttl)
logback扩展,集成transmittable-thread-local支持跨线程池的mdc跟踪
- [`oldratlee/log4j2-ttl-thread-context-map` ](https://github.com/oldratlee/log4j2-ttl-thread-context-map)
Log4j2 TTL ThreadContextMap, Log4j2 extension integrated TransmittableThreadLocal to MDC
- [`qqxx6661/log-record` ](https://github.com/qqxx6661/log-record)
业务日志记录框架,使用注解优雅记录日志,支持SpEL表达式,自定义上下文,自定义函数,实体类DIFF等特性。
- **中间件/字节码**
- [`ymm-tech/easy-byte-coder` ](https://github.com/ymm-tech/easy-byte-coder)
Easy-byte-coder is a non-invasive bytecode injection framework based on JVM
- **业务服务或平台应用**
- [`OpenBankProject/OBP-API` ](https://github.com/OpenBankProject/OBP-API)
An open source RESTful API platform for banks that supports Open Banking, XS2A and PSD2 through access to accounts, transactions, counterparties, payments, entitlements and metadata - plus a host of internal banking and management APIs
- [`gz-yami/mall4j` ](https://github.com/gz-yami/mall4j) [](https://gitee.com/gz-yami/mall4j)
电商商城 java电商商城系统 uniapp商城 多用户商城
- [`Joolun/JooLun-wx` ](https://github.com/Joolun/JooLun-wx) [](https://gitee.com/joolun/JooLun-wx)
JooLun微信商城
- [`HummerRisk/HummerRisk` ](https://github.com/HummerRisk/HummerRisk)
云原生安全平台,包括混合云安全治理和容器云安全检测
- [`XiaoMi/mone` ](https://github.com/XiaoMi/mone)
`Mone`以微服务为核心的一站式企业协同研发平台。支持公共云、专有云和混合云多种部署形态;提供从“项目创建->开发->部署->治理->应用观测”端到端的研发全流程服务;通过云原生新技术和研发新模式,打造“双敏”,敏捷研发和敏捷组织,保障小米-中国区高复杂业务、大规模团队的敏捷研发协同,实现多倍效能提升。
- [`yangzongzhuan/RuoYi-Cloud` ](https://github.com/yangzongzhuan/RuoYi-Cloud) [](https://gitee.com/y_project/RuoYi-Cloud)
基于Spring Boot、Spring Cloud & Alibaba的分布式微服务架构权限管理系统
- [`somowhere/albedo` ](https://github.com/somowhere/albedo) [](https://gitee.com/somowhere/albedo)
基于 Spring Boot 、Spring Security、Mybatis 的RBAC权限管理系统
- [`qwdigital/LinkWechat` ](https://github.com/qwdigital/LinkWechat) [](https://gitee.com/LinkWeChat/link-wechat)
基于企业微信的开源 SCRM 系统,采用主流的 Java 微服务架构,是企业私域流量管理与营销的综合解决方案,助力企业提高客户运营效率,强化营销能力,拓展盈利空间
- [`fushengqian/fuint` ](https://github.com/fushengqian/fuint) [](https://gitee.com/fuint/fuint-uniapp)
fuint会员营销系统是一套开源的实体店铺会员管理和营销系统
- [`hiparker/opsli-boot` ](https://github.com/hiparker/opsli-boot) [](https://gitee.com/hiparker/opsli-boot)
一款的低代码快速平台,零代码开发,致力于做更简洁的后台管理系统
- [`topiam/eiam` ](https://github.com/topiam/eiam) [](https://gitee.com/topiam/eiam)
EIAM(Employee Identity and Access Management Program)企业级开源IAM平台,实现用户全生命周期的管理、统一认证和单点登录、为数字身份安全赋能
- [`Newspiral/newspiral-business` ](https://github.com/Newspiral/newspiral-business)
联盟区块链底层平台
- **工具产品**
- [`ssssssss-team/spider-flow` ](https://github.com/ssssssss-team/spider-flow) [](https://gitee.com/ssssssss-team/spider-flow)
新一代爬虫平台,以图形化方式定义爬虫流程,不写代码即可完成爬虫
- [`nekolr/slime` ](https://github.com/nekolr/slime)
🍰 一个可视化的爬虫平台
- [`Jackson0714/PassJava-Platform` ](https://github.com/Jackson0714/PassJava-Platform)
一款面试刷题的 Spring Cloud 开源系统。零碎时间利用小程序查看常见面试题,夯实Java基础。 该项目可以教会你如何搭建SpringBoot项目,Spring Cloud项目。 采用流行的技术,如 SpringBoot、MyBatis、Redis、 MySql、 MongoDB、 RabbitMQ、Elasticsearch,采用Docker容器化部署
- [`martin-chips/DimpleBlog` ](https://github.com/martin-chips/DimpleBlog)
基于`SpringBoot2`搭建的个人博客系统
- [`zjcscut/octopus` ](https://github.com/zjcscut/octopus)
长链接压缩为短链接的服务
- [`xggz/mqr` ](https://github.com/xggz/mqr) [](https://gitee.com/mlyai/mqr)
茉莉QQ机器人(简称MQR),采用mirai的Android协议实现的QQ机器人服务,通过web控制机器人的启停和配置
- **测试解决方案或工具**
- [`alibaba/jvm-sandbox-repeater` ](https://github.com/alibaba/jvm-sandbox-repeater)
A Java server-side recording and playback solution based on JVM-Sandbox, 录制/回放通用解决方案
- [`vivo/MoonBox` ](https://github.com/vivo/MoonBox)
Moonbox(月光宝盒)是JVM-Sandbox生态下的,基于jvm-sandbox-repeater重新开发的,一款流量回放平台产品。相较于jvm-sandbox-repeater,Moonbox功能更加丰富、数据可靠性更高,同时便于快速线上部署和使用
- [`alibaba/testable-mock` ](https://github.com/alibaba/testable-mock)
换种思路写Mock,让单元测试更简单
- [`shulieTech/Takin` ](https://github.com/shulieTech/Takin)
全链路压测平台,measure online environmental performance test for full-links, Especially for microservices
- [`shulieTech/LinkAgent` ](https://github.com/shulieTech/LinkAgent)
a Java-based open-source agent designed to collect data and control Functions for Java applications through JVM bytecode, without modifying applications codes
- [`alibaba/virtual-environment` ](https://github.com/alibaba/virtual-environment)
Route isolation with service sharing, 阿里测试环境服务隔离和联调机制的`Kubernetes`版实现
- **`Spring Cloud`/`Spring Boot`的框架方案/脚手架**
- [`YunaiV/ruoyi-vue-pro` ](https://github.com/YunaiV/ruoyi-vue-pro) [](https://gitee.com/zhijiantianya/ruoyi-vue-pro)
一套全部开源的企业级的快速开发平台。基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 微信小程序,支持 RBAC 动态权限、数据权限、SaaS 多租户、Activiti + Flowable 工作流、三方登录、支付、短信、商城等功能
- [`YunaiV/yudao-cloud` ](https://github.com/YunaiV/yudao-cloud) [](https://gitee.com/zhijiantianya/yudao-cloud)
RuoYi-Vue 全新 Cloud 版本,优化重构所有功能。基于 Spring Cloud Alibaba + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
- [`zlt2000/microservices-platform` ](https://github.com/zlt2000/microservices-platform) [](https://gitee.com/zlt2000/microservices-platform)
基于SpringBoot2.x、SpringCloud和SpringCloudAlibaba并采用前后端分离的企业级微服务多租户系统架构
- [`dromara/lamp-cloud` ](https://github.com/zuihou/lamp-cloud) [](https://gitee.com/dromara/lamp-cloud)
基于Jdk11 + SpringCloud + SpringBoot 的微服务快速开发平台,其中的可配置的SaaS功能尤其闪耀, 具备RBAC功能、网关统一鉴权、Xss防跨站攻击、自动代码生成、多种存储系统、分布式事务、分布式定时任务等多个模块,支持多业务系统并行开发, 支持多服务并行开发,可以作为后端服务的开发脚手架
- [`zuihou/lamp-util` ](https://github.com/zuihou/lamp-util) [](https://gitee.com/zuihou111/lamp-util)
打造一套兼顾 SpringBoot 和 SpringCloud 项目的公共工具类
- [`matevip/matecloud` ](https://github.com/matevip/matecloud) [](https://gitee.com/matevip/matecloud)
一款基于Spring Cloud Alibaba的微服务架构
- [`gavenwangcn/vole` ](https://github.com/gavenwangcn/vole)
SpringCloud 微服务业务脚手架
- [`liuweijw/fw-cloud-framework` ](https://github.com/liuweijw/fw-cloud-framework) [](https://gitee.com/liuweijw/fw-cloud-framework)
基于springcloud全家桶开发分布式框架(支持oauth2认证授权、SSO登录、统一下单、微信公众号服务、Shardingdbc分库分表、常见服务监控、链路监控、异步日志、redis缓存等功能),实现基于Vue全家桶等前后端分离项目工程
- [`liuht777/Taroco` ](https://github.com/liuht777/Taroco)
整合Nacos、Spring Cloud Alibaba,提供了一系列starter组件, 同时提供服务治理、服务监控、OAuth2权限认证,支持服务降级/熔断、服务权重
- [`mingyang66/spring-parent` ](https://github.com/mingyang66/spring-parent)
数据库多数据源、Redis多数据源、日志组件、全链路日志追踪、埋点扩展点、Netty、微服务、开发基础框架支持、异常统一处理、返回值、跨域、API路由、监控等
- [`budwk/budwk` ](https://github.com/budwk/budwk) [](https://gitee.com/budwk/budwk)
`BudWk` 原名 [`NutzWk` ](https://github.com/Wizzercn/NutzWk) [](https://gitee.com/wizzer/NutzWk),基于国产框架 nutz 及 nutzboot 开发的开源Web基础项目,集权限体系、系统参数、数据字典、站内消息、定时任务、CMS、微信等最常用功能,不庞杂、不面面俱到,使其具有上手容易、开发便捷、扩展灵活等特性,特别适合各类大中小型定制化项目需求
- [`yinjihuan/spring-cloud` ](https://github.com/yinjihuan/spring-cloud)
《Spring Cloud微服务-全栈技术与案例解析》和《Spring Cloud微服务 入门 实战与进阶》配套源码
- [`louyanfeng25/ddd-demo` ](https://github.com/louyanfeng25/ddd-demo)
《深入浅出DDD》讲解的演示项目,为了能够更好的理解Demo中的分层与逻辑处理,我强烈建议你配合小册来深入了解DDD
- [`nageoffer/12306` ](https://github.com/nageoffer/12306)
12306 铁路购票服务是与大家生活和出行相关的关键系统,包括会员、购票、订单、支付和网关等服务。
更多使用`TTL`的开源项目 参见 [](https://github.com/alibaba/transmittable-thread-local/network/dependents)
# 👷 Contributors
- Jerry Lee \ [@oldratlee](https://github.com/oldratlee)
- Yang Fang \ [@driventokill](https://github.com/driventokill)
- Zava Xu \ [@zavakid](https://github.com/zavakid)
- wuwen \ [@wuwen5](https://github.com/wuwen5)
- rybalkinsd \ [@rybalkinsd](https://github.com/rybalkinsd)
- David Dai \<351450944 at qq dot com> [@LNAmp](https://github.com/LNAmp)
- Your name here :-)
[](https://github.com/alibaba/transmittable-thread-local/graphs/contributors)
---
## File: docs/developer-guide.md
# 🎓 Developer Guide
---------------------------
- [📌 框架/中间件集成`TTL`传递](#-%E6%A1%86%E6%9E%B6%E4%B8%AD%E9%97%B4%E4%BB%B6%E9%9B%86%E6%88%90ttl%E4%BC%A0%E9%80%92)
- [📟 关于`Java Agent`](#-%E5%85%B3%E4%BA%8Ejava-agent)
- [`Java Agent`方式对应用代码无侵入](#java-agent%E6%96%B9%E5%BC%8F%E5%AF%B9%E5%BA%94%E7%94%A8%E4%BB%A3%E7%A0%81%E6%97%A0%E4%BE%B5%E5%85%A5)
- [已有`Java Agent`中嵌入`TTL Agent`](#%E5%B7%B2%E6%9C%89java-agent%E4%B8%AD%E5%B5%8C%E5%85%A5ttl-agent)
- [👢 `Bootstrap ClassPath`上添加通用库`Jar`的问题及其解决方法](#-bootstrap-classpath%E4%B8%8A%E6%B7%BB%E5%8A%A0%E9%80%9A%E7%94%A8%E5%BA%93jar%E7%9A%84%E9%97%AE%E9%A2%98%E5%8F%8A%E5%85%B6%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95)
- [🔨 如何编译构建](#-%E5%A6%82%E4%BD%95%E7%BC%96%E8%AF%91%E6%9E%84%E5%BB%BA)
- [发布操作列表](#%E5%8F%91%E5%B8%83%E6%93%8D%E4%BD%9C%E5%88%97%E8%A1%A8)
- [📚 相关资料](#-%E7%9B%B8%E5%85%B3%E8%B5%84%E6%96%99)
- [`JDK` core classes](#jdk-core-classes)
- [`Java Agent`](#java-agent)
- [`Javassist`](#javassist)
- [`Maven Shade`插件](#maven-shade%E6%8F%92%E4%BB%B6)
---------------------------
# 📌 框架/中间件集成`TTL`传递
框架/中间件集成`TTL`传递,通过[`TransmittableThreadLocal.Transmitter`](../ttl-core/src/main/java/com/alibaba/ttl3/transmitter/Transmitter.java)
抓取当前线程的所有`TTL`值并在其他线程进行回放;在回放线程执行完业务操作后,恢复为回放线程原来的`TTL`值。
`TransmittableThreadLocal.Transmitter`提供了所有`TTL`值的抓取、回放和恢复方法(即`CRR`操作):
1. `capture`方法:抓取线程(线程A)的所有`TTL`值。
2. `replay`方法:在另一个线程(线程B)中,回放在`capture`方法中抓取的`TTL`值,并返回 回放前`TTL`值的备份
3. `restore`方法:恢复线程B执行`replay`方法之前的`TTL`值(即备份)
示例代码:
```java
// ===========================================================================
// 线程 A
// ===========================================================================
TransmittableThreadLocal context = new TransmittableThreadLocal<>();
context.set("value-set-in-parent");
// (1) 抓取当前线程的所有TTL值
final Object captured = TransmittableThreadLocal.Transmitter.capture();
// ===========================================================================
// 线程 B(异步线程)
// ===========================================================================
// (2) 在线程 B中回放在capture方法中抓取的TTL值,并返回 回放前TTL值的备份
final Object backup = TransmittableThreadLocal.Transmitter.replay(captured);
try {
// 你的业务逻辑,这里你可以获取到外面设置的TTL值
String value = context.get();
System.out.println("Hello: " + value);
...
String result = "World: " + value;
} finally {
// (3) 恢复线程 B执行replay方法之前的TTL值(即备份)
TransmittableThreadLocal.Transmitter.restore(backup);
}
```
更多`TTL`传递的代码实现示例,参见 [`TtlRunnable.java`](../ttl-core/src/main/java/com/alibaba/ttl3/TtlRunnable.java)、[`TtlCallable.java`](../ttl-core/src/main/java/com/alibaba/ttl3/TtlCallable.java)。
当然可以使用`TransmittableThreadLocal.Transmitter`的工具方法`runSupplierWithCaptured`和`runCallableWithCaptured`和可爱的`Java 8 Lambda`语法
来简化`replay`和`restore`操作,示例代码:
```java
// ===========================================================================
// 线程 A
// ===========================================================================
TransmittableThreadLocal context = new TransmittableThreadLocal<>();
context.set("value-set-in-parent");
// (1) 抓取当前线程的所有TTL值
final Object captured = TransmittableThreadLocal.Transmitter.capture();
// ===========================================================================
// 线程 B(异步线程)
// ===========================================================================
String result = runSupplierWithCaptured(captured, () -> {
// 你的业务逻辑,这里你可以获取到外面设置的TTL值
String value = context.get();
System.out.println("Hello: " + value);
...
return "World: " + value;
}); // (2) + (3)
```
- 更多`TTL`传递的说明,详见[`TransmittableThreadLocal.Transmitter`的`JavaDoc`](../ttl-core/src/main/java/com/alibaba/ttl3/transmitter/Transmitter.java)。
- 更多`TTL`传递的代码实现,参见[`TtlRunnable.java`](../ttl-core/src/main/java/com/alibaba/ttl3/TtlRunnable.java)、[`TtlCallable.java`](../ttl-core/src/main/java/com/alibaba/ttl3/TtlCallable.java)。
# 📟 关于`Java Agent`
## `Java Agent`方式对应用代码无侵入
[User Guide - 2.3 使用`Java Agent`来修饰`JDK`线程池实现类](../README.md#23-%E4%BD%BF%E7%94%A8java-agent%E6%9D%A5%E4%BF%AE%E9%A5%B0jdk%E7%BA%BF%E7%A8%8B%E6%B1%A0%E5%AE%9E%E7%8E%B0%E7%B1%BB) 说到了,相对修饰`Runnable`或是线程池的方式,`Java Agent`方式是对应用代码无侵入的。下面做一些展开说明。
按框架图,把前面示例代码操作可以分成下面几部分:
1. 读取信息设置到`TTL`。
这部分在容器中完成,无需应用参与。
2. 提交`Runnable`到线程池。要有修饰操作`Runnable`(无论是直接修饰`Runnable`还是修饰线程池)。
这部分操作一定是在用户应用中触发。
3. 读取`TTL`,做业务检查。
在`SDK`中完成,无需应用参与。
只有第2部分的操作和应用代码相关。
如果不通过`Java Agent`修饰线程池,则修饰操作需要应用代码来完成。
使用`Java Agent`方式,应用无需修改代码,即做到 相对应用代码 透明地完成跨线程池的上下文传递。
更多关于应用场景的了解说明参见文档[需求场景](requirement-scenario.md)。
## 已有`Java Agent`中嵌入`TTL Agent`
这样可以减少`Java`启动命令行上的`Agent`的配置。
在自己的`Agent`中加上`TTL Agent`的逻辑,示例代码如下([`YourXxxAgent.java`](../ttl2-compatible/src/test/java/com/alibaba/demo/ttl/agent/YourXxxAgent.java)):
```java
import com.alibaba.ttl.threadpool.agent.TtlAgent;
import com.alibaba.ttl.threadpool.agent.TtlTransformer;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.util.logging.Logger;
public final class YourXxxAgent {
private static final Logger logger = Logger.getLogger(YourXxxAgent.class.getName());
public static void premain(String agentArgs, Instrumentation inst) {
TtlAgent.premain(agentArgs, inst); // add TTL Transformer
// add your Transformer
...
}
}
```
关于`Java Agent`和`ClassFileTransformer`的如何实现可以参考:[`TtlAgent.java`](../ttl2-compatible/src/main/java/com/alibaba/ttl/threadpool/agent/TtlAgent.java)、[`TtlTransformer.java`](../ttl2-compatible/src/main/java/com/alibaba/ttl/threadpool/agent/TtlTransformer.java)。
注意,在`bootclasspath`上,还是要加上`TTL Jar`:
```bash
-Xbootclasspath/a:/path/to/transmittable-thread-local-2.x.y.jar:/path/to/your/agent/jar/files
```
# 👢 `Bootstrap ClassPath`上添加通用库`Jar`的问题及其解决方法
`TTL Agent`的使用方式,需要将`TTL Jar`加到`Bootstrap ClassPath`上(通过`Java`命令行参数`-Xbootclasspath`);这样`TTL`的类与`JDK`的标准库的类(如`java.lang.String`)的`ClassLoader`是一样的,都在`Bootstrap ClassPath`上。
`Bootstrap ClassPath`上的类会优先于应用`ClassPath`的`Jar`被加载,并且加载`ClassLoader`不能被改。
\# 当然技术上严格地说,通过`Bootstrap ClassPath`上的类(如标准库的类)是可以改`ClassLoader`的,但这样做一般只会带来各种麻烦的问题。关于`ClassLoader`及其使用注意的介绍说明 可以参见[ClassLoader委托关系的完备配置](https://github.com/oldratlee/land#1-classloader%E5%A7%94%E6%89%98%E5%85%B3%E7%B3%BB%E7%9A%84%E5%AE%8C%E5%A4%87%E9%85%8D%E7%BD%AE)。
`TTL Agent`自己内部实现使用了`Javassist`,即在`Bootstrap ClassPath`上也需要添加`Javassist`。如果应用中也使用了`Javassist`,由于运行时会优先使用`TTL Agent`配置`Bootstrap ClassPath`上的`Javassist`,应用逻辑运行时实际不能选择/指定应用自己的`Javassist`的版本,带来了 应用需要的`Javassist`与`TTL Agent`用的`Javassist`之间的兼容性风险。
可以通过 `repackage`依赖(即 重命名/改写 依赖类的包名)来解决这个问题。`Maven`提供了[`Shade`插件](https://maven.apache.org/plugins/maven-shade-plugin/),可以完成下面的操作:
- `repackage` `Javassist`的类文件
- 添加`repackage`过的`Javassist`到`TTL Jar`中
这样操作后,`TTL Agent`不需要依赖外部的`Javassist`依赖,效果上这样的`shade`过的`TTL Jar`是自包含的、在使用上是编译/运行时0依赖的,自然也规避了依赖冲突的问题。
# 🔨 如何编译构建
编译构建的环境要求: **_`JDK 8+`_**;用`Maven`常规的方式执行编译构建即可:
\# 在工程中已经包含了符合版本要求的`Maven`,直接运行 **_工程根目录下的`mvnw`_**;并不需要先手动自己安装好`Maven`。
```bash
# 运行测试Case
./mvnw test
# 编译打包
./mvnw package
# 运行测试Case、编译打包、安装TTL库到Maven本地
./mvnw install
#####################################################
# 如果使用你自己安装的 maven,版本要求:maven 3.3.9+
mvn install
```
# 发布操作列表
详见独立文档 [发布操作列表](release-action-list.md)。
# 📚 相关资料
## `JDK` core classes
- [WeakHashMap](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/WeakHashMap.html)
- [InheritableThreadLocal](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/InheritableThreadLocal.html)
## `Java Agent`
- 官方文档
- [`Java Agent`规范 - `JavaDoc`](https://docs.oracle.com/en/java/javase/21/docs/api/java.instrument/java/lang/instrument/package-summary.html#package.description)
- [JAR File Specification - JAR Manifest](https://docs.oracle.com/en/java/javase/21/docs/specs/jar/jar.html#jar-manifest)
- [Working with Manifest Files - The Java™ Tutorials](https://docs.oracle.com/javase/tutorial/deployment/jar/manifestindex.html)
- [Java SE 6 新特性: Instrumentation 新功能](https://www.ibm.com/developerworks/cn/java/j-lo-jse61/)
- [Creation, dynamic loading and instrumentation with javaagents](https://dhruba.name/2010/02/07/creation-dynamic-loading-and-instrumentation-with-javaagents/)
- [JavaAgent加载机制分析](https://www.iteye.com/blog/nijiaben-1847212/)
## `Javassist`
- [Getting Started with Javassist](https://www.javassist.org/tutorial/tutorial.html)
## `Maven Shade`插件
- [`Maven Shade`插件文档](https://maven.apache.org/plugins/maven-shade-plugin/)
---
## File: docs/developer-guide-en.md
# 🎓 Developer Guide
---------------------------
- [📌 Framework/Middleware integration to `TTL` transmittance](#-frameworkmiddleware-integration-to-ttl-transmittance)
- [📚 Related material](#-related-material)
- [`JDK` core classes](#jdk-core-classes)
- [`Java` Agent](#java-agent)
- [`Javassist`](#javassist)
- [`Maven Shade plugin`](#maven-shade-plugin)
---------------------------
# 📌 Framework/Middleware integration to `TTL` transmittance
[`TransmittableThreadLocal.Transmitter`](../ttl-core/src/main/java/com/alibaba/ttl3/transmitter/Transmitter.java) to capture all `TTL` values of current thread and replay them in another thread.
There are following methods:
1. `capture`: capture all `TTL` values in current thread
2. `replay`: replay the captured `TTL` values in the current thread, and return the backup `TTL` values before replay
3. `restore`: restore `TTL` values before replay
Sample code:
```java
// ===========================================================================
// Thread A
// ===========================================================================
TransmittableThreadLocal context = new TransmittableThreadLocal();
context.set("value-set-in-parent");
// 1. capture all TTL values in current thread
final Object captured = TransmittableThreadLocal.Transmitter.capture();
// ===========================================================================
// Thread B
// ===========================================================================
// 2. replay the captured TTL values in current thread, and return the backup TTL values before replay
final Object backup = TransmittableThreadLocal.Transmitter.replay(captured);
try {
// Your biz code, you can get the TTL value from here
String value = context.get();
...
} finally {
// 3. restore TTL values before replay
TransmittableThreadLocal.Transmitter.restore(backup);
}
```
- For more info about `TransmittableThreadLocal.Transmitter`, see [its Javadoc](../ttl-core/src/main/java/com/alibaba/ttl3/transmitter/Transmitter.java).
- For more actual implementation code of `TTL` transmittance, see [`TtlRunnable.java`](../ttl-core/src/main/java/com/alibaba/ttl3/TtlRunnable.java) and [`TtlCallable.java`](../ttl-core/src/main/java/com/alibaba/ttl3/TtlCallable.java).
# 📚 Related material
## `JDK` core classes
- [WeakHashMap](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/WeakHashMap.html)
- [InheritableThreadLocal](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/InheritableThreadLocal.html)
## `Java` Agent
- [Java Agent Specification](https://docs.oracle.com/en/java/javase/21/docs/api/java.instrument/java/lang/instrument/package-summary.html)
## `Javassist`
- [Getting Started with Javassist](https://www.javassist.org/tutorial/tutorial.html)
## `Maven Shade plugin`
- [`Maven Shade plugin` doc](https://maven.apache.org/plugins/maven-shade-plugin/)
---
## File: docs/release-action-list.md
发布操作列表
===============================
1. 准备发布分支
1. 如`POM`中有降开发版本,注意 修改 新加`API`的 **_`@since`_** !!
2. 从`master`分支新建发布分支
3. 在发布分支上,更新版本号及相关信息
- 更新`POM`的版本号成要发布的版本号,去掉`SNAPSHOT`
- 更新`README`
- 更新badge的引用,由master分支名改成Tag名
- `sed 's/master/v2.x.y/g' -i README*`
- `javadoc` badge的JavaDoc链接到固定版本
https://alibaba.github.io/transmittable-thread-local/apidocs/2.x.y/index.html
- 示例`Maven`依赖的版本
- 更新`JavaDoc`链接到固定版本
2. 新建并Push Tag,如`v2.x.y`
- `git tag -m 'release v2.x.y' v2.x.y`
- `git push origin v2.x.y`
3. 等待Tag的CI通过 https://github.com/alibaba/transmittable-thread-local/actions
4. 执行[`scripts/check-japi-compliance.sh`](../scripts/check-japi-compliance.sh),检查`API`兼容性
5. 发布版本到`Maven`中央库
`./mvnw clean && ./mvnw deploy -DperformRelease`
6. 更新`JavaDoc`
1. 生成`JavaDoc`,更新到分支`gh-pages`
- `git checkout gh-pages`
- `mv target/apidocs apidocs/2.x.y`
2. 修改`index.html`的重定向到最新版本的`JavaDoc`
7. 编写Release Note:
8. 升级`Master`分支的开发版本号
- 更新 `README`中的示例`Maven`依赖版本
---
## File: docs/requirement-scenario.md
# 🎨 需求场景
在`ThreadLocal`的需求场景即是`TTL`的潜在需求场景,如果你的业务需要『在使用线程池等会池化复用线程的组件情况下传递`ThreadLocal`』则是`TTL`目标场景。
下面是几个典型场景例子。
-------------------------------
- [🔎 1. 分布式跟踪系统](#-1-%E5%88%86%E5%B8%83%E5%BC%8F%E8%B7%9F%E8%B8%AA%E7%B3%BB%E7%BB%9F)
- [🌵 2. 日志收集记录系统上下文](#-2-%E6%97%A5%E5%BF%97%E6%94%B6%E9%9B%86%E8%AE%B0%E5%BD%95%E7%B3%BB%E7%BB%9F%E4%B8%8A%E4%B8%8B%E6%96%87)
- [`Log4j2 MDC`的`TTL`集成](#log4j2-mdc%E7%9A%84ttl%E9%9B%86%E6%88%90)
- [`Logback MDC`的`TTL`集成](#logback-mdc%E7%9A%84ttl%E9%9B%86%E6%88%90)
- [👜 3. `Request`级`Cache`](#-3-request%E7%BA%A7cache)
- [🛁 4. 应用容器或上层框架跨应用代码给下层`SDK`传递信息](#-4-%E5%BA%94%E7%94%A8%E5%AE%B9%E5%99%A8%E6%88%96%E4%B8%8A%E5%B1%82%E6%A1%86%E6%9E%B6%E8%B7%A8%E5%BA%94%E7%94%A8%E4%BB%A3%E7%A0%81%E7%BB%99%E4%B8%8B%E5%B1%82sdk%E4%BC%A0%E9%80%92%E4%BF%A1%E6%81%AF)
- [上面场景使用`TTL`的整体构架](#%E4%B8%8A%E9%9D%A2%E5%9C%BA%E6%99%AF%E4%BD%BF%E7%94%A8ttl%E7%9A%84%E6%95%B4%E4%BD%93%E6%9E%84%E6%9E%B6)
-------------------------------
## 🔎 1. 分布式跟踪系统 或 全链路压测(即链路打标)
关于『分布式跟踪系统』可以了解一下`Google`的`Dapper`(介绍的论文:[中文](https://bigbully.github.io/Dapper-translation/)| [英文](https://research.google.com/pubs/pub36356.html))。分布式跟踪系统作为基础设施,不会限制『使用线程池等会池化复用线程的组件』,并期望对业务逻辑尽可能的透明。
分布式跟踪系统的实现的示意Demo参见[`DistributedTracerUseDemo.kt`](../ttl2-compatible/src/test/java/com/alibaba/demo/distributed_tracer/refcount/DistributedTracerUseDemo.kt)
从技术能力上讲,全链路压测 与 分布式跟踪系统 是一样的,即链路打标。
PS: 多谢 [@wyzssw](https://github.com/https://github.com/wyzssw) 对分布式追踪系统场景说明交流和实现上讨论建议:
- [Issue: 分布式追踪系统场景下,如何使用TTL](https://github.com/alibaba/transmittable-thread-local/issues/53)
## 🌵 2. 日志收集记录系统上下文
由于不限制用户应用使用线程池,系统的上下文需要能跨线程的传递,且不影响应用代码。
### `Log4j2 MDC`的`TTL`集成
`Log4j2`通过[`Thread Context`](https://logging.apache.org/log4j/2.x/manual/thread-context.html)提供了`Mapped Diagnostic Context`(`MDC`,诊断上下文)的功能,通过[`ThreadLocal`](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/ThreadLocal.html)/[`InheritableThreadLocal`](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/InheritableThreadLocal.html)实现上下文传递。
在[`Thread Context文档`](https://logging.apache.org/log4j/2.x/manual/thread-context.html)中提到了在使用线程池等会池化复用线程的组件(如`Executors`)时有问题,需要提供一个机制方案:
> The Stack and the Map are managed per thread and are based on ThreadLocal by default. The Map can be configured to use an InheritableThreadLocal by setting system property isThreadContextMapInheritable to "true". When configured this way, the contents of the Map will be passed to child threads. However, as discussed in the [Executors](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/Executors.html#privilegedThreadFactory%28%29) class and in other cases where thread pooling is utilized, the ThreadContext may not always be automatically passed to worker threads. In those cases the pooling mechanism should provide a means for doing so. The getContext() and cloneStack() methods can be used to obtain copies of the Map and Stack respectively.
即是`TTL`要解决的问题,提供`Log4j2 MDC`的`TTL`集成,详见工程[`log4j2-ttl-thread-context-map`](https://github.com/oldratlee/log4j2-ttl-thread-context-map)。对应依赖:
```xml
com.alibaba
log4j2-ttl-thread-context-map
1.3.0
```
可以在 [maven.org](https://repo1.maven.org/maven2/com/alibaba/log4j2-ttl-thread-context-map/maven-metadata.xml) 查看可用的版本。
PS: 多谢 @bwzhang2011 和 @wuwen5 对日志场景说明交流和实现上讨论建议:
- [Issue: 能否提供与LOG4J(2)中的MDC集成或增强](https://github.com/alibaba/transmittable-thread-local/issues/49) [@bwzhang2011](https://github.com/bwzhang2011)
- [Issue: slf4j MDCAdapter with multi-thread-context 支持](https://github.com/alibaba/transmittable-thread-local/issues/51) [@bwzhang2011](https://github.com/bwzhang2011)
### `Logback MDC`的`TTL`集成
`Logback`的集成参见[@ofpay](https://github.com/ofpay)提供的[`logback-mdc-ttl`](https://github.com/ofpay/logback-mdc-ttl)。对应依赖:
```xml
com.ofpay
logback-mdc-ttl
1.0.2
```
可以在 [maven.org](https://repo1.maven.org/maven2/com/ofpay/logback-mdc-ttl/maven-metadata.xml) 查看可用的版本。
这个集成已经在 **_线上产品环境_** 使用的。说明详见[欧飞网的使用场景](https://github.com/alibaba/transmittable-thread-local/issues/73#issuecomment-300665308)。
## 👜 3. `Request`级`Cache`
对于计算逻辑复杂业务流程,基础数据读取服务(这样的读取服务往往是个外部远程服务)可能需要多次调用,期望能缓存起来,以避免多次重复执行高成本操作。
同时,在入口发起不同的请求,处理的是不同用户的数据,所以不同发起请求之间不需要共享数据,这样也能避免请求对应的不同用户之间可能的数据污染。
因为涉及多个上下游线程,其实是`Session`级缓存。
通过`Request`级缓存可以
- 避免重复执行高成本操作,提升性能。
- 避免不同`Request`之间的数据污染。
更多讨论与使用方式参见[**_`@olove`_**](https://github.com/olove) 提的Issue:[讨论:Session级Cache场景下,TransmittableThreadLocal的使用](https://github.com/alibaba/transmittable-thread-local/issues/122)。
## 🛁 4. 应用容器或上层框架跨应用代码给下层`SDK`传递信息
举个具体的业务场景,在`App Engine`(`PAAS`)上会运行由应用提供商提供的应用(`SAAS`模式)。多个`SAAS`用户购买并使用这个应用(即`SAAS`应用)。`SAAS`应用往往是一个实例为多个`SAAS`用户提供服务。
\# 另一种模式是:`SAAS`用户使用完全独立一个`SAAS`应用,包含独立应用实例及其后的数据源(如`DB`、缓存,etc)。
需要避免的`SAAS`应用拿到多个`SAAS`用户的数据。一个解决方法是处理过程关联好一个`SAAS`用户的上下文,在上下文中应用只能处理(读/写)这个`SAAS`用户的数据。请求由`SAAS`用户发起(如从`Web`请求进入`App Engine`),`App Engine`可以知道是从哪个`SAAS`用户,在`Web`请求时在上下文中设置好`SAAS`用户`ID`。应用处理数据(`DB`、`Web`、消息 etc.)是通过`App Engine`提供的服务`SDK`来完成。当应用处理数据时,`SDK`检查数据所属的`SAAS`用户是否和上下文中的`SAAS`用户`ID`一致,如果不一致则拒绝数据的读写。
应用代码会使用线程池,并且这样的使用是正常的业务需求。`SAAS`用户`ID`的从要`App Engine`传递到下层`SDK`,要支持这样的用法。
### 上面场景使用`TTL`的整体构架
构架涉及3个角色:容器、用户应用、`SDK`。
整体流程:
1. 请求进入`PAAS`容器,提取上下文信息并设置好上下文。
2. 进入用户应用处理业务,业务调用`SDK`(如`DB`、消息、etc)。
用户应用会使用线程池,所以调用`SDK`的线程可能不是请求的线程。
3. 进入`SDK`处理。
提取上下文的信息,决定是否符合拒绝处理。
整个过程中,上下文的传递 对于 **用户应用代码** 期望是透明的。
---
## File: ttl-integrations/vertx4-ttl-integration/README.md
# Vertx 4的TTL集成
## 1. 保证异步io回调中传递TTL值
### 1.1修饰`io.vertx.core.Handler`
使用[`TtlVertxHandler`](src/main/java/com/alibaba/ttl/integration/vertx4/TtlVertxHandler.java)来修饰传入的`Handler`。
### 1.2 修饰`io.vertx.core.Future`
修饰了的Vert.x执行器组件如下:
- `io.vertx.core.Future`
- `io.vertx.core.impl.future.FutureImpl`
---
- 修饰实现代码在[`VertxFutureTtlTransformlet.java`](src/main/java/com/alibaba/ttl/integration/vertx4/agent/transformlet/VertxFutureTtlTransformlet.java)。
示例代码:
```java
Vertx vertx = Vertx.vertx();
//build channel
ManagedChannel channel = VertxChannelBuilder
.forAddress(vertx, "localhost", 8080)
.usePlaintext()
.build();
// set in parent thread
TransmittableThreadLocal context = new TransmittableThreadLocal<>();
context.set("value-set-in-parent");
//init stub
io.grpc.stub.XXX stub = XXX.newVertxStub(channel);
HelloRequest request = HelloRequest.newBuilder().setName("Julien").build();
//init handler
Handler> handler = event -> {
// read in callback, value is "value-set-in-parent"
context.get();
if (event.succeeded()) {
//do something
} else {
// find exception
}
};
// extra work, create decorated TtlVertxHandler object
TtlVertxHandler> ttlVertxHandler = TtlVertxHandler.get(handler);
//send request
stub.sayHello(request).onComplete(ttlVertxHandler);
```
## 2. 保证`eventbus`上传递TTL值
### 2.1修饰`java.lang.Runnable`
使用[`TtlRunnable`](../../src/main/java/com/alibaba/ttl/TtlRunnable.java)来修饰传入的`Runnable`。
### 2.2 修饰`io.netty.util.concurrent.SingleThreadEventExecutor`
修饰实现代码在[`NettySingleThreadEventExecutorTtlTransformlet.java`](src/main/java/com/alibaba/ttl/integration/vertx4/agent/transformlet/NettySingleThreadEventExecutorTtlTransformlet.java)。
---
## File: ttl-integrations/vertx3-ttl-integration/README.md
# Vertx 4的TTL集成
## 1. 保证异步io回调中传递TTL值
### 1.1修饰`io.vertx.core.Handler`
使用[`TtlVertxHandler`](src/main/java/com/alibaba/ttl/integration/vertx3/TtlVertxHandler.java)来修饰传入的`Handler`。
### 1.2 修饰`io.vertx.core.Future`
修饰了的Vert.x执行器组件如下:
- `io.vertx.core.Future`
- `io.vertx.core.impl.future.FutureImpl`
- `io.vertx.core.http.impl.HttpClientImpl`
---
- 修饰实现代码在[`VertxFutureTtlTransformlet.java`](src/main/java/com/alibaba/ttl/integration/vertx3/agent/transformlet/VertxFutureTtlTransformlet.java)。
示例代码:
```java
Vertx vertx = Vertx.vertx();
//build channel
ManagedChannel channel = VertxChannelBuilder
.forAddress(vertx, "localhost", 8080)
.usePlaintext()
.build();
// set in parent thread
TransmittableThreadLocal context = new TransmittableThreadLocal<>();
context.set("value-set-in-parent");
//init stub
io.grpc.stub.XXX stub = XXX.newVertxStub(channel);
HelloRequest request = HelloRequest.newBuilder().setName("Julien").build();
//init handler
Handler> handler = event -> {
// read in callback, value is "value-set-in-parent"
context.get();
if (event.succeeded()) {
//do something
} else {
// find exception
}
};
// extra work, create decorated TtlVertxHandler object
TtlVertxHandler> ttlVertxHandler = TtlVertxHandler.get(handler);
//send request
stub.sayHello(request).onComplete(ttlVertxHandler);
```
## 2. 保证`eventbus`上传递TTL值
### 2.1修饰`java.lang.Runnable`
使用[`TtlRunnable`](../../src/main/java/com/alibaba/ttl/TtlRunnable.java)来修饰传入的`Runnable`。
### 2.2 修饰`io.netty.util.concurrent.SingleThreadEventExecutor`
修饰实现代码在[`NettySingleThreadEventExecutorTtlTransformlet.java`](src/main/java/com/alibaba/ttl/integration/vertx3/agent/transformlet/NettySingleThreadEventExecutorTtlTransformlet.java)。
---
## File: ttl-integrations/sample-ttl-agent-extension-transformlet/README.md
# `TTL Agent`扩展`Transformlet`实现的示例工程
## 扩展`Transformlet`的实现
为了提供`TTL Agent`扩展`Transformlet`,包含2部分:
1. `TTL Agent`扩展`Transformlet`的实现类:[`SampleExtensionTransformlet`](src/main/java/com/alibaba/ttl/agent/extension_transformlet/sample/transformlet/SampleExtensionTransformlet.java)。
- 这个示例`Transformlet`修改了类[`ToBeTransformedClass`](src/main/java/com/alibaba/ttl/agent/extension_transformlet/sample/biz/ToBeTransformedClass.java)的`toBeTransformedMethod`方法:在修改方法前插入一行代码,修改方法参数值乘以2(`$1 *= 2;`)。
1. `TTL Agent`扩展`Transformlet`的配置文件:[`META-INF/ttl.agent.transformlets`](src/main/resources/META-INF/ttl.agent.transformlets)
- 配置文件的内容是 扩展`Transformlet`实现类的全类名。
在这个示例工程是`com.alibaba.ttl.agent.extension_transformlet.sample.transformlet.SampleExtensionTransformlet`。
- `TTL Agent`会扫描`Class Path`上的`META-INF/ttl.agent.transformlets`文件,自动发现并启用这些扩展`Transformlet`。
即只要将扩展`Transformlet`的依赖`Jar`引入到应用中就会自动生效。
- 这个扫描并自动加载生效与`JDK`的[`ServiceLoader`](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/ServiceLoader.html)一样,只是使用不同的扩展配置文件。
## 扩展`Transformlet`实现的注意点
在`Transformlet`的实现逻辑中,**_不要_** 加载被`transform`的类 或是 做实例化,如
- `ToBeTransformedClass.class`
- `Class.forName("com.alibaba.ttl.agent.extension_transformlet.sample.biz.ToBeTransformedClass")`
- `new ToBeTransformedClass()`
加载被`transform`的类会导致对该类的`transform`操作被跳过,也就是`Transformlet`失效了。
## 扩展`Transformlet`的测试与生效验证
单元测试类 在 [`ToBeTransformedClassTest`](src/test/java/com/alibaba/ttl/agent/extension_transformlet/sample/biz/ToBeTransformedClassTest.java)。
通过运行`Maven`单元测试验证扩展`Transformlet` `SampleExtensionTransformlet`是否生效:
```bash
# sample-ttl-agent-extension-transformlet 工程目录,执行
# 1. 先 mvn install TTL lib
(cd ../.. && mvn install -Dmaven.test.skip)
# 2. 验证 扩展Transformlet SampleExtensionTransformlet 是否生效
mvn test -Penable-TtlAgent-forTest
# 更多输出TTL的Transform类操作的日志
mvn test -Penable-TtlAgent-forTest -Penable-LogTransform-forTest
```
## 运行示例`SampleMain`
可以通过`Java`命令行参数来运行示例`SampleMain`:
```java
java -javaagent:path/to/transmittable-thread-local-2.x.y.jar \
-cp target/classes \
com.alibaba.ttl.agent.extension_transformlet.sample.biz.SampleMain
```
通过脚本[`scripts/run.sh`](scripts/run.sh)快速上面命令行的运行。
--- METRICS ---
- Files Extracted: 9
- Estimated Token Budget: ~21858 tokens
- Recency Window: Active (< 180 days)
- Canonical Reference: https://codewiki.google/github.com/alibaba/transmittable-thread-local