云原生是一种编程方式,包含了一系列的特征,比如符合 12-factor 应用等,为了让一个应用具备云原生的架构,Spring Cloud 提供了不同的组件,但是首要的一点,是如何让分布式系统的的各个组件易于触达。为了达到这一点,Spring Boot 提供了很多的功能,而一些功能则需要 Spring Cloud 的两个依赖包提供:Spring Cloud Context 和 Spring Cloud Commons。那么本文着重介绍 Spring Cloud Commons 中的服务发现和注册接口。
根据 12-factor 应用的特性,一个应用应当绑定一个或者多个端口对外提供服务,因此要触达不同的应用,我们只需要知道他的端口号,IP或者域名,通讯协议(比如HTTP)等即可。因此Spring Cloud 将一个服务实例抽象如下:
public interface ServiceInstance {
default String getInstanceId() {return null;}
String getServiceId();
String getHost();
int getPort();
boolean isSecure();
URI getUri();
Map<String, String> getMetadata();
default String getScheme() {return null;}
}
包含的信息如下:
其中,URI getUri(),可以由接口的其他信息构建。因此对于一个客户端来说,只需要知道一个服务的实例URI或者URI列表,就可以发起调用了。
在对服务进行了抽象之后,如何获取服务,成为了第二个关键的问题。获取服务,也可以成为服务发现,定义如下:
public interface DiscoveryClient extends Ordered {
int DEFAULT_ORDER = 0;
String description();
List<ServiceInstance> getInstances(String serviceId);
List<String> getServices();
default void probe() {getServices();}
@Override
default int getOrder() {return DEFAULT_ORDER;}
}
其定义的方法也很简单,核心在两个方法。
Spring Cloud Common 依赖包,提供了一个DiscoveryClient的实现类CompositeDiscoveryClient。具体实现如下:
public class CompositeDiscoveryClient implements DiscoveryClient {
private final List<DiscoveryClient> discoveryClients;
public CompositeDiscoveryClient(List<DiscoveryClient> discoveryClients) {
AnnotationAwareOrderComparator.sort(discoveryClients);
this.discoveryClients = discoveryClients;
}
@Override
public List<ServiceInstance> getInstances(String serviceId) {
if (this.discoveryClients != null) {
for (DiscoveryClient discoveryClient : this.discoveryClients) {
List<ServiceInstance> instances = discoveryClient.getInstances(serviceId);
if (instances != null && !instances.isEmpty()) {
return instances;
}
}
}
return Collections.emptyList();
}
@Override
public List<String> getServices() {
LinkedHashSet<String> services = new LinkedHashSet<>();
if (this.discoveryClients != null) {
for (DiscoveryClient discoveryClient : this.discoveryClients) {
List<String> serviceForClient = discoveryClient.getServices();
if (serviceForClient != null) {
services.addAll(serviceForClient);
}
}
}
return new ArrayList<>(services);
}
}
CompositeDiscoveryClient 并不复杂,其代码表明,Spring Cloud 是支持多注册中心的,在一个客户端系统中,可以存在多个 DiscoveryClient 的实现类,比如可以同时存在 Eureka 和 Nacos。 通过 Spring Cloud Common 中的 spring.factories,CompositeDiscoveryClientAutoConfiguration 自动化配置类会生成 CompositeDiscoveryClient 的spring bean, 所有的实现类都会组合到 CompositeDiscoveryClient 中。因此一般来说,在实现 DiscoveryClient 的时候,只需要将实现类交给 Spring 进行托管,就会注入到 CompositeDiscoveryClient 中,而需要使用 Spring Cloud 的 DiscoveryClient 功能的时候,应该使用 CompositeDiscoveryClient 而不是其他实现类。CompositeDiscoveryClientAutoConfiguration 的实现如下:
@Configuration(proxyBeanMethods = false)
@AutoConfigureBefore(SimpleDiscoveryClientAutoConfiguration.class)
public class CompositeDiscoveryClientAutoConfiguration {
@Bean
@Primary
public CompositeDiscoveryClient compositeDiscoveryClient(List<DiscoveryClient> discoveryClients) {
return new CompositeDiscoveryClient(discoveryClients);
}
}
public interface ServiceRegistry<R extends Registration> {
void register(R registration);
void deregister(R registration);
void close();
void setStatus(R registration, String status);
<T> T getStatus(R registration);
}
public interface Registration extends ServiceInstance {
}
其中,Registration 继承了 ServiceInstance,是一个空的继承,没有任何实现。
ServiceRegistry 的功能很简单,就是将当前的服务注册到注册中心,并且可以修改状态和关闭。
至此,Spring Cloud Common 依赖包中的服务发现和注册接口已经介绍完毕。Spring Cloud 将服务注册和发现高度抽象为了3个接口:DiscoveryClient,ServiceRegistry 和 ServiceInstance,其中 ServiceInstance 代表提供服务的实例的信息,而 DiscoveryClient,ServiceRegistry 代表服务发现行为和服务注册行为。ServiceRegistry 功能较为简单,只是将自身注册到注册中心,提供相应的服务。而 DiscoveryClient 和 ServiceInstance 的使用范围更加广泛,比如客户端段的负载均衡,需要 DiscoveryClient 提供服务提供者列表,用于不同策略的服务调用。 在学习Spring Cloud 的注册中心和客户端负载均衡组件前,记忆和理解这2个接口十分重要。