使用 属性 文件更改假装客户端返回的实例(覆盖启动器和尤里卡自动功能区行为)
Change feign client returned instance by using property file ( over-ride starter and eureka auto ribbon behavior)
简介
所以我们有一个用作父级的启动器,它有各种模板和自动配置,也可以加载假客户端。
假装客户端正在使用下面的尤里卡发现来迭代服务。
所以这是我们的一个假客户的例子:
进口org.springframework.cloud.openfeign.FeignClient;
@FeignClient(name="decide-proxy")
public interface DecideControllerApiClient extends DecideControllerApi {
}
在使用此 spring 引导(以及云启动器)作为父项的项目中(在我们的 pom.xml 中),我希望能够转到属性文件并做这样的事情:
eureka.client.filter.enabled=true
eureka.client.filter.services.doc-tools.host=localhost
eureka.client.filter.services.doc-tools.port=8015
我现在有办法做到这一点,但它涉及到使用方面和反射,而且它似乎减慢了我的项目 - 这是一个丑陋的大 hack。
那么有没有办法通过色带 属性 或其他东西来实现?
最好不修改起始代码?
问题是:
我们如何使用 属性 文件更改假装客户端返回的实例。
从本质上讲,我们如何才能覆盖 starter 和 eureka auto ribbon 行为。
当前黑客
于是我看到了下面的link:
基于此,我创建了以下似乎有效的方法:
import com.netflix.appinfo.InstanceInfo;
import eureka.InstanceBuildVersionProperties.InstanceHostFilter;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.netflix.eureka.EurekaDiscoveryClient.EurekaServiceInstance;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@RequiredArgsConstructor
@Aspect
public class EurekaInstanceHostFilter {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final String versionMetadataKey;
private final InstanceBuildVersionProperties filters;
@SuppressWarnings("unchecked")
@Around("execution(public * org.springframework.cloud.netflix.eureka.EurekaDiscoveryClient.getInstances(..))")
public Object filterInstances(ProceedingJoinPoint jp) throws Throwable {
if (filters == null || !filters.isEnabled()) logger.error("Should not be filtering...");
List<ServiceInstance> instances = (List<ServiceInstance>) jp.proceed();
String serviceId = (String) jp.getArgs()[0];
InstanceHostFilter filter = filters.getServices().get(serviceId);
if(filter != null){
instances.forEach( instance -> {
try {
Class<?> clazz = EurekaServiceInstance.class;
Class<?> clazzInfo = InstanceInfo.class;
Field instanceField = clazz.getDeclaredField("instance");
instanceField.setAccessible(true);
InstanceInfo instanceInfo = (InstanceInfo) instanceField.get(instance);
String originalHostName =instanceInfo.getHostName();
int originalPort =instanceInfo.getPort();
//SET THE VALUES
String changeInstanceId = filter.getHost() + ":" + instance.getServiceId() + ":" +filter.getPort();
setField(instanceInfo, clazzInfo, "instanceId", changeInstanceId );
//HomePageURL
String newHomePageUrl = instanceInfo.getHomePageUrl().replace(originalHostName, filter.getHost()) .replace(originalPort+"", filter.getPort()+"");
setField(instanceInfo, clazzInfo, "homePageUrl", newHomePageUrl );
//StatusPageUrl
String statusPageUrl = instanceInfo.getStatusPageUrl().replace(originalHostName, filter.getHost()) .replace(originalPort+"", filter.getPort()+"");
setField(instanceInfo, clazzInfo, "statusPageUrl", statusPageUrl );
//healthCheckURL
String healthCheckUrl = instanceInfo.getHealthCheckUrl().replace(originalHostName, filter.getHost()) .replace(originalPort+"", filter.getPort()+"");
setField(instanceInfo, clazzInfo, "healthCheckUrl", healthCheckUrl );
//hostName
String hostName = instanceInfo.getHostName().replace(originalHostName, filter.getHost());
setField(instanceInfo, clazzInfo, "hostName", hostName );
setIntField(instanceInfo, clazzInfo, "port", filter.getPort());
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
});
}
return instances;
}
private void setField(Object instanceInfo, Class<?> clazzInfo, String fieldName, String value) throws NoSuchFieldException, IllegalAccessException {
Field instanceId = clazzInfo.getDeclaredField(fieldName);
instanceId.setAccessible(true);
instanceId.set(instanceInfo, value);
}
private void setIntField(Object instanceInfo, Class<?> clazzInfo, String fieldName, int value) throws NoSuchFieldException, IllegalAccessException {
Field instanceId = clazzInfo.getDeclaredField(fieldName);
instanceId.setAccessible(true);
instanceId.setInt(instanceInfo, value);
}
}
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConditionalOnProperty(name = "eureka.client.filter.enabled", havingValue = "true")
@EnableConfigurationProperties(InstanceBuildVersionProperties.class)
public class EurekaInstanceHostFilterAutoConfig {
@Value("${eureka.instance.metadata.keys.version:instanceBuildVersion}")
private String versionMetadataKey;
@Bean
@ConditionalOnProperty(name = "eureka.client.filter.enabled", havingValue = "true")
public EurekaInstanceHostFilter eurekaInstanceBuildVersionFilter(InstanceBuildVersionProperties filters) {
return new EurekaInstanceHostFilter(versionMetadataKey, filters);
}
}
import lombok.Getter;
import lombok.Setter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.HashMap;
import java.util.Map;
import static org.apache.commons.lang3.ArrayUtils.contains;
@ConfigurationProperties("eureka.client.filter")
public class InstanceBuildVersionProperties {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* Indicates whether or not service instances versions should be filtered
*/
@Getter @Setter
private boolean enabled = false;
/**
* Map of service instance version filters.
* The key is the service name and the value configures a filter set for services instances
*/
@Getter
private Map<String, InstanceHostFilter> services = new HashMap<>();
public boolean isKept(String serviceId) {
logger.debug("Considering service {} instance", serviceId);
if (services.containsKey(serviceId)) {
InstanceHostFilter filter = services.get(serviceId);
//TODO:
return true;
}
return true;
}
@Getter @Setter
public static class InstanceHostFilter {
/**
* host to use
*/
private String host;
private int port;
}
}
我注意到上面的代码导致我的项目变慢了很多。
此外,由于使用反射和方面,它非常 hacky。
其他想法
唯一的其他想法可能是开始玩启动器本身内的配置条件加载。因此,我可以根据应用程序 属性 文件中的属性尝试加载一个假客户端或另一个假客户端。
当然,这可能意味着我们必须在每个模板、假装客户端和每个微服务的自动配置中包含调试支持。
想法?
更新
我们尝试使用属性文件和其中一个功能区属性加上假名,但这没有用。类似于:
decide-proxy.ribbon.listOfServers=
bootstrap.yml 中的以下配置适合我,
ribbon:
eureka:
enabled: false
my-service:
ribbon:
listOfServers: 192.168.1.217:9010
我在我的 zuul 代理中配置它,这样它就可以引用未在同一个 eureka 中注册的服务。这样它将禁用所有尤里卡查找。
---已更新---
参考以下link,它可以指定一些服务使用指定的服务器列表而不禁用所有的eureka查找。
,指定
my-service:
ribbon:
NIWSServerListClassName:com.netflix.loadbalancer.ConfigurationBasedServerList
listOfServers: server1:18201,server2:18201
为您服务
简介
所以我们有一个用作父级的启动器,它有各种模板和自动配置,也可以加载假客户端。
假装客户端正在使用下面的尤里卡发现来迭代服务。
所以这是我们的一个假客户的例子:
进口org.springframework.cloud.openfeign.FeignClient;
@FeignClient(name="decide-proxy")
public interface DecideControllerApiClient extends DecideControllerApi {
}
在使用此 spring 引导(以及云启动器)作为父项的项目中(在我们的 pom.xml 中),我希望能够转到属性文件并做这样的事情:
eureka.client.filter.enabled=true
eureka.client.filter.services.doc-tools.host=localhost
eureka.client.filter.services.doc-tools.port=8015
我现在有办法做到这一点,但它涉及到使用方面和反射,而且它似乎减慢了我的项目 - 这是一个丑陋的大 hack。
那么有没有办法通过色带 属性 或其他东西来实现?
最好不修改起始代码?
问题是: 我们如何使用 属性 文件更改假装客户端返回的实例。 从本质上讲,我们如何才能覆盖 starter 和 eureka auto ribbon 行为。
当前黑客
于是我看到了下面的link:
基于此,我创建了以下似乎有效的方法:
import com.netflix.appinfo.InstanceInfo;
import eureka.InstanceBuildVersionProperties.InstanceHostFilter;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.netflix.eureka.EurekaDiscoveryClient.EurekaServiceInstance;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@RequiredArgsConstructor
@Aspect
public class EurekaInstanceHostFilter {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final String versionMetadataKey;
private final InstanceBuildVersionProperties filters;
@SuppressWarnings("unchecked")
@Around("execution(public * org.springframework.cloud.netflix.eureka.EurekaDiscoveryClient.getInstances(..))")
public Object filterInstances(ProceedingJoinPoint jp) throws Throwable {
if (filters == null || !filters.isEnabled()) logger.error("Should not be filtering...");
List<ServiceInstance> instances = (List<ServiceInstance>) jp.proceed();
String serviceId = (String) jp.getArgs()[0];
InstanceHostFilter filter = filters.getServices().get(serviceId);
if(filter != null){
instances.forEach( instance -> {
try {
Class<?> clazz = EurekaServiceInstance.class;
Class<?> clazzInfo = InstanceInfo.class;
Field instanceField = clazz.getDeclaredField("instance");
instanceField.setAccessible(true);
InstanceInfo instanceInfo = (InstanceInfo) instanceField.get(instance);
String originalHostName =instanceInfo.getHostName();
int originalPort =instanceInfo.getPort();
//SET THE VALUES
String changeInstanceId = filter.getHost() + ":" + instance.getServiceId() + ":" +filter.getPort();
setField(instanceInfo, clazzInfo, "instanceId", changeInstanceId );
//HomePageURL
String newHomePageUrl = instanceInfo.getHomePageUrl().replace(originalHostName, filter.getHost()) .replace(originalPort+"", filter.getPort()+"");
setField(instanceInfo, clazzInfo, "homePageUrl", newHomePageUrl );
//StatusPageUrl
String statusPageUrl = instanceInfo.getStatusPageUrl().replace(originalHostName, filter.getHost()) .replace(originalPort+"", filter.getPort()+"");
setField(instanceInfo, clazzInfo, "statusPageUrl", statusPageUrl );
//healthCheckURL
String healthCheckUrl = instanceInfo.getHealthCheckUrl().replace(originalHostName, filter.getHost()) .replace(originalPort+"", filter.getPort()+"");
setField(instanceInfo, clazzInfo, "healthCheckUrl", healthCheckUrl );
//hostName
String hostName = instanceInfo.getHostName().replace(originalHostName, filter.getHost());
setField(instanceInfo, clazzInfo, "hostName", hostName );
setIntField(instanceInfo, clazzInfo, "port", filter.getPort());
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
});
}
return instances;
}
private void setField(Object instanceInfo, Class<?> clazzInfo, String fieldName, String value) throws NoSuchFieldException, IllegalAccessException {
Field instanceId = clazzInfo.getDeclaredField(fieldName);
instanceId.setAccessible(true);
instanceId.set(instanceInfo, value);
}
private void setIntField(Object instanceInfo, Class<?> clazzInfo, String fieldName, int value) throws NoSuchFieldException, IllegalAccessException {
Field instanceId = clazzInfo.getDeclaredField(fieldName);
instanceId.setAccessible(true);
instanceId.setInt(instanceInfo, value);
}
}
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConditionalOnProperty(name = "eureka.client.filter.enabled", havingValue = "true")
@EnableConfigurationProperties(InstanceBuildVersionProperties.class)
public class EurekaInstanceHostFilterAutoConfig {
@Value("${eureka.instance.metadata.keys.version:instanceBuildVersion}")
private String versionMetadataKey;
@Bean
@ConditionalOnProperty(name = "eureka.client.filter.enabled", havingValue = "true")
public EurekaInstanceHostFilter eurekaInstanceBuildVersionFilter(InstanceBuildVersionProperties filters) {
return new EurekaInstanceHostFilter(versionMetadataKey, filters);
}
}
import lombok.Getter;
import lombok.Setter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.HashMap;
import java.util.Map;
import static org.apache.commons.lang3.ArrayUtils.contains;
@ConfigurationProperties("eureka.client.filter")
public class InstanceBuildVersionProperties {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* Indicates whether or not service instances versions should be filtered
*/
@Getter @Setter
private boolean enabled = false;
/**
* Map of service instance version filters.
* The key is the service name and the value configures a filter set for services instances
*/
@Getter
private Map<String, InstanceHostFilter> services = new HashMap<>();
public boolean isKept(String serviceId) {
logger.debug("Considering service {} instance", serviceId);
if (services.containsKey(serviceId)) {
InstanceHostFilter filter = services.get(serviceId);
//TODO:
return true;
}
return true;
}
@Getter @Setter
public static class InstanceHostFilter {
/**
* host to use
*/
private String host;
private int port;
}
}
我注意到上面的代码导致我的项目变慢了很多。 此外,由于使用反射和方面,它非常 hacky。
其他想法
唯一的其他想法可能是开始玩启动器本身内的配置条件加载。因此,我可以根据应用程序 属性 文件中的属性尝试加载一个假客户端或另一个假客户端。
当然,这可能意味着我们必须在每个模板、假装客户端和每个微服务的自动配置中包含调试支持。
想法?
更新
我们尝试使用属性文件和其中一个功能区属性加上假名,但这没有用。类似于:
decide-proxy.ribbon.listOfServers=
bootstrap.yml 中的以下配置适合我,
ribbon:
eureka:
enabled: false
my-service:
ribbon:
listOfServers: 192.168.1.217:9010
我在我的 zuul 代理中配置它,这样它就可以引用未在同一个 eureka 中注册的服务。这样它将禁用所有尤里卡查找。
---已更新---
参考以下link,它可以指定一些服务使用指定的服务器列表而不禁用所有的eureka查找。
my-service:
ribbon:
NIWSServerListClassName:com.netflix.loadbalancer.ConfigurationBasedServerList
listOfServers: server1:18201,server2:18201
为您服务