聊聊EurekaRibbonClientConfiguration

895 阅读2分钟

本文主要研究一下EurekaRibbonClientConfiguration

EurekaRibbonClientConfiguration

spring-cloud-netflix-eureka-client-2.0.0.RELEASE-sources.jar!/org/springframework/cloud/netflix/ribbon/eureka/EurekaRibbonClientConfiguration.java

/**
 * Preprocessor that configures defaults for eureka-discovered ribbon clients. Such as:
 * <code>@zone</code>, NIWSServerListClassName, DeploymentContextBasedVipAddresses,
 * NFLoadBalancerRuleClassName, NIWSServerListFilterClassName and more
 *
 * @author Spencer Gibb
 * @author Dave Syer
 * @author Ryan Baxter
 */
@Configuration
public class EurekaRibbonClientConfiguration {

	private static final Log log = LogFactory.getLog(EurekaRibbonClientConfiguration.class);

	@Value("${ribbon.eureka.approximateZoneFromHostname:false}")
	private boolean approximateZoneFromHostname = false;

	@RibbonClientName
	private String serviceId = "client";

	@Autowired(required = false)
	private EurekaClientConfig clientConfig;

	@Autowired(required = false)
	private EurekaInstanceConfig eurekaConfig;

	@Autowired
	private PropertiesFactory propertiesFactory;

	public EurekaRibbonClientConfiguration() {
	}

	public EurekaRibbonClientConfiguration(EurekaClientConfig clientConfig,
			String serviceId, EurekaInstanceConfig eurekaConfig,
			boolean approximateZoneFromHostname) {
		this.clientConfig = clientConfig;
		this.serviceId = serviceId;
		this.eurekaConfig = eurekaConfig;
		this.approximateZoneFromHostname = approximateZoneFromHostname;
	}

	@Bean
	@ConditionalOnMissingBean
	public IPing ribbonPing(IClientConfig config) {
		if (this.propertiesFactory.isSet(IPing.class, serviceId)) {
			return this.propertiesFactory.get(IPing.class, config, serviceId);
		}
		NIWSDiscoveryPing ping = new NIWSDiscoveryPing();
		ping.initWithNiwsConfig(config);
		return ping;
	}

	@Bean
	@ConditionalOnMissingBean
	public ServerList<?> ribbonServerList(IClientConfig config, Provider<EurekaClient> eurekaClientProvider) {
		if (this.propertiesFactory.isSet(ServerList.class, serviceId)) {
			return this.propertiesFactory.get(ServerList.class, config, serviceId);
		}
		DiscoveryEnabledNIWSServerList discoveryServerList = new DiscoveryEnabledNIWSServerList(
				config, eurekaClientProvider);
		DomainExtractingServerList serverList = new DomainExtractingServerList(
				discoveryServerList, config, this.approximateZoneFromHostname);
		return serverList;
	}

	@Bean
	public ServerIntrospector serverIntrospector() {
		return new EurekaServerIntrospector();
	}

	@PostConstruct
	public void preprocess() {
		String zone = ConfigurationManager.getDeploymentContext()
				.getValue(ContextKey.zone);
		if (this.clientConfig != null && StringUtils.isEmpty(zone)) {
			if (this.approximateZoneFromHostname && this.eurekaConfig != null) {
				String approxZone = ZoneUtils
						.extractApproximateZone(this.eurekaConfig.getHostName(false));
				log.debug("Setting Zone To " + approxZone);
				ConfigurationManager.getDeploymentContext().setValue(ContextKey.zone,
						approxZone);
			}
			else {
				String availabilityZone = this.eurekaConfig == null ? null
						: this.eurekaConfig.getMetadataMap().get("zone");
				if (availabilityZone == null) {
					String[] zones = this.clientConfig
							.getAvailabilityZones(this.clientConfig.getRegion());
					// Pick the first one from the regions we want to connect to
					availabilityZone = zones != null && zones.length > 0 ? zones[0]
							: null;
				}
				if (availabilityZone != null) {
					// You can set this with archaius.deployment.* (maybe requires
					// custom deployment context)?
					ConfigurationManager.getDeploymentContext().setValue(ContextKey.zone,
							availabilityZone);
				}
			}
		}
		RibbonUtils.initializeRibbonDefaults(serviceId);
	}

}
  • 创建了NIWSDiscoveryPing、DomainExtractingServerList、EurekaServerIntrospector
  • 初始化之后调用RibbonUtils.initializeRibbonDefaults(serviceId)

NIWSDiscoveryPing

ribbon-eureka-2.2.5-sources.jar!/com/netflix/niws/loadbalancer/NIWSDiscoveryPing.java

/**
 * "Ping" Discovery Client
 * i.e. we dont do a real "ping". We just assume that the server is up if Discovery Client says so
 * @author stonse
 *
 */
public class NIWSDiscoveryPing extends AbstractLoadBalancerPing {
	        
		BaseLoadBalancer lb = null; 
		

		public NIWSDiscoveryPing() {
		}
		
		public BaseLoadBalancer getLb() {
			return lb;
		}

		/**
		 * Non IPing interface method - only set this if you care about the "newServers Feature"
		 * @param lb
		 */
		public void setLb(BaseLoadBalancer lb) {
			this.lb = lb;
		}

