Spring Cloud:
官网地址:官网地址:https://spring.io/projects/spring-cloud
概述:
Spring Cloud 是 一个全家桶式技术栈,内部包含了很多组件:
核心组件:
- Eureka(注册中心)
- Hystrix(断流器,熔断器)
- Feign
- Ribbon(负载均衡)
- Gateway(网关)Zuul
Spring Cloud Eureka
概述:
负责管理,记录服务提供者信息,服务调用则无需自己寻找服务,而是把自己的需求告诉Eureka,然后Eureka会把符合你需求的服务告诉你。
同时,服务提供方与Eureka之间通过“心跳”
机制进行监控,当某个服务提供方出现问题,Eureka自然会把它从服务列表中剔除
原理图:
eureka介绍:
- Eureka-Server:就是服务注册中心(可以是一个集群),对外暴露自己的地址。
- 提供者:启动后向Eureka注册自己信息(地址,服务名称等),并且定期进行服务续约
- 消费者:服务调用方,会定期去Eureka拉取服务列表,然后使用负载均衡算法选出一个服务进行调用。
- 心跳(续约):提供者定期通过http方式向Eureka刷新自己的状态
使用 Eureka Server 步骤:
- 导入依赖:
<!-- eureka服务端 -->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
- 在启动类上添加 @EnableEurekaServer
- 在yaml配置文件 配置eureka 地址
eureka:
client:
service-url: # 如果是集群,每个地址需要使用逗号隔开
defaultZone: http:// "EurekaServer地址" /eureka/
# 当只有一个eurekaServer 时 可使用以下配置
register-with-eureka: false # 不注册自己
fetch-registry: false #不拉取服务
服务注册:(EurekaClient)
- 导入eureka client 依赖
<!-- Eureka客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
- 在yaml配置文件配置eurekaServer 地址
eureka:
client:
service-url:
defaultZone: http:// "EurekaServer地址" /eureka/
- 手动拉取服务提供者地址
– 注入DiscoveryClient 对象
– 根据 服务id 获取服务实例列表
服务注册原理:
服务提供者在启动时,会检测配置属性中的:eureka.client.register-with-erueka=true
参数是否正确,事实上默认就是true。如果值确实为true,则会向EurekaServer发起一个Rest请求,并携带自己的元数据信息,Eureka Server会把这些信息保存到一个双层Map结构中。
一个服务,可以同时启动多个不同实例,形成集群。
通过修改yaml配置文件eureka 配置,可以自定义实例id 防止使用主机名,实例id冲突
eureka:
instance:
ip-address: 127.0.0.1 # ip地址
prefer-ip-address: true # 更倾向于使用ip,而不是host名
instance-id: ${eureka.instance.ip-address}:${server.port}
# 自定义实例的id
服务续约:
在注册服务完成以后,服务提供者会维持一个心跳(定时向EurekaServer发起Rest请求)
默认情况下每个30秒服务会向注册中心发送一次心跳,证明自己还活着。如果超过90秒没有发送心跳,EurekaServer就会认为该服务宕机,会从服务列表中移除,
- 可以通过修改yaml文件配置来修改心跳时间
eureka:
instance:
lease-expiration-duration-in-seconds: 90 # 服务失效时间,默认值90秒
lease-renewal-interval-in-seconds: 30 # 服务续约(renew)的间隔,默认为30秒
获取服务列表:
当服务消费者启动是,会检测eureka.client.fetch-registry=true
参数的值,如果为true,则会从Eureka Server服务的列表只读备份,然后缓存在本地。并且每隔30秒
会重新获取并更新数据。
eureka:
client:
registry-fetch-interval-seconds: 30 # 获取更新数据 默认间隔时间
失效剔除:
服务中心在启动时会创建定时任务,默认每隔60秒将超时(默认90秒)没有续约的服务剔除,
由于非正常关闭不会执行主动下线动作,所以才会出现失效剔除机制,该机制主要是应对非正常关闭服务的情况,如:内存溢出、杀死进程、服务器宕机等非正常流程关闭服务节点时。
可以通过eureka.server.eviction-interval-timer-in-ms
参数对其进行修改,单位是毫秒。
自我保护机制:
触发Eureka的自我保护机制。当服务未按时进行心跳续约时,Eureka会统计服务实例最近15分钟心跳续约的比例是否低于了85%。在生产环境下,因为网络延迟等原因,心跳失败实例的比例很有可能超标,但是此时就把服务剔除列表并不妥当,因为服务可能没有宕机。Eureka在这段时间内不会剔除任何服务实例,直到网络恢复正常。生产环境下这很有效,保证了大多数服务依然可用,不过也有可能获取到失败的服务实例,因此服务调用者必须做好服务的失败容错。
需要使用到容错机制,和熔断机制来进行处理;
eureka:
server:
enable-self-preservation: false # 关闭自我保护模式(缺省为打开)
Spring Cloud Hystrix
概述:
Netflix开源的一个延迟和容错库,用于隔离访问远程服务、第三方库,防止出现级联失败
作用:
- 对通过第三方客户端访问的依赖项的延迟和故障进行保护和控制。
- 在复杂的分布式系统中组织级联故障。
- 快速失败,快速恢复。
- 回退,尽可能优雅地降级
- 启用近实时监控、警报和操作控制。
复杂的分布式体系结构中,应用程序都有很过依赖项,每个依赖项在,某些时候发生不可避免的失败时,主机应用程序没有能隔离这些外部故障的失败依赖,应用程序就会被拖垮
雪崩问题:
服务之间相互调用时或者用户发送请求时,当某个服务发送故障或者宕机,那么其他服务调用故障服务,就会发生阻塞,一直得不到响应,就会一直阻塞,线程一直的不到释放,当越来越多的服务调用,用户请求到来,就会有越来越多的线程阻塞,服务器支持的线程和并发数有限,请求一直阻塞,会导致服务器资源耗尽,从而导致所有其它服务都不可用,形成雪崩效应。
可以使用 "线程隔离"和"服务熔断"方式解决雪崩问题;
使用步骤:
- 导入依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
- 启动类上添加注解
@EnableCircuitBreaker
或者@SpringCloudApplication
此注解中包含:@EnableCircuitBreake
@EnableDiscoveryClient ``@SpringBootApplication
等注解
线程隔离
用户的请求将不再直接访问服务,而是通过线程池中的空闲线程来访问服务,如果线程池已满,或者请求超时,则会进行降级处理:返回给用户一个错误提示
服务降级:
当服务请求发生故障,快速处理失败,使用服务降级,返回友好提示;
在请求方法上使用@HystixCommond(fallbackMethod = )
指定降级处理方法,然后编写降级处理逻辑
熔断的降级逻辑方法必须跟正常逻辑方法保证:相同的参数列表和返回值声明
超时设置:
hystrix 的默认请求超时时间为1s,请求一旦超时,会立马执行降级处理逻辑
hystrix:
command:
default: # 设置 hystrix 请求超时时间
execution.isolation.thread.timeoutInMilliseconds: 2000
服务熔断
尽管线程隔离可以避免出现级联失败,但是对于其他服务访问故障服务,每次访问处理请求都要等待超时,非常浪费系统资源,
服务熔断可以解决这一问题
当Hystix判断一个依赖服务失败比例较高时,就会对其做熔断处理:拦截对故障服务的请求,快速失败,不再阻塞等待
熔断器(断路器)原理:
服务调用方可以判断某些服务反应慢或者存在大量超时情况, 主动熔断,防止系统崩溃, hystrix 可以实现弹性容错,当情况好转,可以自动重连,允许部分请求通过,如果调用成功回到关闭状态,否则继续断开;
熔断器(断路器)状态:
- Closed:关闭状态(断路器关闭),所有请求都正常访问。
- Open:打开状态(断路器打开),所有请求都会被降级。Hystix会对请求情况计数,当一定时间内失败请求百分比达到阈值,则触发熔断,断路器打开。默认失败比例的阈值是50%,请求次数最少不低于20次。
- Half Open:半开状态,open状态不是永久的,打开后会进入休眠时间(默认是5S)。随后断路器会自动进入半开状态。此时会释放1次请求通过,若这个请求是健康的,则会关闭断路器,否则继续保持打开,再次进行5秒休眠计时。
Spring Cloud Ribbon
概述:
Eureka 中已经集成 Ribbon 所以不需要导入依赖
-
在spring cloud 中Ribbon负载均衡 和 Feign , Eureka 之间互相协作处理;
- 首先,ribbon会根据 服务id 从 eurekaServer注册中心获对应的所有服务实例相关信息,ip ,端口号等,
- Ribbon 使用 默认的 Round Robin (轮询算法) 服务实例中获取一个
- Feign 根据获取到的服务实例,构造http请求并发送
-
单独使用 Ribbon
// 启动类中声明 Bean
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
-----------------------------------------
// controller 层
@GetMapping("{id}")
public User queryById(@PathVariable("id") Long id){
// 这里不是直接使用ip:端口 而是使用服务id
String url = "http://user-service/user/" + id;
User user = restTemplate.getForObject(url, User.class);
return user;
}
根据服务id(user-service)去eureka拉取服务列表,返回列表后,Ribbon的负载均衡策略会从列表从选择一个实例,向实例发送请求
源码跟踪:
LoadBalancerInterceptor
,这个类会在对RestTemplate的请求进行拦截,然后从Eureka根据服务id获取服务列表,随后利用负载均衡算法得到真实的服务地址信息,替换服务id。
-
LoadBalancerInterceptor
类中方法
request.getURI()
:获取请求uri
originalUri.getHost()
:获取uri路径的主机名,就是服务id user-service
this.loadBalancer.execute()
:处理服务id,和用户请求。 -
继续跟入
this.loadBalancer.execute()
execute方法
getLoadBalancer(serviceId)
:根据服务id 获取负载均衡器(loadBalancer),然后负载均衡器会根据服务id 去Eureka中获取服务列表并保存
getServer(loadBalancer)
:利用内置负载均衡算法,从服务列表选择一个服务实例 -
底层通过实现
IRule
接口(负载均衡规则) 来指定负载均衡规则,默认使用RoundRobinRule
实现类(轮询规则)- 可以通过修改yaml配置文件来指定规则(一般使用默认规则轮询)
- 格式是:
{服务名称}.ribbon.NFLoadBalancerRuleClassName
,值就是IRule的实现类。
-
Ribbon 默认懒加载 即第一次访问时才会去创建负载均衡客户端
ribbon:
eager-load:
enabled: true # 开启饥饿加载
clients: user-service #指定服务id
Spring Cloud Gateway
概述:
流程图:
Spring Cloud Feign
概述:
是一个http请求调用的轻量级框架,可以以Java接口注解的方式调用Http请求,而不用像Java中通过封装HTTP请求报文的方式直接调用。Feign通过处理注解,将请求模板化,当实际调用的时候,传入参数,根据参数再应用到请求上,进而转化成真正的请求
使用步骤:
- 导入依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
- 在启动类上 添加@EnableFeignClients
- 定义客户端接口,使用@FeignClient注解 指定服务id
- 接口中定义的方法,使用springMVC注解,feign会根据注解自动生成url,并获取访问结果,
原理:
- 启动时,程序会进行包扫描,扫描所有包下所有@FeignClient注解的类,并将这些类注入到spring的IOC容器中。当定义的Feign中的接口被调用时,通过JDK的动态代理来生成RequestTemplate。
- RequestTemplate中包含请求的所有信息,如请求参数,请求URL等。
- RequestTemplate声明Request,然后将Request交给client处理,这个client默认是JDK的HTTPUrlConnection,也可以是OKhttp、Apache的HTTPClient等。
- 最后client封装成LoadBaLanceClient,结合ribbon负载均衡地发起调用。
负载均衡:
feign 本身集成了ribbon依赖和自动配置
不需要导入依赖,也不需要注册restTemplate
- 可以通过修改yaml文件修改配置
ribbon:
ConnectTimeout: 500 # 连接超时时长
ReadTimeout: 2000 # 数据通信超时时长
MaxAutoRetries: 0 # 当前服务器的重试次数
MaxAutoRetriesNextServer: 1 # 重试多少次服务
OkToRetryOnAllOperations: false # 是否对所有的请求方式都重试
Hystix的超时时间,应该比重试的总时间要大,
整合Hystrix:
Feign默认对Hystrix 也做了集成;默认情况下是关闭的
- 开启hystrix
feign:
hystrix:
enabled: true # 开启Feign的熔断功能
但是在Feign中Hystrix的降级处理逻辑比较复杂;
- 实现带有@FeignClient 注解的接口
- 编写降级处理逻辑
- 在 @FeignClient注解 的接口中指定实现类的字节码文件
日志级别:
- yaml文件配置日志
logging:
level:
包名: debug # 级别
- 编写配置类
@Configuration
public class FeignConfig {
@Bean
Logger.Level feignLoggerLevel(){
return Logger.Level.FULL; #设置日志级别
}
}
级别:
- NONE:不记录任何日志信息,这是默认值。
- BASIC:仅记录请求的方法,URL以及响应状态码和执行时间
- HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息
- FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。