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

Java Agent 使用方式 与 显式的使用方式 混用时,在tomcat下出现失效情况 #231

Closed
codingPao opened this issue Dec 30, 2020 · 7 comments
Assignees
Labels

Comments

@codingPao
Copy link

codingPao commented Dec 30, 2020

定位了一下发现原因是:

  • tomcatWebappClassLoader打破了双亲委托机制
  • com.alibaba.ttl.TransmittableThreadLocal.holder会在WebappClassLoader中初始化一份;在做TTL操作时,TTL的引用由这个 holder 持有。
  • submit时,TtlRunnable虽然走了增强,但是copy操作的是由BootstrapClassLoader初始化的com.alibaba.ttl.TransmittableThreadLocal.holder进行copy,没有copy到预期的TTL。
@codingPao
Copy link
Author

tomcat版本是8.0.22

@codingPao
Copy link
Author

codingPao commented Dec 30, 2020

private static final TransmittableThreadLocal<String> TRANSMITTABLE_CONTEXT = new TransmittableThreadLocal();

public void test() {
    TRANSMITTABLE_CONTEXT.set("Test");
    ExecutorService e = Executors.newFixedThreadPool(1);
    e.submit(() -> log.info("init thread"));
    e.submit(() -> {
        final String test = TRANSMITTABLE_CONTEXT.get();
        log.info("TTL:{}", test);
    });
}

这是我的demo

@oldratlee
Copy link
Member

oldratlee commented Dec 31, 2020

tomcatWebappClassLoader打破了双亲委托机制

tomcatWebappClassLoader双亲委托机制应该是可以配置的,
比如JDK标准类(如java.lang.String)不会打破,否则也会有各种问题。

你找一下 tomcatWebappClassLoader双亲委托的配置的方式,
将TTL的com.alibaba.ttl包 配置成走双亲委托,来解决问题。 @codingPao
(Java Agent方式使用TTL时,TTL的类应该与JDK标准类是一样的ClassLoader方式)


有了结果反馈一下 :") @codingPao

  • 你如何配置的
  • 要注意什么

方便 其它人 也了解和解决类似的问题 :")


PS: 相关的Issue与解决:

@oldratlee oldratlee self-assigned this Dec 31, 2020
@oldratlee oldratlee added the ❓question Further information is requested label Dec 31, 2020
@codingPao
Copy link
Author

codingPao commented Dec 31, 2020

目前能想到得有两个解决方式:

  • 调整tomcat配置
  • 增强tomcatWebappClassLoader

如果调整tomcat配置,可以参考https://tomcat.apache.org/tomcat-8.0-doc/class-loader-howto.html
If the web application class loader is configured with then the order becomes:

  • Bootstrap classes of your JVM
  • System class loader classes (described above)
  • Common class loader classes (described above)
  • /WEB-INF/classes of your web application
  • /WEB-INF/lib/*.jar of your web application

但是这样调整得代价就是修改了整个tomcat得加载顺序,而tomcat目前也没有找到类似jbossDjboss.modules.system.pkgs配置
于是我们翻阅了tomcatWebappClassLoader源码,以下是tomcat 8.5.30版本
org.apache.catalina.loader.WebappClassLoaderBase#loadClass(java.lang.String, boolean)
org.apache.catalina.loader.WebappClassLoaderBase#filter(java.lang.String, boolean)

public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
    /**
     * 忽略一部分
     */
      boolean delegateLoad = delegate || filter(name, true);
    /**
     * 忽略一部分
     */
}

protected boolean filter(String name, boolean isClassName) {

        if (name == null)
            return false;

        char ch;
        if (name.startsWith("javax")) {
            /* 5 == length("javax") */
            if (name.length() == 5) {
                return false;
            }
            ch = name.charAt(5);
            if (isClassName && ch == '.') {
                /* 6 == length("javax.") */
                if (name.startsWith("servlet.jsp.jstl.", 6)) {
                    return false;
                }
                if (name.startsWith("el.", 6) ||
                    name.startsWith("servlet.", 6) ||
                    name.startsWith("websocket.", 6) ||
                    name.startsWith("security.auth.message.", 6)) {
                    return true;
                }
            } else if (!isClassName && ch == '/') {
                /* 6 == length("javax/") */
                if (name.startsWith("servlet/jsp/jstl/", 6)) {
                    return false;
                }
                if (name.startsWith("el/", 6) ||
                    name.startsWith("servlet/", 6) ||
                    name.startsWith("websocket/", 6) ||
                    name.startsWith("security/auth/message/", 6)) {
                    return true;
                }
            }
        } else if (name.startsWith("org")) {
            /* 3 == length("org") */
            if (name.length() == 3) {
                return false;
            }
            ch = name.charAt(3);
            if (isClassName && ch == '.') {
                /* 4 == length("org.") */
                if (name.startsWith("apache.", 4)) {
                    /* 11 == length("org.apache.") */
                    if (name.startsWith("tomcat.jdbc.", 11)) {
                        return false;
                    }
                    if (name.startsWith("el.", 11) ||
                        name.startsWith("catalina.", 11) ||
                        name.startsWith("jasper.", 11) ||
                        name.startsWith("juli.", 11) ||
                        name.startsWith("tomcat.", 11) ||
                        name.startsWith("naming.", 11) ||
                        name.startsWith("coyote.", 11)) {
                        return true;
                    }
                }
            } else if (!isClassName && ch == '/') {
                /* 4 == length("org/") */
                if (name.startsWith("apache/", 4)) {
                    /* 11 == length("org/apache/") */
                    if (name.startsWith("tomcat/jdbc/", 11)) {
                        return false;
                    }
                    if (name.startsWith("el/", 11) ||
                        name.startsWith("catalina/", 11) ||
                        name.startsWith("jasper/", 11) ||
                        name.startsWith("juli/", 11) ||
                        name.startsWith("tomcat/", 11) ||
                        name.startsWith("naming/", 11) ||
                        name.startsWith("coyote/", 11)) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

基于以上可以看出tomcat对于例如servlet-api.jar等资源加载过滤逻辑。

因为我们目前研发了一个基于agent得插件平台,可以动态下发增强逻辑,
所以我们增强了一下org.apache.catalina.loader.WebappClassLoaderBase得filter方法过滤逻辑,
namecom.alibaba.ttlcom/alibaba/ttl开头得返回true,让bootstrapclassloader负责加载TTL相关类。
这样子JVM中只会初始化一份TTL,解决了我们目前遇到得失效问题。

@oldratlee
Copy link
Member

因为我们目前研发了一个基于agent得插件平台,可以动态下发增强逻辑,
所以我们增强了一下org.apache.catalina.loader.WebappClassLoaderBase得filter方法过滤逻辑,
namecom.alibaba.ttlcom/alibaba/ttl开头得返回true,让bootstrapclassloader负责加载TTL相关类。
这样子JVM中只会初始化一份TTL,解决了我们目前遇到得失效问题。

@codingPao 那这个 Issue 先 close 了,后续有什么欢迎继续讨论。

@xiaoxiaocaiiao
Copy link

@oldratlee 能分享下这个修复方案的源码在哪个项目中能看到吗

@oldratlee
Copy link
Member

@oldratlee 能分享下这个修复方案的源码在哪个项目中能看到吗

Tomcat的修改与配置 @codingPao 的实现解决的。 @xingwenyang

@codingPao 可以交流 回答一下 😃❤️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants