服务发现和注册
Eureka
Eureka的拓扑结构如下图:
Eureka有以下几个特点:
- 服务器的自我保护机制。客户端通过“心跳”向Eureka集群保证自己的可用性,这个“心跳”的 间隔默认为90s,但实际上即使超过90s没有心跳发送Eureka服务器也不会立刻将该客户端从服务 列表信息中删除。这样设计的出发点是出于对网络故障导致的心跳发送失败的考虑,也是Eureka 优先保证可用性的体现。
- 服务器优先保证可用性。Eureka集群不存在类似Leader节点的概念,所有的节点都是平等的。 这使得Eureka集群不会由于Leader的选举导致整体不可用,并且当客户端与其中部分的节点之间 存在网络故障时,可以从其他的节点中获得服务信息。
- 客户端会缓存节点信息。当客户端首次从Eureka服务器得到服务列表时,会将之缓存在本地内存 当中。因此,即使在最为极端的情况下客户端和集群中所有的Eureka服务器都发生网络故障,依然 存在不会导致服务调用的失败的可能。
Eureka的设计原则是优先保证可用性,平等的节点保证了客户端可以向任意的服务器进行注册和 心跳的发送,自我保护机制使得客户端可以最大程度得到“可能可用”的服务列表。
Zookeeper
Zookeeper的拓扑结构如下图:
Zookeeper提供分布式协同服务,也能作为分布式的服务注册中心使用。而Zookeeper作为服务注册 中心则有一下特点:
- Leader选举产生集群整体的不可用。当Zookeeper集群中Leader节点故障时,即使客户端与其他 节点能够正常通信也无法获得任何已被注册服务的信息。
- 临时节点在客户端和服务器的会话结束时会立刻被删除。因此产生网络分区导致心跳无法发送时, 服务的信息会立刻从Zookeeper中被删除。
由于Zookeeper优先一致性的设计原则,在作为服务注册中心时容易产生由网络分区产生的服务 不可用的情况,因此,有观点提出Zookeeper或许并不适合作为服务注册中心使用。
服务调用
Ribbon
Ribbon能够将服务名通过服务发现转为实际的ip:port
形式,并且会在转换的过程中使用特定
的负载均衡算法决定调用的服务器。
Ribbon的负载均衡算法有:
- 轮循策略
- 随机策略
- 权重策略。根据响应时间决定调用的服务器
- 最小连接数策略。
- 重试策略
- 可用性敏感策略
- 区域敏感策略
也可以通过实现IRule
接口的方式自定义负载均衡算法。配置负载均衡算法的方式有2种:
- 全局配置。将
IRule
的Bean定义的配置类放置于主启动方法可以扫描的路径下 - 指定特定服务的负载均衡算法。将
IRule
的Bean定义的配置类放置在主启动方法无法扫描 的路径下,并通过@RibbonClient
指定服务名以及IRule
所在的配置类
Spring Cloud Ribbon集成方式:
- 引入依赖
spring-cloud-starter-netflix-ribbon
(实际Eureka本身就会引入Ribbon的依赖) - 定义
RestTemplate
的Bean,并使用@LoadBalanced
修饰定义方法 - 使用
http://{service-name}
的方式调用服务接口
OpenFeign
OpenFeign用于实现声明市的HTTP Client,相较于Ribbon中直接使用RestTemplate
而言
更加方便。
Spring Cloud OpenFeign集成方式:
- 引入依赖
spring-cloud-starter-openfeign
- 开启声明式HTTP客户端
@EnableFeignClients
- 定义接口并使用
@FeignClient
修饰
|
|
断路器
Hystrix
Hystrix的基本原理如下图所示:
Hystrix的基本原理是通过将需要隔离的逻辑放入指定的线程池中运行,从而达到隔离的目的。
举个例子,假设a()
方法被@HystrixCommand
注解修饰,那么在b()
方法中调用a()
时,
就会通过特别为a()
准备的线程池执行a()
的逻辑,这样b()
的线程就能够监控a()
的
执行结果,无论是超时、异常等情况都能够被检测并纳入统计。
Spring Cloud中Hystrix的集成方式:
- 引入依赖
spring-cloud-starter-netflix-hystrix
- 开启断路器
@EnableCircuitBreaker
- 使用
@HystrixCommand
修饰需要监控的方法
统一网关
Gateway
统一网关位处于外部客户端以及内部系统之间,它的主要作用有:
- 统一鉴权。对客户端传入的请求的参数进行鉴权处理,如果不通过则直接返回失败
- 统一监控。对所有访问内网的请求进行监控和记录等操作
- 请求路由。将客户端提供的URL转为内部服务器调用的URL
Gateway类似于Eureka Server是一个单独的服务程序,不需要写业务相关代码,但需要配置 相关的路由规则以及请求的过滤器。为此,Gateway提供了3个主要概念:
- 断言。判断客户端Request是否能被某个路由规则转发
- 路由。通过客户端提供的URL计算出内部可以调用的服务URL
- 过滤器。处理客户端提供的Reuqest以及内部服务返回的Response,可以用于日志记录以及 统一鉴权
Gateway的启用方式:
- 引入依赖
spring-cloud-starter-gateway
- 按需求实现
GlobalFilter
接口引入自定义的过滤器 - 配置相应的路由规则,示例如下
|
|
Gateway作为统一网关有以下特点:
- 使用Java语言编写,因此与Spring生态统一使用学习成本相对较低
- 提供的机制能够实现复杂的网关功能
- 性能而言,比Nginx低,但使用了高性能网络框架Netty,因此性能上要高于Zuul
- 默认的工作方式是代码/配置的硬编码的方式,而不是通过Web页面的配置方式,因此更新路由 规则需要重启服务
统一配置
Config + Bus
Config的主要思想是通过git管理配置文件的版本,而Config服务器能够通过HTTP URL的方式 访问存在于git服务器的特定分支的配置文件。
- 引入依赖
spring-cloud-config-server
- 使用
@EnableConfigServer
注解开启配置服务器 - 通过配置文件指定远程配置文件所在的git仓库地址
|
|
可通过REST风格的接口直接从Config服务器获取到git上的配置文件,获取的URL按照特定 的规则有3种:
/{label}/{name}-{profile}.yml
/{name}-{profile}.yml
/{name}/{profile}/{label}
对于想要引入可刷新配置的服务提供者,需要执行以下步骤:
- 引入依赖
spring-cloud-starter-config
- 在引入可刷新依赖的Bean的类上使用
@RefreshScope
修饰 - 服务的配置文件名称需要改为
bootstrap.yml
(可能是父子容器),并进行以下配置
|
|
通过POST调用需要刷新配置的服务提供者的http://ip:port/actuator/refresh
接口
- 引入依赖
spring-cloud-starter-bus-amqp
- 在配置文件中新增RabbitMQ配置并暴露
bus-refresh
接口
|
|
客户端需要进行以下配置:
- 引入
spring-cloud-starter-bus-amqp
- 配置文件
bootstrap.yml
中新增相应的RabbitMQ配置
|
|
刷新配置的方式均为调用Config服务器的特定接口:
- 全局刷新
POST http://host:port/actuator/bus-refresh
- 定点刷新
POST http://host:port/actuator/bus-refresh/{application}:{port}
消息驱动
Alibaba
Nacos
Nacos能够同时提供服务注册以及统一配置中心的功能,Nacos集群官方推荐的拓扑是由虚拟IP 以及负载均衡的服务器作为统一入口的,这么做的好处在于Nacos集群的拓展可以不更改客户端 的配置信息。其拓扑如下:
Nacos由官方提供专门的服务器二进制包,运行之后可以通过页面进行所有功能的配置。
Nacos Discovery
- 引入依赖
spring-cloud-starter-nacos-discovery
- 使用
@EnableDiscoveryClient
开启服务发现客户端 - 在配置文件中配置Nacos服务器的地址
|
|
Nacos作为服务注册中心有以下特点:
- 支持Ribbon
- AP和CP可切换
Nacos Config
- 引入依赖
spring-cloud-starter-nacos-config
- 在配置文件中引入Nacos相关服务器的配置
|
|
Nacos通过使用(namespace, group, dataId)
坐标唯一确定一个配置文件,在实际开发中
可以通过命名空间(namespace)以及分组(group)进行分割。其中,
dataId
是由多个部分拼合而成,规则为{prefix}-{spring-profiles-active}.{file-extension}
,
规则中所有的字段都需要与配置文件一致。
Sentinel
同样是服务的熔断与降级,Sentinel采用的方案为动态配置,也就是通过页面对服务中 的各个资源进行降级/限流/熔断的规则配置,并通过其他服务(如Nacos)保存配置的规则。
Sentinel的集成方法:
- 引入依赖
spring-cloud-starter-alibaba-sentinel
- 在配置文件中引入以下配置
|
|
Sentinel的@SentinelResource
提供了对规则异常的处理以及对普通运行时异常的处理。这里
有一个问题,即对普通运行时异常的处理和Spring本身的@RestControllerAdvisor
之间的冲突。
实验结果为:
- 使用
@RestControllerAdvisor
能够触发异常的降级策略 - 同时使用
@SentinelResource
的fallback
属性与@RestControllerAdvisor
优先触发前者
由此,可以使用@RestControllerAdvisor
对整个应用的异常进行统一处理,并结合fallback
对需要特殊处理方式的方法进行额外处理。
Sentinel也能够和Hystrix一样开启Feign的服务降级,开启方法也是在feign
的配置中开启相应
的降级。
|
|
使用方式与Hystrix的降级一致,即通过实现@FeignClient
修饰的接口。
Sentinel也提供了持久化方案,也就是能够将在Dashboard中进行的所有配置保存在其他的服务器 中。在使用Spring Cloud Alibaba时,最为推荐的是使用同为Alibaba套件的Nacos,开启步骤如下:
- 引入依赖
sentinel-datasource-nacos
- 在
spring.cloud.sentinel
配置中新增以下内容
|
|
Seata
在图中,存在以下几个概念:
TC
. 事务协调器TM
. 事务管理器RM
. 资源管理器
对于整个分布式事务而言,首先需要事务的开启者通过TM
向TC
注册一个全局事务,并
从TC
得到一个全局事务ID,这个ID将在整个调用链的上下文中传递。而涉及到几个
分支事务,则分别携带全局事务ID向TC
注册自己,在完成自己相应的操作之后告知TC
可以提交。最终,由TC
控制并决定提交/回滚事务。当然,这只是大致流程,实际上通过
TC
控制全部事务本身就十分困难。
Seata的集成方法:
- 引入依赖
seata-all
- 通过
@GlobalTransactional
的注解修饰需要开启分布式事务的方法
总结
本文是Spring Cloud的一些凌乱介绍,来自于我个人在学习网课时的一些记录,仅仅只涉及 了一部分粗浅的内容。
Spring Cloud的组件也代表了目前一般的微服务所需要的基本功能,包括了服务发现/注册、 服务调用、服务熔断、统一配置、统一网关和分布式事务。
服务发现/注册、统一配置和分布式事务需要中心服务器进行协调,而为了保证高可用性, 中心服务器往往是由集群所构成。因此,学习是需要考虑2点:
- 客户端和服务器之间的通信,这部分代表了组件功能的设计思路
- 集群内部各节点的构成方案,这部分关系到集群内部的请求处理逻辑,也关系到了集群 的可用性与一致性问题
服务调用与服务熔断则更加偏向编码,因此学习时更加偏向于如何使用以及如何拓展,例如
Ribbon中的IRule
路由规则以及服务熔断的中的降级规则等。