在微服务开发过程中,我们有时候会选择Eureka作为服务注册管理中心,经常会碰到一些访问不到的实例被注册上来(不在一个网络上的示例,或者经常会掉线的实例),基于这个现状,我们可以有什么办法限制呢?最直接的方式是直接限制IP地址,如何做。
通过翻阅Eureka源码,发现有一个Configuration: org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfiguration,里面有注册一个InstanceRegistry.
@Bean
public PeerAwareInstanceRegistry peerAwareInstanceRegistry(
ServerCodecs serverCodecs) {
this.eurekaClient.getApplications(); // force initialization
return new InstanceRegistry(this.eurekaServerConfig, this.eurekaClientConfig,
serverCodecs, this.eurekaClient,
this.instanceRegistryProperties.getExpectedNumberOfRenewsPerMin(),
this.instanceRegistryProperties.getDefaultOpenForTrafficCount());
}
都已经发现这个类,那么接下来就好办了,我们重写覆盖这个注入即可。
废话不多说,找到关键注册类,覆盖InstanceRegistry即可
写一个CustomInstanceRegistry,配置写在类注释中。
import java.util.List;
import com.netflix.eureka.lease.Lease;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.cloud.netflix.eureka.server.event.EurekaInstanceCanceledEvent;
import org.springframework.cloud.netflix.eureka.server.event.EurekaInstanceRegisteredEvent;
import org.springframework.cloud.netflix.eureka.server.event.EurekaInstanceRenewedEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import com.netflix.appinfo.ApplicationInfoManager;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.discovery.EurekaClient;
import com.netflix.discovery.EurekaClientConfig;
import com.netflix.discovery.shared.Application;
import com.netflix.eureka.EurekaServerConfig;
import com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl;
import com.netflix.eureka.resources.ServerCodecs;
import org.springframework.context.ApplicationEvent;
/**
* <code>
* @Autowired
* private ApplicationInfoManager applicationInfoManager;
*
* @Autowired
* private EurekaServerConfig eurekaServerConfig;
*
* @Autowired
* private EurekaClientConfig eurekaClientConfig;
*
* @Autowired
* @Qualifier(value = "eurekaClient")
* private EurekaClient eurekaClient;
*
*
*@Value("${eureka.server.expectedNumberOfRenewsPerMin:1}")
* private int expectedNumberOfRenewsPerMin;
*
* @Value("${eureka.server.defaultOpenForTrafficCount:1}")
* private int defaultOpenForTrafficCount;
*
*
* @Value("${eureka.server.allowed.address:''}")
* private String[] allowedAddress;
*
* @Primary
* @Bean
* public PeerAwareInstanceRegistry peerAwareInstanceRegistry(
* ServerCodecs serverCodecs) {
* this.eurekaClient.getApplications(); // force initialization
* return new CustInstanceRegistry(this.eurekaServerConfig, this.eurekaClientConfig,
* serverCodecs, this.eurekaClient, this.expectedNumberOfRenewsPerMin,
* this.defaultOpenForTrafficCount, Arrays.asList(allowedAddress));
* }
* </code>
*<p>
* 覆盖InstanceRegistryBean,设置只允许哪些IP地址注册到Eureka服**务里面来
* </p>
* @author Spencer Gibb
* @author Lycol
*
*/
public class CustomInstanceRegistry extends PeerAwareInstanceRegistryImpl
implements ApplicationContextAware {
private ApplicationContext ctxt;
private int defaultOpenForTrafficCount;
private List<String> allowedRegisteredIpAddress;
Logger log= LoggerFactory.getLogger(CustInstanceRegistry.class);
public CustInstanceRegistry(EurekaServerConfig serverConfig,
EurekaClientConfig clientConfig, ServerCodecs serverCodecs,
EurekaClient eurekaClient, int expectedNumberOfRenewsPerMin,
int defaultOpenForTrafficCount,
List<String> allowedRegisteredIpAddress) {
super(serverConfig, clientConfig, serverCodecs, eurekaClient);
this.expectedNumberOfRenewsPerMin = expectedNumberOfRenewsPerMin;
this.defaultOpenForTrafficCount = defaultOpenForTrafficCount;
this.allowedRegisteredIpAddress=allowedRegisteredIpAddress;
}
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
this.ctxt = context;
}
@Override
public void openForTraffic(ApplicationInfoManager applicationInfoManager, int count) {
super.openForTraffic(applicationInfoManager,
count == 0 ? this.defaultOpenForTrafficCount : count);
}
@Override
public void register(InstanceInfo info, int leaseDuration, boolean isReplication) {
handleRegistration(info, leaseDuration, isReplication);
//不允许注册的IP地址
if(CollectionUtils.isNotEmpty(allowedRegisteredIpAddress)&&
!allowedRegisteredIpAddress.contains(info.getIPAddr())){
log.warn("IP 地址不能被注册到Eureka实例中心来:{[]}",info.getIPAddr());
return;
}
super.register(info, leaseDuration, isReplication);
}
@Override
public void register(final InstanceInfo info, final boolean isReplication) {
handleRegistration(info, resolveInstanceLeaseDuration(info), isReplication);
//不允许注册的IP地址
if(CollectionUtils.isNotEmpty(allowedRegisteredIpAddress)&&
!allowedRegisteredIpAddress.contains(info.getIPAddr())){
log.warn("IP 地址不能被注册到Eureka实例中心来:{[]}",info.getIPAddr());
return;
}
super.register(info, isReplication);
}
@Override
public boolean cancel(String appName, String serverId, boolean isReplication) {
handleCancelation(appName, serverId, isReplication);
return super.cancel(appName, serverId, isReplication);
}
@Override
public boolean renew(final String appName, final String serverId,
boolean isReplication) {
log("renew " + appName + " serverId " + serverId + ", isReplication {}"
+ isReplication);
List<Application> applications = getSortedApplications();
for (Application input : applications) {
if (input.getName().equals(appName)) {
InstanceInfo instance = null;
for (InstanceInfo info : input.getInstances()) {
if (info.getId().equals(serverId)) {
instance = info;
break;
}
}
publishEvent(new EurekaInstanceRenewedEvent(this, appName, serverId,
instance, isReplication));
break;
}
}
return super.renew(appName, serverId, isReplication);
}
@Override
protected boolean internalCancel(String appName, String id, boolean isReplication) {
handleCancelation(appName, id, isReplication);
return super.internalCancel(appName, id, isReplication);
}
private void handleCancelation(String appName, String id, boolean isReplication) {
log("cancel " + appName + ", serverId " + id + ", isReplication " + isReplication);
publishEvent(new EurekaInstanceCanceledEvent(this, appName, id, isReplication));
}
private void handleRegistration(InstanceInfo info, int leaseDuration,
boolean isReplication) {
log("register " + info.getAppName() + ", vip " + info.getVIPAddress()
+ ", leaseDuration " + leaseDuration + ", isReplication "
+ isReplication);
publishEvent(new EurekaInstanceRegisteredEvent(this, info, leaseDuration,
isReplication));
}
private void log(String message) {
if (log.isDebugEnabled()) {
log.debug(message);
}
}
private void publishEvent(ApplicationEvent applicationEvent) {
this.ctxt.publishEvent(applicationEvent);
}
private int resolveInstanceLeaseDuration(final InstanceInfo info) {
int leaseDuration = Lease.DEFAULT_DURATION_IN_SECS;
if (info.getLeaseInfo() != null && info.getLeaseInfo().getDurationInSecs() > 0) {
leaseDuration = info.getLeaseInfo().getDurationInSecs();
}
return leaseDuration;
}
}
注意:允许注册的Ip地址配置在属性文件中,YML文件配置示例,注意这个配置是我设置的。
eureka:
server:
allowed:
address: 192.168.0.1, 192.168.199.108
备注:所有框架都是有套路的,不是我们没有造框架的能力,而是我们懒,喜欢拿来主义。