1、微服务框架需要解决的4个问题
- 服务很多,客户端该怎么访问?(API网关)
- 这么多服务,服务之间怎么通信?(使用HTTP、RPC通信)
- 服务怎么治理?(服务注册与发现)
- 服务挂了怎么办?(熔断机制)
解决方案:
spring cloud 生态
-
Spring Cloud NetFlix 一站式解决方案(2018年开始已经停止维护,本文以此为例介绍Spring Cloud
注意: 从Spring Cloud 2020.0正式版发布后将不再支持ribbon、hystrix 和 zuul。)
api 网关 ,zuul组件
Feign --Http通信方式,同步,阻塞
服务注册与发现:Eureka
熔断机制:Hystrix
… -
Apache Dubbo Zookeeper 半自动
API网关,没有,整合第三方组件或者自己实现
Dubbo —RPC通信方式,异步,非阻塞
Zookeeper
熔断机制:没有,借助Hystrix -
Spring Cloud Alibaba 一站式解决方案
2、什么是springCloud
SpringCloud,基于SpringBoot提供了一套微服务解决方案,包括服务注册与发现,配置中心,全链路监控,服务网关,负载均衡,熔断器等组件,除了基于NetFlix的开源组件做高度抽象封装之外,还有一些选型中立的开源组件。
SpringCloud利用SpringBoot的开发便利性,巧妙地简化了分布式系统基础设施的开发,SpringCloud为开发人员提供了快速构建分布式系统的一些工具,包括配置管理,服务发现,断路器,路由,微代理,事件总线,全局锁,决策竞选,分布式会话等等,他们都可以用SpringBoot的开发风格做到一键启动和部署。
SpringBoot并没有重复造轮子,它只是将目前各家公司开发的比较成熟,经得起实际考验的服务框架组合起来通过SpringBoot风格进行再封装,屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂,易部署和易维护的分布式系统开发工具包
SpringCloud 是分布式微服务架构下的一站式解决方案,是各个微服务架构落地技术的集合体,俗称微服务全家桶。
springCloud和springBoot关系
SpringBoot专注于快速、方便的开发单个个体微服务,Springloud关注全局的服务治理框架
3、Dubbo和springCloud的区别
Dubbo | Spring | |
---|---|---|
服务注册中心 | Zookeeper | Spring Cloud Netfilx Eureka |
服务调度方式 | RPC | HTTP REST API (RestTemplate) |
服务监控 | Dubbo-monitor | Spring Boot Admin |
断路器 | 不完善 | Spring Cloud Netflix Hystrix |
服务网关 | 无 | Spring Cloud Netflix Zuul |
分布式配置 | 无 | Spring Cloud Config |
服务跟踪 | 无 | Spring Cloud Sleuth |
消息总线 | 无 | Spring Cloud Bus |
数据流 | 无 | Spring Cloud Stream |
批量任务 | 无 | Spring Cloud Task |
最大区别: SpringCloud抛弃了Dubbo的RPC通信,采用的是基于HTTP的REST方式
严格来说,这两种方式各有优劣。虽然从一定程度上来说,后者牺牲了服务调用的性能,但也避免了上面提到的原生RPC带来的问题。而且REST相比RPC更为灵活,服务提供方和调用方的依赖只依靠一纸契约,不存在代码级别的强依赖,这在强调快速演化的微服务环境下,显得更加合适。
RPC 主要是基于 TCP/IP 协议的,而 HTTP 服务主要是基于 HTTP 协议的
HTTP 协议是在传输层协议 TCP 之上的,所以效率来看的话,RPC 当然是要更胜一筹啦!
4、服务注册中心
4.1、CAP理论
CAP理论是分布式架构中重要理论:
-
一致性(Consistency) (所有节点在同一时间具有相同的数据)
-
可用性(Availability) (保证每个请求不管成功或者失败都有响应)
-
分隔容忍(Partition tolerance) (系统中任意信息的丢失或失败不会影响系统的继续运作)
关于 P 的理解,我觉得是在整个系统中某个部分,挂掉了,或者宕机了,并不影响整个系统的运作或者说使用,而可用性是,某个系统的某个节点挂了,但是并不影响系统的接受或者发出请求。
CAP 不可能都取,只能取其中2个,原因:
1、如果C是第一需求的话,那么会影响A的性能,因为要数据同步,不然请求结果会有差异,但是数据同步会消耗时间,期间可用性就会降低。
2、如果A是第一需求,那么只要有一个服务在,就能正常接受请求,但是对与返回结果变不能保证,原因是,在分布式部署的时候,数据一致的过程不可能想切线路那么快。
4.2、目前流行的注册中心产品对比
Nacos | Eureka | Consul | Zookeeper | |
---|---|---|---|---|
一致性协议(设计理论) | CP+AP | AP | CP | CP |
健康检查 | TCP/HTTP/MYSQL/Client Beat | Client Beat | TCP/HTTP/gRPC/Cmd | Keep Alive |
负载均衡策略 | 权重/metadata/Selector | Ribbon | Fabio | 无 |
雪崩保护 | 有 | 有 | 无 | 无 |
自动注销实例 | 支持 | 支持 | 支持 | 支持 |
访问协议 | HTTP/DNS | HTTP | HTTP/DNS | TCP |
监听支持 | 支持 | 支持 | 支持 | 支持 |
多数据中心 | 支持 | 支持 | 支持 | 不支持 |
跨注册中心同步 | 支持 | 不支持 | 支持 | 不支持 |
SpringCloud集成 | 支持 | 支持 | 支持 | 支持 |
Dubbo集成 | 支持 | 不支持 | 支持 | 支持 |
K8S集成 | 支持 | 不支持 | 支持 | 不支持 |
4.3、Eureka
Eureka是Netflix的一个子模块,遵循AP原则。Eureka是一个基于REST的服务,用于定位服务,以实现云端中间层服务发现和故障转移,服务注册与发现对于微服务来说是非常重要的,有了服务发现与注册,只需要使用服务的标识符,就可以访问到服务,而不需要修改服务调用的配置文件了,功能类似于Dubbo的注册中心,比如Zookeeper;
Eureka的基本架构:
- SpringCloud 封装了NetFlix公司开发的Eureka模块来实现服务注册和发现 (对比Zookeeper)。
- Eureka采用了C/S的架构设计,EurekaServer 作为服务注册功能的服务器,他是服务注册中心。
- 而系统中的其他微服务。使用Eureka的客户端连接到EurekaServer并维持心跳连接。这样系统的维护人员就可以通过EurekaServer来监控系统中各个微服务是否正常运行,SpringCloud的一些其他模块 (比如Zuul)就可以通过EurekaServer来发现系统中的其他微服务,并执行相关的逻辑。
- Eureka 包含两个组件: Eureka Server 和 Eureka Client
- Eureka Server提供服务注册服务,各个节点启动后,会在EurekaServer中进行注册,这样Eureka Server0中的服务注册表中将会村粗所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。
- Eureka Client是一个java客户端,用于简化EurekaServer的交互,客户端同时也具备一个内置的,使用轮询负载算法的负载均衡器。在应用启动后,将会向EurekaServer发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除掉 (自动保护机制,默认周期为90秒)。
1、创建Eureka Server
1.1、导入Eureka Server的依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- spring热部署工具 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<version>3.0.3</version>
</dependency>
1.2、编写配置
server:
port: 8081
#Eureka配置
eureka:
instance:
hostname: localhost #Eureka服务端的实例名称
client:
register-with-eureka: false # 表示是否eureka注册中心注册自己
fetch-registry: false #fetch-registry如果为false,则表示自己为注册中心
service-url: # 监控页面~
defaultZone: http://$(eureka.instance.hostname):$(server.port)/eureka/
1.3、在启动类上添加@EnableEurekaServer,开启Eureka的服务注册。
2、创建服务并注册到中心注册
1.1、导入Eureka的依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
1.2、编写配置
server:
port: 7001
#spring的配置
spring:
application:
name: springcloud-provider-dept #服务名称
#Eureka配置,服务注册到哪里
eureka:
client:
service-url:
defaultZone: http://localhost:8081/eureka/
instance:
instance-id: springcloud-provider-dept7001 # 修改eureka 上的默认浙述信息
1.3、在启动类上添加注解@EnableEurekaClient,启动后自动向注册中心注册。
添加注解@EnableDiscoveryClient,服务发现,具有拉取服务信息功能
3、集群环境配置
注册中心集群配置
server:
port: 8081
#Eureka配置
eureka:
instance:
hostname: localhost #Eureka服务端的实例名称
client:
register-with-eureka: false # 表示是否eureka注册中心注册自己
fetch-registry: false #fetch-registry如果为false,则表示自己为注册中心
service-url: # 监控页面~
# 单机:defaultZone: http://$(eureka.instance.hostname):$(server.port)/eureka/
# 集群 (关联其他的注册中心):
defaultZone: http://eureka8082.com:8082/eureka/,http://eureka8083.com:8083/eureka/
服务端配置
server:
port: 7001
#spring的配置
spring:
application:
name: springcloud-provider-dept #服务名称
#Eureka配置,服务注册到哪里
eureka:
client:
service-url:
# 向多个注册中心注册
defaultZone: http://localhost:8081/eureka/,http://eureka8082.com:8082/eureka/,http://eureka8083.com:8083/eureka/
instance:
instance-id: springcloud-provider-dept7001 # 修改eureka 上的默认浙述信息
5、负载均衡(Ribbon)
5.1、负载均衡是什么?
- LB,即负载均衡(Load Balance) ,在微服务或分布式集群中经常用的一种应用。
- 负载均衡简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA (高可用)。
- 常见的负载均衡软件有 Nginx,Lvs 等等
- dubbo、SpringCloud中均给我们提供了负载均衡,SpringCloud的负载均衡算法可以自定义
负载均衡简单分类: - 集中式LB
- 即在服务的消费方和提供方之间使用独立的LB设施,如Nginx: 反向代理服务器,由该设施负责把访问请求通过某种策略转发至服务的提供方!
- 进程式LB
- 将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选出一个合适的服务器。Ribbon就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址!
5.2、ribbon是什么?
Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。简单的说,Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将NetFlix的中间层服务连接在一起。Ribbon的客户端组件提供一系列完整的配置项如: 连接超时、重试等等。简单的说,就是在配置文件中列出LoadBalancer (简称LB: 负载均衡)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等等)去连接这些机器。我们也很容易使用Ribbon实现自定义的负载均衡算法!
消费端集成ribbon实现负载均衡
1、导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
2、消费端配置
server:
port: 80
#Eureka配置
eureka:
client:
register-with-eureka: false # 表示不向eureka注册中心注册自己
service-url:
defaultZone: http://eureka8081.com:8081/eureka/,http://eureka8082.com:8082/eureka/,http://eureka8083.com:8083/eureka/
3、在启动类上添加注解@EnableEurekaClient
4、配置负载均衡
//配置负载均衡实现restTemplate
//IRule接口的实现类实现负载均衡的算法
//RoundRobinRule: 轮询
//RandomRule: 随机
//AvailabilityFilteringRule: 会先过滤访问故障的服务。对剩下的服务进行轮询
//RetryRule: 会先按照轮询获取服务~,如果服务获取失败,则会在指定的时间内进行重试
@Bean
@LoadBalanced //Ribbon
pubic RestTemplate getRestTemplate() {
return new RestTemplate;
}
5、通过服务名访问服务提供端
@RestController
public class DeptController
{
private static final String REST_URL_PREFIX = "http://MICROSERVICECLOUD-DEPT"; //微服务的虚拟id(即服务提供者spring.application.name属性的值)
@Autowired
private RestTemplate restTemplate;
@RequestMapping(value = "/consumer/dept/add")
public boolean add(Dept dept)
{
return restTemplate.postForObject(REST_URL_PREFIX + "/dept/add", dept, Boolean.class);
}
}
自定义负载均衡算法
在springboot主程序扫描的包外定义配置类,然后为springboot主程序添加 @RibbonClient 注解引入配置类。
//在启动类上加上该注解,加载自定义的Ribbon类,
@RibbonClient(name = "custom", configuration = Ribbon.class)
注意: Ribbon类必须是@Configuration类,但请注意,对于主应用程序上下文,它不在@ComponentScan中。否则,它由所有@RibbonClients共享。如果您使用@ComponentScan(或@SpringBootApplication),则需要采取措施避免将其包括在内(例如,可以将其放在单独的,不重叠的程序包中,或指定要在@ComponentScan)。
Ribbon.class
@Configuration
publci class Ribbon {
@Bean
public IRule myRule() {
return new MyRule();
}
}
自定义的负载均衡算法类
package com.qzh.myRule;
import com.netflix.client.config.IClientConfig;
import java.util.List;
import com.netflix.loadbalancer.*;
public class myRule extends AbstractLoadBalancerRule {
//自定义算法规则,每个服务调用3次,换下一个服务
private int total = 0; //被调用次数
private int currentIndex = 0;//当前提供服务对象
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
} else {
Server server = null;
while(server == null) {
if (Thread.interrupted()) {
return null;
}
List<Server> upList = lb.getReachableServers();//获得所有正常运行的服务
List<Server> allList = lb.getAllServers();//获得所有的服务
int serverCount = allList.size();
if (serverCount == 0) {
return null;
}
//int index = this.rand.nextInt(serverCount);//生成随机数
//server = (Server)upList.get(index);//从活着的服务中随机获取服务
//负载均衡算法体
if (total < 3) {
server = upList.get(currentIndex);
total++;
} else {
total = 0;
currentIndex++;
if (currentIndex >= upList.size()) {
currentIndex = 0;
}
server = upList.get(currentIndex);
}
if (server == null) {
Thread.yield();
} else {
if (server.isAlive()) {
return server;
}
server = null;
Thread.yield();
}
}
return server;
}
}
public Server choose(Object key) {
return this.choose(this.getLoadBalancer(), key);
}
public void initWithNiwsConfig(IClientConfig clientConfig) {
}
}
5.2、Feign实现负载均衡
feign是声明式的web service客户端,它让微服务之间的调用变得更简单了,类似controller调用service。SpringCloud集成了Ribbon和Eureka,可在使用Feign时提供负载均衡的http客户端。
只需要创建一个接口,然后添加注解即可!
feign,主要是社区,大家都习惯面向接口编程。这个是很多开发人员的规范。调用微服务访问两种方法:
- 微服务名字I[ribbon]
- 接口和注解 [feign ]
Feign的使用步骤
1、添加Feign的依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
2、编写配置
#Eureka配置
eureka:
client:
register-with-eureka: false # 表示不向eureka注册中心注册自己
service-url:
defaultZone: http://eureka8081.com:8081/eureka/,http://eureka8082.com:8082/eureka/,http://eureka8083.com:8083/eureka/
3、在启动类上添加@EnableFeignClients
4、创建远程调用接口,添加注解@FeignClient
@FeignClient("PRODUCT-PROVIDER")//PRODUCT-PROVIDER远程调用服务名称
public interface ProductService {
@GetMapping("/product/{id}")//与远程调用服务的接口请求地址及参数相同
Product getProduct(@PathVariable Long id);
}
5、使用步骤4创建的接口,与使用普通的service接口相同
6、熔断机制
服务雪崩
多个微服务之间调用的时候,比如A服务调用B服务,B服务调用C服务,…,形成一条调用链,若链路上某个服务调用响应时间过长或不可用,对微服务A的调用就会占据越来越多的资源,进而引起系统的崩溃,即“雪崩效应”。
在高流量应用中,单一的后端依赖可能导致系统资源在几秒内饱和,比失败更糟糕的是,这些服务调用之间可能发生延迟增加、备份队列、线程和其他系统资源紧张,从而导致整个应用程序发生级联故障,系统崩溃。为了保证在单个依赖失败的时候,不引起整个应用程序崩溃,我们必须"弃车保帅",采用熔断机制对应用的故障和延迟进行隔离和管理。
服务熔断
现代微服务架构都是分布式的,由非常多的服务组成。不同服务之间相互调用,组成复杂的调用链路。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用。复杂链路上的某一环不稳定,就可能会层层级联,最终导致整个链路都不可用。因此我们需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。熔断降级作为保护自身的手段,通常在客户端(调用端)进行配置。
使用场景:
1、 服务故障或者升级时,让客户端快速失败
2、失败处理逻辑容易定义
3、响应耗时较长,客户端设置的read timeout会比较长,防止客户端大量重试请求导致的连接、线程资源不能释放
服务降级
服务降级指的是当服务器压力剧增的情况下,为了保证核心功能的可用性,选择降低某些非核心功能的可用性,或直接关闭该功能。
实际业务一般服务降级都放在这种消费者端/客户端。
服务降级是从整个系统的负荷情况出发和考虑的,对某些负荷会比较高的情况,为了预防某些功能(业务场景)出现负荷过载或者响应慢的情况,在其内部暂时舍弃对一些非核心的接口和数据的请求,而直接返回一个提前准备好的 fallback 兜底处理。这样,虽然提供的是一个有损的服务,但却保证了整个系统的稳定性和可用性。
使用场景:(异常降级、调用超时降级)
1、服务处理异常,把异常信息直接反馈给客户端,不再走其他逻辑
2、服务处理异常,把请求缓存下来,给客户端返回一个中间态,事后再重试缓存的请求
3、监控系统检测到突增流量,为了避免非核心业务功能耗费系统资源,关闭这些非核心功能
4、数据库请求压力大,可以考虑返回缓存中的数据
5、对于耗时的写操作,可以改为异步写
6、暂时关闭跑批任务,以节省系统资源
服务限流
限流是从用户访问压力的角度来考虑如何应对系统故障。
限流为了对服务端的接口接受请求的频率进行限制,防止服务挂掉。比如某一接口的请求限制为 100 个每秒, 对超过限制的请求放弃处理或者放到队列中等待处理。限流可以有效应对突发请求过多。
限流方法:流量计数器、滑动时间窗口、漏桶算法、令牌桶算法、分布式限流
Hystrix(熔断器)
本节内容参考:https://blog.csdn.net/KIMTOU/article/details/125359690
熔断器本身是一种开关装置,当某个服务发生故障后,通过断路器的故障监控,向服务调用方返回一个符合预期的、可处理的降级响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常。这样就保证了服务调用方的线程不会被长时间、不必要地占用,避免故障在微服务系统中的蔓延,防止系统雪崩效应的发生。
Hystrix 是一个用于处理分布式系统的延迟和容错的开源库,提供了熔断器功能。在分布式系统里,许多依赖服务不可避免的会调用失败,比如超时、异常等。Hystrix 能够保证在一个依赖服务出现问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。Spring Cloud Hystrix 具有服务降级、服务熔断、线程隔离、请求缓存、请求合并以及实时故障监控等强大功能。
熔断机制是为了应对雪崩效应而出现的一种微服务链路保护机制。
在熔断机制中涉及了三种熔断状态:
- 熔断关闭状态 (Closed) :当服务访问正常时,熔断器处于关闭状态,服务调用方可以正常地进行服务调用。
- 熔断开启状态 (Open) :默认情况下,在固定时间内接口调用出错比率达到一个阈值(例如 50%),熔断器会进入熔断开启状态。进入熔断状态后,后续对该服务的调用都会被切断,熔断器会执行本地的降级 (FallBack) 方法。
- 半熔断状态 (Half-Open) :在熔断开启一段时间之后,熔断器会进入半熔断状态。在半熔断状态下,熔断器会尝试恢复服务调用方对服务的调用,允许部分请求调用该服务,并监控其调用成功率。如果成功率达到预期,则说明服务已恢复正常,熔断器进入关闭状态;如果成功率仍旧很低,则重新进入熔断开启状态。
Hystrix 服务降级
Hystrix 服务降级 FallBack 既可以放在服务端进行,也可以放在客户端进行。
1、引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
2、服务端降级
修改业务类,使用 @HystrixCommand 与 @HystrixProperty 注解。
@Service
public class PaymentHystrixService {
//一旦该方法失败并抛出了异常信息后,会自动调用@HystrixCommand注解标注的fallbackMethod指定的方法
@HystrixCommand(fallbackMethod = "paymentInfoHystrix", commandProperties = {
//规定3秒钟以内就不报错,正常运行,超过3秒就报错
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",
value = "3000")
})
public String paymentInfo(Integer id) {
int timeout = 5;
//int i = 1 / 0; //直接报错也可触发降级
TimeUnit.SECONDS.sleep(timeout);
return "线程池: " + Thread.currentThread().getName() +
", paymentInfo_TimeOut, id = " + id + ", 耗时" + timeout + "秒";
}
public String paymentInfoHystrix(Integer id) {
return "线程池: " + Thread.currentThread().getName() +
", paymentInfo_TimeOutHandler, id = " + id + "系统繁忙或运行报错,请稍后重试";
}
}
主启动类激活熔断器
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
@EnableCircuitBreaker //激活熔断器功能
public class PaymentApplication {
3、客户端降级
application.yml 中添加以下配置,开启客户端的 Hystrix 功能
#开启Hystrix熔断器功能
feign:
hystrix:
enabled: true
主启动类启用 Hystrix
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients //开启 OpenFeign 功能
@EnableHystrix //启用 Hystrix
public class OrderFeignApplication {
Feign 调用降级。
@RestController
@RequestMapping("/order")
public class OrderFeignHystrixController {
@Resource
private PaymentFeignService paymentFeignService;
@GetMapping("/hystrix/timeout/{id}")
//一旦该方法失败并抛出了异常信息后,会自动调用@HystrixCommand注解标注的fallbackMethod指定的方法
@HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod",commandProperties = {
//规定使用feign调用服务,1.5秒以内返回就不报错,正常运行;超过1.5秒就报错,调用兜底降级方法
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",
value = "1500")
})
public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
//支付服务处理业务需要3秒,虽然支付服务规定在5秒内正常,但客户端规定1.5秒内返回,故会报错
return paymentFeignService.paymentFeignTimeout();
}
public String paymentTimeOutFallbackMethod(Integer id) {
return "我是订单消费者80,对方支付系统繁忙,请稍后重试";
}
}
4、全局服务降级
除了个别重要核心业务有专属降级方法,其它普通的可以通过 @DefaultProperties(defaultFallback = “”) 统一跳转到统一处理结果页面。通用的和独享的各自分开,避免了代码膨胀,合理减少了代码量。
在类名上标注 @DefaultProperties 注解,并通过其 defaultFallback 属性指定一个全局的降级方法。
@RestController
@RequestMapping("/order")
@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod") //指定全局降级方法
public class OrderFeignHystrixController {
@Resource
private PaymentFeignService paymentFeignService;
@GetMapping("/hystrix/timeout/{id}")
@HystrixCommand //使用全局降级方法,不加该注解则不使用降级方法
public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
int i = 1 / 0;
return paymentFeignService.paymentFeignTimeout();
}
//全局降级方法
public String payment_Global_FallbackMethod() {
return "全局异常处理信息,请稍后再试~";
}
}
注意: 全局降级方法的优先级较低,只有业务方法没有指定其降级方法,并且加上 @HystrixCommand 注解时,服务降级时才会触发全局降级方法。若业务方法指定它自己的降级方法,那么在服务降级时,就只会直接触发它自己的降级方法,而非全局降级方法。
注意: 降级(FallBack)方法必须与其对应的业务方法在同一个类中,否则无法生效。
5、解耦降级逻辑
只需要为 Feign 客户端定义的接口添加一个服务降级处理的实现类即可实现对业务逻辑与降级逻辑的解耦
@Component
public class PaymentFallbackService implements PaymentFeignService{
@Override
public String paymentInfo_OK(Integer id) {
return "paymentInfo_OK PaymentFallbackService fallback";
}
@Override
public String paymentInfo_TimeOut(Integer id) {
return "paymentInfo_TimeOut PaymentFallbackService fallback";
}
}
@FeignClient 指定 fallback 类。
@Component
@FeignClient(value = "PROVIDER-PAYMENT", fallback = PaymentFallbackService.class)
public interface PaymentFeignService {
//整合hystrix熔断器
@GetMapping("/payment/hystrix/ok/{id}")
String paymentInfo_OK(@PathVariable("id") Integer id);
@GetMapping("/payment/hystrix/timeout/{id}")
String paymentInfo_TimeOut(@PathVariable("id") Integer id);
}
6、服务熔断
业务类配置服务熔断。
@Service
public class PaymentHystrixService {
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",
commandProperties = {
//以下参数在 HystrixCommandProperties 类中有默认配置
//是否开启熔断器
@HystrixProperty(name = "circuitBreaker.enabled", value = "true"),
//统计时间窗(默认10秒)
@HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds",
value = "1000"),
//统计时间窗内请求次数
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
//休眠时间窗口期
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",
value = "10000"),
//在统计时间窗口期以内,请求失败率达到60%时进入熔断状态
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60"),
})
public String paymentCircuitBreaker(Integer id) {
if (id < 0) {
throw new RuntimeException("id不能为负数");
}
return Thread.currentThread().getName() + "调用成功,流水号:" + UUID.randomUUID();
}
public String paymentCircuitBreaker_fallback(Integer id) {
return "请稍后再试, id = " + id;
}
}
@RestController
@RequestMapping("/payment")
public class PaymentHystrixController {
@Resource
private PaymentHystrixService paymentHystrixService;
//==========服务熔断==========
@GetMapping("/hystrix/circuit/{id}")
public String paymentCircuitBreaker(@PathVariable("id") Integer id) {
String result = paymentHystrixService.paymentCircuitBreaker(id);
log.info("result: " + result);
return result;
}
}
Hystrix 故障监控
Hystrix 还提供了准实时的调用监控(Hystrix Dashboard)功能,Hystrix 会持续地记录所有通过 Hystrix 发起的请求的执行信息,并以统计报表的形式展示给用户,包括每秒执行请求的数量、成功请求的数量和失败请求的数量等。
依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- 服务监控 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
@SpringBootApplication
@EnableHystrixDashboard //开启 Hystrix 监控功能
public class HystrixDashboardApplication {
@Configuration
public class HystrixDashboardConfig {
/**
*此配置是为了服务监控而配置,与服务容错本身无关,springcloud升级后的坑
*ServletRegistrationBean因为springboot的默认路径不是"/hystrix.stream",
*只要在自己的项目里配置上下面的servlet就可以了
*否则,Unable to connect to Command Metric Stream 404
*/
@Bean
public ServletRegistrationBean getServlet() {
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean =
new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}
}
访问地址:http://ip:port/hystrix.stream
7、路由网关(Zuul)
Zuul包含了对请求的路由和过滤两个最主要的功能:
其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础,而过滤器功能则负责对请求的处理过程进行干预,是实现请求校验,服务聚合等功能的基础。Zuul和Eureka进行整合,将Zuul自身注册为Eureka服务治理下的应用,同时从Eureka中获得其他微服务的消息,即以后的访问微服务都是通过Zuul跳转后获得。
注意: Zuul服务最终还是会注册进Eureka
提供: 代理 +路由+ 过滤三大功能!
实现:
1、导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- 服务监控 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
2、编写配置
#Eureka配置
eureka:
client:
service-url:
defaultZone: http://eureka8081.com:8081/eureka/,http://eureka8082.com:8082/eureka/,http://eureka8083.com:8083/eureka/
instance:
instance-id: zuul.com
prefer-ip-address: true #显示真实ip
zuul:
routes:
springcloud-provider-dept.serviceId: springcloud-tigong-ip #真实的服务名
springcloud-provider-dept.path: /mydept/** #网关,隐藏真实服务名
# 设置公共的前缀
prefix: /Tujiji
#忽略,不能再使用这个路径访问。 这样的话就只能使用路由网关启用,*隐藏所有
ignored-services: "*"
3、在启动类上添加注解@EnableZuulProxy,开启路由网关