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

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

Open
zpinsg opened this issue Jul 3, 2024 · 1 comment

Comments

@zpinsg
Copy link

zpinsg commented Jul 3, 2024

现在想实现通过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
Copy link

Aresxue commented Jul 30, 2024

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

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

No branches or pull requests

2 participants