在学习Spring Cloud Zuul的时候在官网发现了下面的描述
Zuul starter不包括 discovery client, 所以为了实现基于service ID的路由转发,你必须同时在类路径下提供一个discovery client ( 可以使用 Eureka )
这点很重要,意味我们可以自定义服务发现客户端替代Eureka
关于服务发现的概念:https://www.jianshu.com/p/5eac16e9804a
DiscoveryClientRouteLocator 过滤器从一个DiscoveryClient(例如Eureka)和属性文件中加载了路由定义信息。
serviceId对应的路由信息从DiscoveryClient中获取。
从以上信息可知,Zuul会从DiscoveryClient中获取路由信息。而服务发现就在DiscoveryClient中实现。
在之前的实现的zuulserver项目中,pom文件中有如下依赖
<dependency>
<groupId>org.apache.servicecombgroupId>
<artifactId>spring-boot-starter-discoveryartifactId>
<version>1.1.0version>
dependency>
以上就是一个自定义discovery client。可以完全代替Eureka。这个discovery client是专门与ServiceComb的服务与发现注册中心ServiceCenter进行交互的。
服务发现有三个角色,服务提供者、服务消费者和服务中介。服务中介是联系服务提供者和服务消费者的桥梁。服务提供者将自己提供的服务地址注册到服务中介,服务消费者从服务中介那里查找自己想要的服务的地址,然后享受这个服务。服务中介提供多个服务,每个服务对应多个服务提供者。
下面看下servicecomb discovery client的目录结构,很明显这是一个自定义的SpringBoot的starter项目。源码从github上下载:https://github.com/apache/servicecomb-java-chassis
springboot的starter项目一般是做自动化配置的。那么按照starter项目的思路来分析:
先看spring.factories文件是否有org.springframework.boot.autoconfigure.EnableAutoConfiguration的配置或者@AutoConfigureBefore, @AutoConfigureAfter 注解。
发现在ScbDiscoveryClientConfiguration类中和ScbRibbonConfiguration类中分别有AutoConfigureBefore和AutoConfigureAfter注解,分别来看看这两个类做了什么。
这是一个Spring配置类,在这个类里只实例化了DiscoveryClient对象
@AutoConfigureBefore(SimpleDiscoveryClientAutoConfiguration.class)
@Configuration
public class ScbDiscoveryClientConfiguration {
@Bean
@Order(5000)
@ConditionalOnProperty(value = "servicecomb.discoveryClient.enabled", havingValue = "true", matchIfMissing = true)
public DiscoveryClient cseDiscoveryClient() {
return new ScbDiscoveryClient();
}
}
代码如上,AutoConfigureBefore顾名思义,会在某个配置完成后自动执行当前配置。
这个配置类只实例化了一个DiscoveryClient对象。重点来了,这个DiscoveryClient对象就是给Zuul使用的!!!
最终会被Zuul的 DiscoveryClientRouteLocator 过滤器 用来加载路由定义信息。
这是个Spring配置类,这个类是为了导入配置类RibbonAutoConfiguration.class
@Configuration
@EnableConfigurationProperties
@ConditionalOnBean(SpringClientFactory.class)
@AutoConfigureAfter(RibbonAutoConfiguration.class)
@RibbonClients(defaultConfiguration = ScbRibbonClientConfiguration.class)
public class ScbRibbonConfiguration {
}
代码如上,没有任何实现,注意看@AutoConfigureAfter内指定的是RibbonAutoConfiguration类,所以会在Ribbon初始化配置执行当前类,再看另一个注解@RibbonClients ,指定了ScbRibbonClientConfiguration类来配置RibbonClient。下面看这个类。
这个类是Spring配置类,负责实例化Ribbon定制相关Bean
public class ScbRibbonClientConfiguration {
@Bean
public ServerList<Server> ribbonServerList(
IClientConfig config) {
ServiceCombServerList serverList = new ServiceCombServerList();
serverList.initWithNiwsConfig(config);
return serverList;
}
}
代码如上, ServerList是Ribbon框架的东西(Ribbon是客户端负载均衡框架)。ServerList是存储服务实例的对象。
这个类实例化了一个ServiceCombServerList 对象。接着看下这个类。
这个类是给Ribbon使用,负责返回服务实例信息。
重点在下面这个方法,这个方法会去真正获取服务实例。最终返回的是可用微服务实例的真实ip地址和端口。
DiscoveryTree是ServiceComb的Registry包的,这个包是负责服务注册的。
...........
private DiscoveryTree discoveryTree = new DiscoveryTree();
@Override
public List<Server> getInitialListOfServers() {
DiscoveryContext context = new DiscoveryContext();
context.setInputParameters(serviceId);
//使用 ServiceComb的Registry包的 DiscoveryTree获取服务实例信息
VersionedCache serversVersionedCache = discoveryTree.discovery(context,
RegistryUtils.getAppId(),
serviceId,
DefinitionConst.VERSION_RULE_ALL);
return serversVersionedCache.data();
}
...........
DiscoveryTree的逻辑比较复杂,可以通过下面的处理流程了解其处理过程。参考官方文档
调用链太复杂,不详细展开分析,如下图,这个图是反的,最顶部的是最终调用的方法
直接看最终调用处:
在这个方法内直接调用ServiceCenter的rest接口Const.REGISTRY_API.MICROSERVICE_INSTANCES来获取相应的微服务实例信息 (http://127.0.0.1:30100/v4/default/registry/instances)
.......省略
@Override
public MicroserviceInstances findServiceInstances(String consumerId, String appId, String serviceName,
String versionRule, String revision) {
// must register self first, and then invoke findServiceInstances
.......省略
RequestParam requestParam = new RequestParam().addQueryParam("appId", appId)
.addQueryParam("serviceName", serviceName)
.addQueryParam("global", "true")
.addQueryParam("version", versionRule)
.addHeader("X-ConsumerId", consumerId);
if (revision != null) {
requestParam.addQueryParam("rev", revision);
}
//调用ServiceCenter的rest接口Const.REGISTRY_API.MICROSERVICE_INSTANCES来获取相应的微服务实例信息
RestUtils.get(ipPort,
Const.REGISTRY_API.MICROSERVICE_INSTANCES,
requestParam,
syncHandlerForInstances(countDownLatch, microserviceInstances));
try {
countDownLatch.await();
if (!microserviceInstances.isNeedRefresh()) {
return microserviceInstances;
}
if (microserviceInstances.getInstancesResponse() == null) {
return null; // error
}
List<MicroserviceInstance> list = microserviceInstances.getInstancesResponse().getInstances();
if (list == null) {
microserviceInstances.getInstancesResponse().setInstances(new ArrayList<>());
}
return microserviceInstances;
} catch (Exception e) {
.......省略
}
return null;
}
.......省略
demo地址: https://blog.csdn.net/kakuls/article/details/86509736