		public boolean isAlive(Server server) {
		    boolean isAlive = true;
		    if (server!=null && server instanceof DiscoveryEnabledServer){
	            DiscoveryEnabledServer dServer = (DiscoveryEnabledServer)server;	            
	            InstanceInfo instanceInfo = dServer.getInstanceInfo();
	            if (instanceInfo!=null){	                
	                InstanceStatus status = instanceInfo.getStatus();
	                if (status!=null){
	                    isAlive = status.equals(InstanceStatus.UP);
	                }
	            }
	        }
		    return isAlive;
		}

        @Override
        public void initWithNiwsConfig(
                IClientConfig clientConfig) {
        }
		
}
  • NIWSDiscoveryPing通过获取实例的eureka状态来判断是否健康

DomainExtractingServerList

spring-cloud-netflix-eureka-client-2.0.0.RELEASE-sources.jar!/org/springframework/cloud/netflix/ribbon/eureka/DomainExtractingServerList.java

public class DomainExtractingServerList implements ServerList<DiscoveryEnabledServer> {

	private ServerList<DiscoveryEnabledServer> list;
	private final RibbonProperties ribbon;

	private boolean approximateZoneFromHostname;

	public DomainExtractingServerList(ServerList<DiscoveryEnabledServer> list,
			IClientConfig clientConfig, boolean approximateZoneFromHostname) {
		this.list = list;
		this.ribbon = RibbonProperties.from(clientConfig);
		this.approximateZoneFromHostname = approximateZoneFromHostname;
	}

	@Override
	public List<DiscoveryEnabledServer> getInitialListOfServers() {
		List<DiscoveryEnabledServer> servers = setZones(this.list
				.getInitialListOfServers());
		return servers;
	}

	@Override
	public List<DiscoveryEnabledServer> getUpdatedListOfServers() {
		List<DiscoveryEnabledServer> servers = setZones(this.list
				.getUpdatedListOfServers());
		return servers;
	}

	private List<DiscoveryEnabledServer> setZones(List<DiscoveryEnabledServer> servers) {
		List<DiscoveryEnabledServer> result = new ArrayList<>();
		boolean isSecure = this.ribbon.isSecure(true);
		boolean shouldUseIpAddr = this.ribbon.isUseIPAddrForServer();
		for (DiscoveryEnabledServer server : servers) {
			result.add(new DomainExtractingServer(server, isSecure, shouldUseIpAddr,
					this.approximateZoneFromHostname));
		}
		return result;
	}

}

class DomainExtractingServer extends DiscoveryEnabledServer {

	private String id;

	@Override
	public String getId() {
		return id;
	}

	@Override
	public void setId(String id) {
		this.id = id;
	}

	public DomainExtractingServer(DiscoveryEnabledServer server, boolean useSecurePort,
			boolean useIpAddr, boolean approximateZoneFromHostname) {
		// host and port are set in super()
		super(server.getInstanceInfo(), useSecurePort, useIpAddr);
		if (server.getInstanceInfo().getMetadata().containsKey("zone")) {
			setZone(server.getInstanceInfo().getMetadata().get("zone"));
		}
		else if (approximateZoneFromHostname) {
			setZone(ZoneUtils.extractApproximateZone(server.getHost()));
		}
		else {
			setZone(server.getZone());
		}
		setId(extractId(server));
		setAlive(server.isAlive());
		setReadyToServe(server.isReadyToServe());
	}

	private String extractId(Server server) {
		if (server instanceof DiscoveryEnabledServer) {
			DiscoveryEnabledServer enabled = (DiscoveryEnabledServer) server;
			InstanceInfo instance = enabled.getInstanceInfo();
			if (instance.getMetadata().containsKey("instanceId")) {
				return instance.getHostName()+":"+instance.getMetadata().get("instanceId");
			}
		}
		return super.getId();
	}
}
  • getInitialListOfServers及getUpdatedListOfServers都调用了setZones从eureka的metadata读取zone

EurekaServerIntrospector

spring-cloud-netflix-eureka-client-2.0.0.RELEASE-sources.jar!/org/springframework/cloud/netflix/ribbon/eureka/EurekaServerIntrospector.java

public class EurekaServerIntrospector extends DefaultServerIntrospector {

	@Override
	public boolean isSecure(Server server) {
		if (server instanceof DiscoveryEnabledServer) {
			DiscoveryEnabledServer discoveryServer = (DiscoveryEnabledServer) server;
			return discoveryServer.getInstanceInfo().isPortEnabled(InstanceInfo.PortType.SECURE);
		}
		return super.isSecure(server);
	}

	@Override
	public Map<String, String> getMetadata(Server server) {
		if (server instanceof DiscoveryEnabledServer) {
			DiscoveryEnabledServer discoveryServer = (DiscoveryEnabledServer) server;
			return discoveryServer.getInstanceInfo().getMetadata();
		}
		return super.getMetadata(server);
	}
}
  • 这里从eureka的instanceInfo获取metadata

小结

EurekaRibbonClientConfiguration主要是给ribbon配置eureka相关的特性,主要是NIWSDiscoveryPing、DomainExtractingServerList、EurekaServerIntrospector这几个:

  • 其中NIWSDiscoveryPing通过eureka中instance的状态来判断是否ping的通
  • DomainExtractingServerList提供getInitialListOfServers以及getUpdatedListOfServers方法
  • EurekaServerIntrospector主要是通过instanceInfo获取metadata

doc