微服务架构演进
在单体应用时代,所有的功能(代码)耦合在一起,并且部署到同一个进程中。
随着业务的增加,单体应用面临的问题也会越来越多,例如:
- 当部分模块出现问题的时候,会影响整个应用。
- 应用功能越多,部署成本越高。
- 技术栈受限,要求所有的开发者必须使用相同的开发语言。
这种情况就像搭积木一样,积木搭的越高,整体结构就会变得越不稳定,越上面加的新积木(添加功能)或拿掉一些积木(删除功能)都非常危险。随时可能导致整个积木结构崩溃。
微服务架构,即把一个单体应用拆分成为各个子应用,各个子应用在单独的进程中。 下图是Martin Fowler(ThoughtWorks首席科学家,当今世界软件开发领域最具影响力的大师之一)在其博客上介绍的单体应用与微服务应用之间的区别:单体应用会把所有的功能放到一个进程里,扩容时相当于复制单体应用到多个服务器上。微服务架构会把每个功能单独放到一个进程里,扩容时根据需要对不同的微服务进行不同的操作。
微服务架构的优点:
- 每个微服务可以独立开发、独立运行、独立部署,可以使用任意一种开发语言。
- 每个微服务之间是独立的,如果某个服务宕机,只会影响当前服务,不会对整个业务系统产生影响。
- 不同团队维护不同的微服务,职责单一。
- 可以针对不同的微服务做不同的扩/缩容策略,不会造成资源浪费。
Spring Cloud统一服务注册/发现编程模型
Spring Cloud统一了服务注册和服务发现编程模型,其代码在spring-cloud-commons模块里
统一编程模型优点
- 无须关注底层服务注册/发现的实现细节,只需了解上层统一的抽象。
- 更换注册中心非常简单,只需要修改maven依赖和对应的注册中心配置信息。
接口 | 作用 |
|---|---|
| org.springframework.cloud.client.discovery.DiscoveryClient | 代表服务发现常见的读取操作 |
| org.springframework.cloud.client.discovery.EnableDiscoveryClient | 使用该注解表示开启服务发现功能 |
| org.springframework.cloud.client.discovery.ReactiveDiscoveryClient | 基于响应式的代表服务发现的常用读取操作 |
| org.springframework.cloud.client.serviceregistry.ServiceRegistry | 注册与注销服务的封装操作 |
| org.springframework.cloud.client.ServiceInstance | 代表服务的一个实例 |
DiscoveryClient和ReactiveDiscoveryClient
DiscoveryClient和ReactiveDiscoveryClient代表Consumer从注册中心发现Provider的服务发现操作。
public interface DiscoveryClient extends Ordered {
/**
* 默认的优先级,多个DiscoveryClient存在的情况下以优先级排序
*/
int DEFAULT_ORDER = 0;
/**
* 具体服务发现组件的描述信息,在HealthIndicator中会被用到
* @return 描述信息
*/
String description();
/**
* 根据服务名查询所有的服务实例
* @param serviceId 服务名
* @return 服务实例集合
*/
List<ServiceInstance> getInstances(String serviceId);
/**
* @return 返回注册中心所有的服务名
*/
List<String> getServices();
/**
* 具体的服务发现组件的优先级,默认为0
* @return 优先级
*/
@Override
default int getOrder() {
return DEFAULT_ORDER;
}
}
- Alibaba Nacos实现:
NacosDiscoveryClient。 - Netflix Eureka实现:
EurekaDiscoveryClient。
public interface ReactiveDiscoveryClient extends Ordered {
int DEFAULT_ORDER = 0;
String description();
Flux<ServiceInstance> getInstances(String serviceId);
Flux<String> getServices();
@Override
default int getOrder() {
return DEFAULT_ORDER;
}
}
ReactiveDiscoveryClient接口中的方法与DiscoveryClient几乎相同,只是把List类型转换成了Reactor里的Flux类型。
ServiceInstance和Registration
ServiceInstance表示客户端从注册中心获取的实例数据结构;Registration表示客户端注册到注册中心的实例数据结构。
ServiceInstance接口定义:
public interface ServiceInstance {
/**
* @return 实例ID,可以不实现,默认返回null
*/
default String getInstanceId() {
return null;
}
/**
* @return 注册的服务ID
*/
String getServiceId();
/**
* @return 服务实例的hostname
*/
String getHost();
/**
* @return 服务实例的端口
*/
int getPort();
/**
* @return 是否使用HTTPS
*/
boolean isSecure();
/**
* @return 服务实例的URI地址
*/
URI getUri();
/**
* @return 服务实例的key/value(键值对)形式对metadata信息
*/
Map<String, String> getMetadata();
/**
* @return The scheme of the service instance.
*/
default String getScheme() {
return null;
}
}
使用DiscoveryClient可以基于服务名获取到这个服务下所有的ServiceInstance集合。
Registration接口定义:
public interface Registration extends ServiceInstance {
}
这个接口继承ServiceInstance,并且没有额外的方法定义。
ServiceRegistry
ServiceRegistry用于服务信息的注册和注销,接口定义:
public interface ServiceRegistry<R extends Registration> {
/**
* 基于实例信息将其注册到注册中心
* @param registration 实例信息
*/
void register(R registration);
/**
* 基于实例信息将其从注册中心注销
* @param registration 实例信息
*/
void deregister(R registration);
/**
* 关闭ServiceRegistry,这是一个生命周期方法
*/
void close();
/**
* 设置服务实例的状态
* @param registration 服务实例
* @param status 状态
* @see org.springframework.cloud.client.serviceregistry.endpoint.ServiceRegistryEndpoint
*/
void setStatus(R registration, String status);
/**
* 获取服务实例的状态
* @param registration 服务实例
* @param <T> The type of the status.
* @return 服务实例当前状态
* @see org.springframework.cloud.client.serviceregistry.endpoint.ServiceRegistryEndpoint
*/
<T> T getStatus(R registration);
}
服务注册和注销过程:
AutoServiceRegistration是一个空接口,表示自动完成服务注册过程。接口具体的实现类(如NacosAutoServiceRegistration)会在NacosServiceRegistryAutoConfiguration自动化配置类中自动构造。
AbstractAutoServiceRegistration抽象类实现了AutoServiceRegistration接口,同时也实现了ApplicationListener接口并监听WebServerInitializedEvent。收到事件后,使用ServiceRegistry完成服务注册。应用程序关闭时触发@PreDestroy注解使用ServiceRegistry完成服务注销,这就是Spring Cloud服务信息的注册和注销时机。
Spring Cloud 2.2.0.RELEASE之后的版本通过WebServerInitializedEvent事件的监听完成服务注册已被声明为过期方法。Spring Cloud把注册时机的决定权交给了各个注册中心实现。
ServiceRegistryEndpoint
ServiceRegistryEndpoint是Spring Cloud服务注册/发现功能对外暴漏到Endpoint,其ID是service-registry,用于获取和设置服务实例的状态。获取和设置的动作由ServiceRegistry的getStatus和setStatus方法完成。
@Endpoint(id = "service-registry")
public class ServiceRegistryEndpoint {
private final ServiceRegistry serviceRegistry;
private Registration registration;
public ServiceRegistryEndpoint(ServiceRegistry<?> serviceRegistry) {
this.serviceRegistry = serviceRegistry;
}
public void setRegistration(Registration registration) {
this.registration = registration;
}
@WriteOperation
public ResponseEntity<?> setStatus(String status) {
Assert.notNull(status, "status may not by null");
if (this.registration == null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body("no registration found");
}
this.serviceRegistry.setStatus(this.registration, status);
return ResponseEntity.ok().build();
}
@ReadOperation
public ResponseEntity getStatus() {
if (this.registration == null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body("no registration found");
}
return ResponseEntity.ok()
.body(this.serviceRegistry.getStatus(this.registration));
}
}
ServiceRegistryEndpoint可以完成应用的无损下线。