Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ForkJoinPool提交的任务是一个runnable类型的,请问这种可以直接把runnable转成ttlRunnable来达到开启ttl的目的吗? #384

Closed
DockerFan opened this issue Jun 1, 2022 · 6 comments
Assignees
Labels
📐 design discussion duplicate 😖 no runnable reproducible demo 😵 please provide a simple runnable demo that reproduce the problem

Comments

@DockerFan
Copy link

debug看业务代码ForkJoinPool的execute方法里的传参是个runnable类型,那可以把一个ForkJoinTask的任务强转成一个runnable,然后处理成ttlRunnable类型这样来开启ttl吗?

@oldratlee
Copy link
Member

oldratlee commented Jun 1, 2022

@DockerFan

  1. 你可以测试一下,验证到效果(以及是不是有问题)。
  2. 要给一下完整可运行的代码(以确认具体的使用方式) 并 说明你期望的运行效果(以及不预期的问题)。
    • 比如『ForkJoinTask的任务强转成一个runnable』这个说法不能准确理解

PS:ForkJoinPool有不风格的用法;这些不同用法本身的区别,可以先查资料了解。

@DockerFan
Copy link
Author

DockerFan commented Jun 2, 2022

先介绍下背景:

  • 有个被测服务里面使用了CompletableFuture.supplyAsync提交了一个Runnable任务到ForkJoinPool
  • 然后sandbox-repeater绑定到被测服务以后需要把每个主请求下面的所有子请求根据traceId进行关联
    (所谓主请求就类似是浏览器发送的http请求,子请求就是处理这个http请求所涉及到的中间件的交互)
  • 所以会涉及到线程池上下文拷贝的问题,但是sandbox-repeater处理的是加载到jvm后的处理

目前解决方案:

一、将ttl-agent绑定到被测服务,这样对于ForkJoinPool所有线程池方法都进行了增强,理论上是不是就可以通过sandbox-repeaterTransmittableThreadLocal拿到正确的上下文拷贝结果呢?

二、通过反射机制,将ForkJoinPoolexecute(Runnable task)如下方法的runnable转成ttlRunnable

public void execute(Runnable task) {
    if (task == null) throw new NullPointerException();

    ForkJoinTask<?> job;
    if (task instanceof ForkJoinTask<?>) // avoid re-wrap
        job = (ForkJoinTask<?>) task;
    else
        job = new ForkJoinTask.RunnableExecuteAction(task);

    externalPush(job);
}

目前看两种方式都没成功,不知道是哪块理解和使用不到位,期待您的回复。

@oldratlee
Copy link
Member

oldratlee commented Jun 3, 2022

先介绍下背景:

……

  • 然后sandbox-repeater绑定到被测服务以后需要把每个主请求下面的所有子请求根据traceId进行关联
    (所谓主请求就类似是浏览器发送的http请求,子请求就是处理这个http请求所涉及到的中间件的交互)
  • 所以会涉及到线程池上下文拷贝的问题,但是sandbox-repeater处理的是加载到jvm后的处理

一、将ttl-agent绑定到被测服务,这样对于ForkJoinPool所有线程池方法都进行了增强,
理论上是不是就可以通过sandbox-repeaterTransmittableThreadLocal拿到正确的上下文拷贝结果呢?

  • 『对于ForkJoinPool所有线程池方法都进行了增强』,具体如何增强?给一下完整可运行的代码,方便实际情况确认与讨论。
    增强的逻辑与sandbox-repeater无关,sandbox-repeater只是实现增强逻辑的工具。
  • 关于sandbox-repeater使用的部分,你可以了解sandbox-repeater项目。
    sandbox-repeater我没有具体使用过、不熟悉 :")

二、通过反射机制,将ForkJoinPoolexecute(Runnable task)如下方法的runnable转成ttlRunnable

上面『通过反射机制 转换』,你的说明和代码少 & 问题相对复杂,不能清楚理解。
比如runnableForkJoinPool#execute方法参数,在不修改/增强这个方法的情况下,用反射修改如何能生效?在你上面的评论说明中,不能看出如何修改/增强这个方法的逻辑。

下面假设在ForkJoinPool#execute方法增强加入了

runnable = TtlRunnable.get(runnable);

作为方法第一行,以在方法入口将参数runnable转成ttlRunnable

简单看来(具体要展开细挖),在入口转成ttlRunnable,影响了ForkJoinPool#execute(Runnable task)方法原有的执行逻辑。
比如,导致ForkJoinPool#execute后续原有逻辑总是会调用new ForkJoinTask.RunnableExecuteAction(task) wrap 成ForkJoinTask<?>

即,这样的转换修改可能会影响 ForkJoinPool本身的逻辑正确性,看起来并不是一个安全可靠的修改方式。

@DockerFan

  • 请 不要提 实际上是重复的 issue/问题 ❤️ 🙏
    duplicate with your last issue 已经加载到jvm的ForkJoinTask如何做ttl增强? #380
  • 请 完整清楚说明问题 ❤️ 🙏
    『要给一下完整可运行的代码(以确认具体的使用方式) 并 说明你期望的运行效果(以及不预期的问题)』

ForkJoinPool的实现有些复杂,运行时增强修改的有些问题我也没有找到可靠可行的解决方法。推荐先了解一下已有的讨论:

@oldratlee oldratlee self-assigned this Jun 3, 2022
@oldratlee
Copy link
Member

oldratlee commented Jun 3, 2022

性能优化那些事(3)
https://zhuanlan.zhihu.com/p/503149395

这篇文章提到:

  • 解决了 TTL Agent运行时生效增强 的问题
  • 并给了 实现代码库。

@DockerFan 可以看看:具体解决到什么程度、是不是有可以参考借鉴的。

关于TTL的首要场景与实现要求

  • TTL功能要首要支持的是 生产环境在线场景;
  • 要求解决方法 足够可靠(如果不能说完全可靠的话),才会实现进来。

即,像测试的便利性 在优先级上要低于 生产在线的可靠性。
当然,如果能找到 两者兼顾的解决方法,就不需要做这样的取舍了。

@oldratlee oldratlee added the 😖 no runnable reproducible demo 😵 please provide a simple runnable demo that reproduce the problem label Jun 10, 2022
@oldratlee
Copy link
Member

oldratlee commented Jun 12, 2022

先 close 了;有进一步信息或问题可以继续讨论。

@JasonMing
Copy link

@DockerFan 之前我自己做了一个方案,分两步

  1. ForkJoinTask 里面插入字段 private final Object captured = capture();
  2. doExec() 中插入包装 Object backup; try { backup = replay(captured); <...原有代码...>} finally { restore(backup); }

这步由于涉及到添加字段,如果类已加载则会失效,必须保障在类初始化之前进行增强。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
📐 design discussion duplicate 😖 no runnable reproducible demo 😵 please provide a simple runnable demo that reproduce the problem
Projects
None yet
Development

No branches or pull requests

3 participants