目 录
- 1. 熔 断
- 1.1 发生场景
- 1.2 熔断实现
- 1.3 熔断测试
- 2. 降 级
- 2.1 发生场景
- 2.2 降级处理
- 2.3 降级测试
在 上篇博客,我们完成了项目的基本搭建工作,那这篇博客就来实现一下微服务的熔断和降级。
1. 熔 断
1.1 发生场景
在前面,我们用 springcloud-service-consumer 这个服务消费者调用 springcloud-service-provider 这个服务提供者的接口,现在我们假设一种情况,如果某天服务提供者宕机了,它的服务器坏掉不能及时为服务消费者提供正常服务,那会发生什么情况?大概是服务消费者调不通接口,它的日志会一堆报错,前端页面也会报错,如果流量大,整个系统可能会发生雪崩,将正常的服务消费者的服务也压垮掉。
面对这样的情况,我们就需要做熔断处理了,将不可用的服务提供者,暂时从整个系统中断开,以保证系统其它服务的正常可用。
1.2 熔断实现
将服务提供者从系统中断开后,原来服务消费者调用服务提供者的接口,改为由服务消费者自身直接返回结果。下面,我们在 springcloud-service-consumer 这个服务消费者中具体实现一下。
第一步,引入依赖
实现熔断,需要用到一个组件 Hystrix ,由于我这里用到的 openFeign 是 3.0.2 这个版本的,它没有集成 Hystrix ,所以需要额外在 springcloud-service-consumer 的 pom.xml 文件中加入对 Hystrix 的依赖。
<!-- 增加hystrix依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.7.RELEASE</version>
</dependency>
第二步,在properties配置文件中加配置
在 application.properties 配置文件中加入配置,以开启熔断支持
#开启服务熔断支持
feign.circuitbreaker.enabled=true
第三步,在启动类加上注解
在 SpringCloudServiceConsumerApplication.java 这个启动类上,加上注解 @EnableHystrix ,开启支持。
package com.yuhuofei;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* @Description
* @ClassName SpringCloudServiceConsumerApplication
* @Author yuhuofei
* @Date 2023/5/22 18:59
* @Version 1.0
*/
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@EnableHystrix
public class SpringCloudServiceConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudServiceConsumerApplication.class, args);
}
}
第四步,新建一个接口实现类
新建一个接口实现类 FeignServiceApiImpl.java 实现接口 FeignServiceApi.java ,内容如下所示
package com.yuhuofei.api;
import com.yuhuofei.entity.PersonInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* @Description 接口回调实现类
* @ClassName FeignServiceApiImpl
* @Author yuhuofei
* @Date 2023/6/18 11:12
* @Version 1.0
*/
@Service
@Slf4j
public class FeignServiceApiImpl implements FeignServiceApi {
@Override
public List<PersonInfo> queryAllUser() {
PersonInfo personInfo = new PersonInfo();
personInfo.setId(0);
personInfo.setName("服务暂时不可用,请稍后再试");
personInfo.setPassWord("000000");
List<PersonInfo> list = new ArrayList<>();
list.add(personInfo);
log.info("调用了熔断实现类,熔断返回信息{}", list);
return list;
}
}
第五步,更改接口注解信息
将接口 FeignServiceApi 的注解 @FeignClient(value = “springcloud-service-provider”) 改为 @FeignClient(value = “springcloud-service-provider”, fallback = FeignServiceApiImpl.class)
package com.yuhuofei.api;
import com.yuhuofei.entity.PersonInfo;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.List;
/**
* @Description feign调用服务提供者的接口
* @InterfaceName FeignServiceApi
* @Author yuhuofei
* @Date 2023/5/23 19:26
* @Version 1.0
*/
@Service
@FeignClient(value = "springcloud-service-provider", fallback = FeignServiceApiImpl.class)
public interface FeignServiceApi {
//调用服务提供者的接口
@GetMapping("/provider/user/list")
List<PersonInfo> queryAllUser();
}
到这里,所有的实现步骤就完成了,下面来测试一下结果。
1.3 熔断测试
我们依次启动注册中心和服务消费者的服务,作为模拟服务提供者宕机,所以不用启动服务提供者的服务。
在浏览器中,访问 http://localhost:8002/consumer/person/list-by-openfeign ,我们可以看到,前端页面能拿到做熔断后返回的信息。
服务消费者的后端日志,也能正常打印出来,没有报错。
2. 降 级
2.1 发生场景
还是以服务消费者的接口为例,前端通过调用接口 http://localhost:8002/consumer/person/list-by-openfeign 来获取列表信息,如果这个接口的逻辑处理比较复杂或者前端传的参数出现特殊情况,导致这个接口响应很慢或者超时,而同时又有很多请求在访问这个接口,并且这个接口又是后面所有业务流程的入口,那势必会影响业务的正常开展,这是其一。其二则是在这种糟糕的情况下,不断地请求还有可能会使系统奔溃,这种时候,就得考虑对这个接口做降级处理了。
2.2 降级处理
由于前面在 springcloud-service-consumer 这个服务消费者中,做熔断的时候,已经引入了 Hystrix 的依赖、在 properties 文件中加了配置、在启动类上加了注解 @EnableHystrix
,所以这里就不需要重复做这几步了。
接下来就是改造需要做降级的接口 http://localhost:8002/consumer/person/list-by-openfeign ,如下所示。
主要是在原来的 ConsumerController.java 类中做了两步
- 第一步,新增了一个降级后的执行方法 personListFallBackMethod
- 第二步,在原来的 getPersonListUseOpenFeign 方法上,新增一个注解 @HystrixCommand(fallbackMethod = “personListFallBackMethod”,
commandProperties = {@HystrixProperty(name = “execution.isolation.thread.timeoutInMilliseconds”, value = “1500”)}),表示这个接口如果超过 1500 毫秒没有影响,则进行降级并调用降级后的方法。
package com.yuhuofei.controller;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.yuhuofei.api.FeignServiceApi;
import com.yuhuofei.entity.PersonInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.ArrayList;
import java.util.List;
/**
* @Description
* @ClassName ConsumerController
* @Author yuhuofei
* @Date 2023/5/23 18:55
* @Version 1.0
*/
@RestController
@RequestMapping("/consumer/person")
@Slf4j
public class ConsumerController {
public static final String PROVIDER_URL = "http://localhost:8001/";
@Autowired
private FeignServiceApi feignServiceApi;
@Autowired
private RestTemplate restTemplate;
@GetMapping("/list")
public List<PersonInfo> getPersonList() {
return restTemplate.getForObject(PROVIDER_URL + "provider/user/list", List.class);
}
//设置降级后回调方法,设置超时降级策略,超时时间1500毫秒
@HystrixCommand(fallbackMethod = "personListFallBackMethod",
commandProperties = {@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500")})
@GetMapping("/list-by-openfeign")
public List<PersonInfo> getPersonListUseOpenFeign() {
//为了模拟降级情况,设置线程睡眠2000毫秒
try {
Thread.sleep(2000);
} catch (Exception e) {
log.error("线程处理异常", e);
}
log.info("通过openfeign调用");
List<PersonInfo> list = feignServiceApi.queryAllUser();
log.info("得到的列表:{}",list);
return list;
}
//降级后,执行的方法
private List<PersonInfo> personListFallBackMethod() {
PersonInfo personInfo = new PersonInfo();
personInfo.setId(-1);
personInfo.setName("正常服务暂时不可用,已进行降级处理,请稍后再试");
personInfo.setPassWord("111111");
List<PersonInfo> list = new ArrayList<>();
list.add(personInfo);
log.info("调用了降级后的方法,降级后返回信息{}", list);
return list;
}
}
2.3 降级测试
一切改造工作完成后,我们依次启动注册中心、服务消费者的服务,然后在浏览器中调用 http://localhost:8002/consumer/person/list-by-openfeign 。
浏览器中,展示结果如下:
查看服务消费者的日志打印情况:
这里分析一下,先是打印线程处理异常,然后继续往下执行,打印通过openfeign调用,超时了,调用降级后要执行的方法并返回结果给前端,后端由于不是阻塞的,所以继续执行调用服务提供者的接口,调不通,最后走到了熔断类里面,流程上没问题,符合预期。
需要提一句的是,微服务的熔断和降级,无论是在服务消费者中还是服务提供者里面,都可以做都可以去实现,我这里只是以服务消费者为例进行了实现。