代碼使用SpringCloud版本E3
業務場景:
今天遇到一個業務場景,要求根據服務名獲取當前微服務集群中所有的對應服務實例的IP和端口,通過分析源碼推算出了寫法。
原理簡述:
如果代碼中引入了spring-cloud-netflix-core(版本1.4.4.RELEASE),則在代碼初始化的時候,會通過RibbonAutoConfiguration類創建一個SpringClientFactory的bean,通過該bean可以獲取服務實例的IP和端口列表。
代碼中的 DomainExtractingServerList對象 是 DynamicServerListLoadBalancer 類中的屬性,該屬性保存的是全量的服務實例,不過卻是私有的,所以只能通過反射來獲取了。
具體代碼:
package com.liuyx; import com.netflix.loadbalancer.DynamicServerListLoadBalancer; import com.netflix.loadbalancer.ILoadBalancer; import com.netflix.loadbalancer.Server; import com.netflix.loadbalancer.ServerList; import com.netflix.niws.loadbalancer.DiscoveryEnabledServer; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.netflix.ribbon.SpringClientFactory; import org.springframework.cloud.netflix.ribbon.eureka.DomainExtractingServerList; import org.springframework.context.ApplicationContext; import java.lang.reflect.Field; import java.util.List; /** * Created by liu.yuxiang on 2017/10/12. */ @SpringBootApplication//(exclude = {ConfigClientAutoConfiguration.class}) public class PortalZuulApplication { public static void main(String[] args) { ApplicationContext ctx = new SpringApplicationBuilder(PortalZuulApplication.class).web(true).run(args); SpringClientFactory springClientFactory = ctx.getBean(SpringClientFactory.class); ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer("[服務名]"); List<Server> servers = loadBalancer.getReachableServers(); for(Server server:servers){ //如果服務有設置zone,此處獲取的可能並不是所有的實例 System.out.println("---:"+server.getHostPort()); } DynamicServerListLoadBalancer<DiscoveryEnabledServer> dynamicServerListLoadBalancer = (DynamicServerListLoadBalancer)loadBalancer; ServerList<DiscoveryEnabledServer> serverListImpl = dynamicServerListLoadBalancer.getServerListImpl(); DomainExtractingServerList domainExtractingServerList1 = (DomainExtractingServerList) serverListImpl; try { Field field = domainExtractingServerList1.getClass().getDeclaredField("list"); field.setAccessible(true); ServerList<DiscoveryEnabledServer> list = (ServerList<DiscoveryEnabledServer>)field.get(domainExtractingServerList1); for(DiscoveryEnabledServer server:list.getUpdatedListOfServers()){ //此處獲取的是所有的實例 System.out.println("%%%:"+server.getHostPort()); } /*for(DiscoveryEnabledServer server:list.getInitialListOfServers()){ System.out.println("+++:"+server.getHostPort()); }*/ } catch (Exception e) { e.printStackTrace(); } } }
代碼中的"[服務名]"請換成你自己的。
因項目中使用了zone(即 eureka.instance.metadata-map.zone=xxx),所以只能通過反射,才能獲取真正的所有服務實例,否則只能獲取zone為xxx的服務實例。
如果還不明白,參看 區域親和