alibaba / jvm-sandbox

Real - time non-invasive AOP framework container based on JVM
GNU Lesser General Public License v3.0
6.77k stars 1.56k forks source link

ProcessController.returnImmediately返回new DefaultResponse(eurekaServiceInstance)的问题 #472

Open zpinsg opened 3 months ago

zpinsg commented 3 months ago

现在想实现通过agent的增强,对openfeign远程调用做定制化的负载均衡策略。

Spring Cloud 2021.0.0之后的版本使用的是Spring Cloud LoadBalancer进行负载均衡,我找到了一个合适的增强点:

org.springframework.cloud.loadbalancer.core#getInstanceResponse private Response getInstanceResponse(List instances) { if (instances.isEmpty()) { if (log.isWarnEnabled()) { log.warn("No servers available for service: " + serviceId); } return new EmptyResponse(); }

    // Do not move position when there is only 1 instance, especially some suppliers
    // have already filtered instances
    if (instances.size() == 1) {
        return new DefaultResponse(instances.get(0));
    }

    // Ignore the sign bit, this allows pos to loop sequentially from 0 to
    // Integer.MAX_VALUE
    int pos = this.position.incrementAndGet() & Integer.MAX_VALUE;

    ServiceInstance instance = instances.get(pos % instances.size());

    return new DefaultResponse(instance);
}

其中,最后return的new DefaultResponse(instance) 中的instance为org.springframework.cloud.netflix.eureka.EurekaServiceInstance

我编写的module代码如下: public void loadCompleted() { new EventWatchBuilder(moduleEventWatcher) .onClass(CLASS_NAME) .includeBootstrap() .onBehavior(BEHAVIOR_NAME) .onWatch(new AdviceListener() { @Override protected void before(Advice advice) { defectLogger.info("触发zone-avoidance-rule模块"); try {

// List instances = (List) advice.getParameterArray()[0];

                        Object[] parameters = (Object[]) MethodUtils.invokeMethod(advice, true, "getParameterArray");
                        Object param = parameters[0];
                        defectLogger.info("zone-avoidance-rule模块:参数:" + param);
                        // 获取List中的元素
                        List<?> rawList = (List<?>) param;
                        defectLogger.info("zone-avoidance-rule模块:元素个数:" + rawList.size());
                        for (Object obj : rawList) {
                            Map<String, String> metadata = (Map<String, String>) MethodUtils.invokeMethod(obj, true, "getMetadata");
                            defectLogger.info("zone-avoidance-rule模块:元素metadata-zone:" + metadata.get("zone"));
                            if (metadata.get("zone").equals("zone2")) {

// defectLogger.info("zone-avoidance-rule模块:返回zone2的服务实例"); // Class<?> defaultResponseClass = Class.forName("org.springframework.cloud.client.loadbalancer.DefaultResponse"); // Constructor<?> constructor = defaultResponseClass.getConstructor(EurekaServiceInstance.class); // DefaultResponse defaultResponse = (DefaultResponse) constructor.newInstance(obj); // ProcessController.returnImmediately(defaultResponse);

// ProcessController.returnImmediately(new DefaultResponse((EurekaServiceInstance)obj));

                                ProcessController.returnImmediately(ConstructorUtils.invokeConstructor(DefaultResponse.class, obj));

// InstanceInfo instanceInfo = (InstanceInfo)MethodUtils.invokeExactMethod(obj,"getInstanceInfo"); // EurekaServiceInstance eurekaServiceInstance = new EurekaServiceInstance(instanceInfo); // defectLogger.info("zone-avoidance-rule模块:创建eurekaServiceInstance:" + eurekaServiceInstance); // ProcessController.returnImmediately(new DefaultResponse(eurekaServiceInstance)); } } } catch (Exception e) { throw new RuntimeException(e); } } }); } }

现在遇到的问题: 因为classloader不同,所以 List instances = (List) advice.getParameterArray()[0]强转会报错,我使用反射可以获取到想要的信息。 但是最后,我要通过ProcessController.returnImmediately方法返回new DefaultResponse(eurekaServiceInstance)时,怎么也绕不过去类型转换的问题。 我尝试了好多方法,可见注释的内容,核心问题是我要new DefaultResponse的构造函数需要传递ServiceInstance实例,我将obj传递进去后是构造不出来的。 困扰我好久了,望不吝赐教

Aresxue commented 2 months ago

1.这种模式下默认全程都需要使用反射,你使用反射的姿势可能不对,你应该先获取ServiceInstance.class,然后获取合适的构造器,最后调用该构造器; 2.还有一种方案如果类不多的话,在module中引入spring cloud相关的依赖(版本和业务一致),然后强制这些类使用业务类加载器加载类似特殊路由的逻辑,可参考com.alibaba.jvm.sandbox.core.classloader.RoutingURLClassLoader