这个库带来怎样的好处和优势?
wilddylan opened this issue · 4 comments
wilddylan commented
这个库带来怎样的好处和优势?
oldratlee commented
这个库能带来怎样的好处和优势
@wilddylan 好问题 ❤️ 文档中没正面说明,需要直接说明出来。 :)
回答一个方案『好处』和『优势』时,要说明的是『 解决一个问题(场景) 的 另一其它方案 的 对比』。
即 涉及3部分: 1) 解决的问题/场景,2) 其它解决方案, 3) 对比。
1. 关于 解决的问题/场景
简单地说:异步执行上下文的传递;
期望做到:透明/自动完成所有异步执行上下文的可定制、规范化的捕捉/传递。
2. 其它解决方案
解决『异步执行上下文的传递』,朴素直接的实现方案 是 下面的示例代码。
注:示例中的异步执行 是 以 new Thread
方式,实际代码的异步执行可能是ThreadPool
、RxJava
/Reactor
、Kotlin
协程 等等不同的方式。
import java.util.HashMap;
import java.util.Map;
public class Demo {
public static void main(String[] args) {
// 1. *业务逻辑*需要做的实现逻辑:获取当前上下文
final Map<String, String> context = getContext();
// 2. *业务逻辑*需要做的实现逻辑:捕捉上下文
Map<String, String> contextCopy = new HashMap<>(context);
Runnable asyncLogic = () -> {
// 在异步执行逻辑中,
// 3. 传递,通过 Lambda的外部变量 contextCopy
// 4. 使用上下文
System.out.println(contextCopy);
};
new Thread(asyncLogic).start();
}
private static Map<String, String> getContext() {
return contextHolder.get();
}
// 使用 ThreadLocal:
// - 可以 不同的线程(如执行不同的请求用户的处理) 有自己的Context。
// - 避免 Context 总是通过 函数参数 传递,中间路过的业务的逻辑 都关注/感知 框架的上下文
private static final ThreadLocal<Map<String, String>> contextHolder = ThreadLocal.withInitial(HashMap::new);
}
这样做法的问题
从 业务使用 角度来看
- 繁琐:
业务逻辑自己 要知道 有哪些上下文、分别要如何获取,并一个一个去捕捉/传递。
上面示例代码 只示意了 一个上下文。 - 依赖:
直接依赖 不同 上下文获取的逻辑/类(比如RPC
的上下文、全链路跟踪的上下文、不同业务模块中的业务上下文,等等)。 - 静态(易漏):
- 要事先知道 哪些上下文;如果有 新异步执行的上下文 出现了,业务逻辑要修改:添加新上下文传递的几行代码。
即 因系统的上下文新增,业务的逻辑就跟进要修改。 - 对于业务来说,不关心系统的上下文,即往往就可能遗漏,会是线上故障了。
- 比如对阿里这样的系统,功能/组件多涉及的上下文多、逻辑流程长,
上下文问题 实际上 是个 大的易错的架构问题。需要统一的解决方案。
- 要事先知道 哪些上下文;如果有 新异步执行的上下文 出现了,业务逻辑要修改:添加新上下文传递的几行代码。
- 定制性:
- 业务要关注『上下文的传递方式』。直接传引用?还是拷贝传值?拷贝是深拷贝还是浅拷贝?
上面的示例代码实际上是拷贝了一层HashMap
,即浅拷贝,实际业务中往往不会这么简单。 - 『上下文的传递方式』这点 往往是 上下文的提供者(或说是 业务逻辑的框架部分)才能 决策处理好的,而 上下文的使用者(或说是 业务逻辑的应用部分)往往不(期望)知道上下文的传递方式。
- 这也可以理解成是 依赖,即业务逻辑 依赖/关注/实现了 系统/架构的『上下文的传递方式』。
- 业务要关注『上下文的传递方式』。直接传引用?还是拷贝传值?拷贝是深拷贝还是浅拷贝?
从 整体流程实现 角度来看
上下文传递流程的规范化:
- 上下文传递到了子线程 要做好 清理(或更准确地说是 要恢复成之前的上下文),需要业务逻辑去处理好。
上面示例代码 实际上 没有解决 这方面的问题。 - 如果 业务逻辑 对 清理 的 处理不正确:
- 如果 清理操作漏了:
- 下一次执行可能是上次的,即『上下文的 污染/串号』,会导致业务逻辑错误。
- 『上下文的 泄漏』,会导致内存泄漏问题。
- 如果 清理操作做多了,会出现上下文 丢失。
- 如果 清理操作漏了:
- 上面的问题,在业务开发中引发的
Bug
真是 屡见不鲜 !
怎么设计一个『上下文传递流程』方案(即上下文的生命周期),保证 没有上面的问题? - 期望:上下文生命周期的操作 从业务逻辑中 分离出来;
业务逻辑 不涉及 生命周期,就不会有 由清理引发这些问题了。 - 整个流程/上下文生命周期 可以规范化成:捕捉、回放和恢复 3个操作,即
CRR(capture/replay/restore)
。
3. 对比
好处和优势 对应的就是 上面的问题解决了,即做到:
透明/自动完成所有异步执行上下文的可定制、规范化的捕捉/传递。
基于示例代码说明如下:
import com.alibaba.ttl.TransmittableThreadLocal;
import com.alibaba.ttl.threadpool.TtlExecutors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo {
public static void main(String[] args) {
// # 从 业务使用 角度来看 #
//
// 1. 关于『繁琐』:
// 无需关注上下文的传递(由库接管完成)。
// 自然无需 业务逻辑自己 知道 有哪些上下文、分别要如何获取,并一个一个去捕捉/传递。
// 2. 关于『依赖』:
// 业务逻辑直接使用 TTL,不会依赖 不同 上下文获取的逻辑/类。
// 3. 关于『静态(易漏)』
// 由库接管完成 *所有* 上下文的传递,非静态 自动感知新加的上下文。
// 4. 关于『定制性』:
// 提供了`copy`方法,
// 由框架本身(即上下文的提供者)定制好了『上下文的传递方式』,业务逻辑无需感知。
//
// # 从 整体流程实现 角度来看 #
//
// 1. 上下文传递流程的规范化
// 上下文生命周期的操作 从业务逻辑中 分离出来,
// 由库接管完成,业务逻辑无需感知。不会有生命周期相关 *屡见不鲜*的 Bug!
Runnable asyncLogic = () -> {
// 传递,在异步执行逻辑中,使用上下文
System.out.println(contextHolder1.get());
System.out.println(contextHolder2.get());
};
executorService.submit(asyncLogic);
}
private static final TransmittableThreadLocal<String> contextHolder1 = new TransmittableThreadLocal<String>();
private static final TransmittableThreadLocal<Node> contextHolder2 = new TransmittableThreadLocal<Node>() {
@Override
protected Node copy(Node parentValue) {
return new Node(parentValue.id); // 定制传递方式
}
@Override
protected Node initialValue() {
return new Node("1");
}
};
private static class Node {
final String id;
public Node(String id) {
this.id = id;
}
@Override
public String toString() {
return "Node{id='" + id + '\'' + '}';
}
}
private static final ExecutorService executorService = TtlExecutors.getTtlExecutorService(Executors.newCachedThreadPool());
}
@wilddylan 你看看,我说的明白不? 不清楚,继续交流。
另外,推荐看一下 issue
wilddylan commented
很棒唉 ~ 嘿嘿,为你点赞,快给我一个你加密的工号,我去给你点赞!
oldratlee commented
很棒唉 ~ 嘿嘿,为你点赞,快给我一个你加密的工号,我去给你点赞!
噗~
直接用我的id oldratlee
在内网就查到我了,
名字 在我的github
主页上: https://github.com/oldratlee 。
PS: 这个Issue
不用关了,好让大家可以方便地看到 ❤️ @wilddylan
oldratlee commented
Closed.
上面的讨论内容已并入项目文档主页: ✨ 使用TTL
的好处与必要性
欢迎 大家在这个 issue 继续讨论 ❤️