基础
https://blog.csdn.net/qq_41520636/article/details/115668271
Hystrix 降级
Hystix 降级:当服务发生异常或调用超时,返回默认数据
项目结构
hystrix-provider(服务提供方)
1、定义降级方法
2、使用 @HystrixCommand 注解配置降级方法
3、在启动类上开启Hystrix功能:@EnableCircuitBreaker
pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-parent</artifactId>
<groupId>com.hikktn</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>hystrix-provider</artifactId>
<dependencies>
<!--spring boot web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- eureka-client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
</dependencies>
</project>
application.yml
server:
port: 8001
eureka:
instance:
hostname: localhost # 主机名
prefer-ip-address: true # 将当前实例的ip注册到eureka server 中。默认是false 注册主机名
ip-address: 127.0.0.1 # 设置当前实例的ip
instance-id: ${eureka.instance.ip-address}:${spring.application.name}:${server.port} # 设置web控制台显示的 实例id
client:
service-url:
defaultZone: http://localhost:8761/eureka
spring:
application:
name: hystrix-provider # 设置当前应用的名称。将来会在eureka中Application显示。将来需要使用该名称来获取路径
bean
package com.hikktn.entity;
import java.io.Serializable;
/**
* @ClassName Response
* @Description TODO
* @Author lisonglin
* @Date 2021/4/16 13:20
* @Version 1.0
*/
public class AbstractResult<T> implements Serializable {
private int id;
private int code;
private String error;
private T data;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public AbstractResult() {
}
public AbstractResult(int id, int code, String error, T data) {
this.id = id;
this.code = code;
this.error = error;
this.data = data;
}
@Override
public String toString() {
return "AbstractResult{" + "id=" + id + ", code=" + code + ", error='" + error + '\'' + ", data=" + data + '}';
}
}
package com.hikktn.entity;
import java.io.Serializable;
/**
* @ClassName Goods
* @Description 商品实体类
* @Author lisonglin
* @Date 2021/4/12 14:40
* @Version 1.0
*/
public class Goods implements Serializable {
private int id;
// 商品标题
private String title;
// 商品价格
private double price;
// 商品库存
private int count;
public Goods() {
}
public Goods(int id, String title, double price, int count) {
this.id = id;
this.title = title;
this.price = price;
this.count = count;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
@Override
public String toString() {
return "Goods{" + "id=" + id + ", title='" + title + '\'' + ", price=" + price + ", count=" + count + '}';
}
}
dao
package com.hikktn.dao;
import com.hikktn.entity.Goods;
import org.springframework.stereotype.Repository;
/**
* @ClassName GoodsDao
* @Description TODO
* @Author lisonglin
* @Date 2021/4/12 14:42
* @Version 1.0
*/
@Repository
public class GoodsDao {
public Goods findOne(int id){
// 这里是从数据库中查询得出
return new Goods(1,"小米手机",1999.0,50);
}
}
service
package com.hikktn.service;
import com.hikktn.dao.GoodsDao;
import com.hikktn.entity.Goods;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @ClassName GoodsService
* @Description TODO
* @Author lisonglin
* @Date 2021/4/12 14:44
* @Version 1.0
*/
@Service
public class GoodsService {
@Autowired
private GoodsDao goodsDao;
public Goods findOne(int id){
return goodsDao.findOne(id);
}
}
控制器
package com.hikktn.controller;
import com.hikktn.entity.AbstractResult;
import com.hikktn.entity.Goods;
import com.hikktn.service.GoodsService;
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @ClassName GoodsController
* @Description 服务的提供方
* @Author lisonglin
* @Date 2021/4/12 14:45
* @Version 1.0
*/
@RestController
@RequestMapping("/goods")
public class GoodsController {
@Autowired
private GoodsService goodsService;
@Value("${server.port}")
private int port;
@GetMapping("/findOne/{id}")
@HystrixCommand(fallbackMethod = "defaultFallback",commandProperties = {
//设置Hystrix的超时时间,默认1s
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "3000")
})
public AbstractResult<Goods> findOne(@PathVariable("id") int id){
Goods goods = goodsService.findOne(id);
//1.造个异常
// int i = 3/0;
// //当前线程睡2秒
// try {
// Thread.sleep(2000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
goods.setTitle(goods.getTitle() + ":" + port);//将端口号,设置到了 商品标题上
AbstractResult<Goods> result = new AbstractResult<Goods>(1, 200, "服务端正常访问", goods);
return result;
}
/**
* 定义降级方法:
* 1. 方法的返回值需要和原方法一样
* 2. 方法的参数需要和原方法一样
*/
public AbstractResult<Goods> defaultFallback(@PathVariable("id") int id){
AbstractResult<Goods> result = new AbstractResult<Goods>(id, 500, "服务端降级了~~~", null);
return result;
}
}
启动类
package com.hikktn;
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;
/**
* @ClassName ProviderApp
* @Description TODO
* @Author lisonglin
* @Date 2021/4/12 14:37
* @Version 1.0
*/
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker // 开启Hystrix功能
public class HystrixProviderApp {
public static void main(String[] args){
SpringApplication.run(HystrixProviderApp.class,args);
}
}
测试
正常情况
降级情况
将GoodsController.java类中的
放开
hystrix-consumer(服务消费方)
1、定义feign调用接口实现类,复写方法,此方法为降级方法。
2、在@FeignClient 注解中使用 fallback 属性设置降级处理类。
3、配置开启 feign.hystrix.enabled = true
pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-parent</artifactId>
<groupId>com.hikktn</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>hystrix-consumer</artifactId>
<dependencies>
<!--spring boot web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- eureka-client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
</project>
application.yml
server:
port: 9000
eureka:
instance:
hostname: localhost # 主机名
# 是否将自己的ip注册到eureka中
prefer-ip-address: true
# 设置当前实例ip
ip-address: 127.0.0.1
# 设置web控制台显示的实例id
instance-id: ${eureka.instance.ip-address}:${spring.application.name}:${server.port}:@project.version@
client:
service-url:
# eureka 服务端地址,客户端使用该地址和enreka创建连接
defaultZone: http://localhost:8761/eureka
spring:
application:
name: hystrix-consumer # 设置当前应用的名称。将来会在eureka中Application显示。将来需要使用该名称来获取路径
# 开启feign对hystrix的支持
feign:
hystrix:
enabled: true
# 配置的方式设置Ribbon的负载均衡策略
HYSTRIX-PROVIDER: # 设置的服务提供方的 应用名称
ribbon:
NFloadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 策略类
ConnectTimeout: 1000 # 连接超时时间 默认1s
ReadTimeout: 3000 # 逻辑处理的超时时间 默认1s
# 设置当前的日志级别 debug,feign只支持记录debug级别的日志
logging:
level:
com.hikktn: debug
配置类
package com.hikktn.config;
import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FeignLogConfig {
/*
NONE,不记录
BASIC,记录基本的请求行,响应状态码数据
HEADERS,记录基本的请求行,响应状态码数据,记录响应头信息
FULL;记录完成的请求 响应数据
*/
@Bean
public Logger.Level level(){
return Logger.Level.FULL;
}
}
bean
package com.hikktn.entity;
import java.io.Serializable;
/**
* @ClassName Response
* @Description TODO
* @Author lisonglin
* @Date 2021/4/16 13:20
* @Version 1.0
*/
public class AbstractResult<T> implements Serializable {
private int id;
private int code;
private String error;
private T data;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
public AbstractResult(int id, int code, String error, T data) {
this.id = id;
this.code = code;
this.error = error;
this.data = data;
}
public AbstractResult() {
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
@Override
public String toString() {
return "AbstractResult{" + "id=" + id + ", code=" + code + ", error='" + error + '\'' + ", data=" + data + '}';
}
}
package com.hikktn.entity;
import java.io.Serializable;
/**
* @ClassName Goods
* @Description 商品实体类
* @Author lisonglin
* @Date 2021/4/12 14:40
* @Version 1.0
*/
public class Goods implements Serializable {
private int id;
// 商品标题
private String title;
// 商品价格
private double price;
// 商品库存
private int count;
public Goods() {
}
public Goods(int id, String title, double price, int count) {
this.id = id;
this.title = title;
this.price = price;
this.count = count;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
@Override
public String toString() {
return "Goods{" + "id=" + id + ", title='" + title + '\'' + ", price=" + price + ", count=" + count + '}';
}
}
接口
package com.hikktn.service;
import com.hikktn.config.FeignLogConfig;
import com.hikktn.entity.AbstractResult;
import com.hikktn.entity.Goods;
import com.hikktn.service.impl.GoodsFeignImpl;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* @ClassName GoodsFeign
* @Description TODO
* @Author lisonglin
* @Date 2021/4/16 1:54
* @Version 1.0
*/
@FeignClient(value = "HYSTRIX-PROVIDER",configuration = FeignLogConfig.class,fallback = GoodsFeignImpl.class)
public interface GoodsFeign {
@GetMapping("/goods/findOne/{id}")
public AbstractResult<Goods> findGoodsById(@PathVariable("id") int id);
}
接口实现类
package com.hikktn.service.impl;
import com.hikktn.entity.AbstractResult;
import com.hikktn.entity.Goods;
import com.hikktn.service.GoodsFeign;
import org.springframework.stereotype.Component;
/**
* @ClassName GoodsFeignImpl
* @Description TODO
* @Author lisonglin
* @Date 2021/4/16 18:14
* @Version 1.0
*/
@Component
public class GoodsFeignImpl implements GoodsFeign {
@Override
public AbstractResult<Goods> findGoodsById(int id) {
AbstractResult result = new AbstractResult();
result.setCode(500);
result.setError("客户端降级了");
return result;
}
}
控制器
package com.hikktn.controller;
import com.hikktn.entity.AbstractResult;
import com.hikktn.entity.Goods;
import com.hikktn.service.GoodsFeign;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @ClassName OrderController
* @Description 服务的消费方
* @Author lisonglin
* @Date 2021/4/12 14:51
* @Version 1.0
*/
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private GoodsFeign goodsFeign;
@GetMapping("/goods/{id}")
public AbstractResult findOne(@PathVariable("id") int id) {
AbstractResult<Goods> goods = goodsFeign.findGoodsById(id);
System.out.println("结果:"+goods);
return goods;
}
}
启动类
package com.hikktn;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* @ClassName ProviderApp
* @Description TODO
* @Author lisonglin
* @Date 2021/4/12 14:37
* @Version 1.0
*/
@SpringBootApplication
@EnableFeignClients //开启Feign的功能
public class HystrixConsumerApp {
public static void main(String[] args){
SpringApplication.run(HystrixConsumerApp.class,args);
}
}
测试
启动三个项目
- eureka-server
- hystrix-consumer
- hystrix-provider
正常情况
降级情况
将GoodsController.java类中的,因为在consumer中没有设置连接超时时间,而连接超时时间默认为1秒钟,所以当提供方等待两秒钟,消费方就已经出现错误,画面挂掉了,而后进行了降级处理。
放开