apache / dubbo

The java implementation of Apache Dubbo. An RPC and microservice framework.
https://dubbo.apache.org/
Apache License 2.0
40.5k stars 26.43k forks source link

拦截扩展的类加载器疑问 #13047

Closed houkunlin closed 8 months ago

houkunlin commented 1 year ago

Ask your question here

Dubbo版本:org.apache.dubbo:dubbo-bom:3.2.5 SpringBoot:3.1.2

根据 https://cn.dubbo.apache.org/zh-cn/overview/mannual/java-sdk/reference-manual/spi/description/filter/ 文档,在使用拦截器时,无法获取 AopAspect 类中 ThreadLocal 数据,经过调试发现:

在 Controller 中 与在 Filter 中,两个 AopAspect.class 不是一个对象,两个 AopAspect.LISTS 不是同一个 ThreadLocal 对象,导致在 Filter 中无法获取到数据。

@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class AopAspect {
    /**
     * 当前请求的某个数据列表
     */
    public static final ThreadLocal<List<?>> LISTS = new ThreadLocal<>();
}

@Slf4j
@Activate(group = CommonConstants.CONSUMER, order = -10000, onClass = {OBJECT_MAPPER_CLASS_NAME})
public class MyConsumerFilter implements Filter {
    public static final String KEY = DataScope.class.getName() + "#Dubbo";

    private final ObjectMapperCodec mapper;

    public MyConsumerFilter(ApplicationModel applicationModel) {
        this.mapper = applicationModel.getBeanFactory().getBean(ObjectMapperCodec.class);
    }

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        log.info("消费者");
        Result result;
        try {
            initContext(invocation);
            result = invoker.invoke(invocation);
        } finally {
            invocation.getObjectAttachments().remove(KEY);
        }
        return result;
    }

    private void initContext(Invocation invocation) {
        // 在 Controller 中,这个是有数据的,但是在 Filter 中无法获取数据
        List<?> lists = AopAspect.LISTS.get();
        if (lists == null || lists.isEmpty()) {
            return;
        }

        String context = mapper.serialize(new MyContext(lists));

        log.info("消费者:{}", context);
        invocation.setObjectAttachment(KEY, context);
    }
}

META-INF/dubbo/org.apache.dubbo.rpc.Filter 文本文件中增加

MyConsumerFilter=com.houkunlin.MyConsumerFilter
songxiaosheng commented 1 year ago

说明不是一个线程,同一个线程内是一样的,可以贴一下你的线程名字么

MingJunDuan commented 1 year ago

threadlocal有跨线程池上下文丢失情况,查看是否存在跨线程池的情况

houkunlin commented 1 year ago

调试执行 Thread.currentThread() 看到都是同一个线程对象

下图,在 Filter 里面调试的内容 image

下图,在 Controller 里面调试的内容 image

AlbumenJ commented 1 year ago

是不是环境里面存在多个 AopAspect.class,对应的 class loader 是不一样的

AlbumenJ commented 1 year ago

Dubbo 的 Filter 默认是使用 Dubbo 的 classloader 加载的

houkunlin commented 1 year ago

有且只有一个 AopAspect.class

AlbumenJ commented 1 year ago

在两边分别执行 System.identityHashCode(AopAspect.LISTS) 看下结果是不是一样呢

houkunlin commented 1 year ago

log.warn("调试 Dubbo 问题 HashCode: {}", System.identityHashCode(DataScopeAop.DATA_SCOPE_LIST)); 执行打印日志截图

image

AlbumenJ commented 1 year ago

log.warn("调试 Dubbo 问题 HashCode: {}", System.identityHashCode(DataScopeAop.DATA_SCOPE_LIST)); 执行打印日志截图

image

如果这两个对象不一样就意味着环境里面有两个 DataScopeAop 类

kimmking commented 1 year ago

最开始的截图就能看出来,两个类的id不一样,一个是22718,一个是7773,说明不是一个类,存在两个不同cl加载的Aop类,导致LISTS静态对象也不是一个。 可以把这两个类的classloader也打印一下。

houkunlin commented 12 months ago

在 Controller 获取 AOP 的 classloader image

在过滤器中获取 AOP 的 classloader image

AlbumenJ commented 12 months ago

那这个应该需要你去处理类加载器的差异,Dubbo 只能通过 TCCL 去加载你的类

CuriousRookie commented 11 months ago

RestartClassLoader... 把 spring-boot-devtools 去掉,或者参考官方文档进行一些自定义?

AlbumenJ commented 8 months ago

No news is good news. Please feel free to create a new issue if you have any question.