基于Eureka搭建Springcloud微服务-7.使用Hystrix实现服务降级和熔断
原创2020年5月24日大约 9 分钟约 2593 字
7.使用Hystrix实现服务降级和熔断
7.1.章节内容概述
本章节涉及主要内容有:
7.1.章节内容概述
7.2.章节内容大纲
7.3.Hystrix简介
7.4.搭建服务提供者第一个节点(Hystrix)
7.5.搭建服务提供者第二个节点(Hystrix)
7.6.搭建服务消费者(Hystrix)
7.7.测试服务降级和服务熔断(Hystrix)
具体每个小节中包含的内容可使通过下面的章节内容大纲进行查看。
7.2.章节内容大纲
7.3.Hystrix简介
Hystrix是由Netflix开源的一个服务隔离组件,通过服务隔离来避免由于依赖延迟、异常,引起资源耗尽导致系统不可用的解决方案。这说的有点儿太官方了,它的功能主要有以下三个:
服务降级
当服务调用发生异常时,快速返回一个事先设置好的值,针对系统全局稳定性考虑,消费端和服务端都可以做
服务熔断
当调用服务发生多次异常时服务会会熔断,如数据库连接故障,当故障修复时服务又会恢复到正常状态,针对服务提供端稳定性考虑
服务限流
对访问的流量进行限制
https://github.com/Netflix/Hystrix
7.4.搭建服务提供者第一个节点(Hystrix)
7.4.1.模块简介
具有服务熔断和服务降级功能的服务提供者的第一个节点,启动端口: 8003
7.4.2.模块目录结构
springcloud-provider-hystrix-cluster-node-payment8003
|-- src
| •-- main
| |-- java
| | •-- org
| | •-- openatom
| | •-- springcloud
| | |-- controller
| | | •-- PaymentHystrixController.java
| | |-- dao
| | | •-- PaymentHystrixDao.java
| | |-- service
| | | |-- impl
| | | | •-- PaymentHystrixServiceImpl.java
| | | •-- PaymentHystrixService.java
| | •-- PaymentServiceProviderHystrixClusterNode8003.java
| •-- resources
| |-- mapper
| | •-- PaymentMapper.xml
| •-- application.yml
•-- pom.xml
7.4.3.创建模块
在父工程(springcloud-eureka)中创建一个名为springcloud-provider-hystrix-cluster-node-payment8003的maven模块,注意:当前模块创建成功后,在父工程pom.xml中<modules></modules>中会自动生成有关当前模块的信息
7.4.4.编写模块pom.xml
<?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>springcloud-eureka</artifactId>
<groupId>org.openatom</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-provider-hystrix-cluster-node-payment8003</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<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>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--引入公共的工程-->
<dependency>
<groupId>org.openatom</groupId>
<artifactId>springcloud-api-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
</dependencies>
<!--热部署需要加这个-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
<configuration>
<fork>true</fork>
<addResources>true</addResources>
</configuration>
</plugin>
</plugins>
<!--打包多环境-->
<resources>
<resource>
<directory>src/main/resources/</directory>
<includes>
<!--不区分环境:直接加载application.yml配置文件-->
<include>application.yml</include>
<!--不区分环境:直接加载mapper下*.xml配置文件-->
<include>mapper/*.xml</include>
</includes>
</resource>
</resources>
</build>
</project>
7.4.5.编写模块application.yml
server:
port: 8003 #访问端口
spring:
application:
name: SPRINGCLOUD-PROVIDER-HYSTRIX-PAYMENT-SERVICE-CLUSTER #注意:服务名不要出现_
devtools: #热部署开关
restart:
enabled: true
logging:
level: info
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型
driver-class-name: com.mysql.cj.jdbc.Driver # mysql驱动包
url: jdbc:mysql://192.168.0.5:3306/payment?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: Mysql123456_
eureka:
client:
register-with-eureka: true #表示是否将自己注册进EurekaServer默认为true。
fetchRegistry: true #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
service-url:
#单机版
defaultZone: http://localhost:7001/eureka
#集群版
#defaultZone: http://eureka7002:7002/eureka,http://eureka7003:7003/eureka,http://eureka7004:7004/eureka
instance:
instance-id: SPRINGCLOUD-PROVIDER-PAYMENT-SERVICE-CLUSTER-NODE-PAYMENT8003 #Eureka仪表盘中Instances currently registered with Eureka.Status显示的内容
prefer-ip-address: true #访问路径可以显示IP地址,点击Eureka仪表盘中Instances currently registered with Eureka.Status显示的内容地址栏是否显示IP地址
lease-renewal-interval-in-seconds: 30 #Eureka客户端向服务端发送心跳的时间间隔,单位为秒(默认是30秒)
lease-expiration-duration-in-seconds: 90 #Eureka服务端在收到最后一次心跳后等待时间上限,单位为秒(默认是90秒),超时将剔除服务
mybatis:
mapperLocations: classpath:mapper/*.xml
type-aliases-package: org.openatom.springcloud.entities # 所有Entity别名类所在包
7.4.6.编写模块Mybatis配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="org.openatom.springcloud.dao.PaymentHystrixDao">
<!--第一种写法:parameterType不写全路径-->
<insert id="create" parameterType="Payment" useGeneratedKeys="true" keyProperty="id">
insert into payment(serial) values(#{serial});
</insert>
<resultMap id="BaseResultMap" type="Payment">
<id column="id" property="id" jdbcType="BIGINT"/>
<id column="serial" property="serial" jdbcType="VARCHAR"/>
</resultMap>
<select id="getPaymentById" parameterType="Long" resultMap="BaseResultMap">
select * from payment where id=#{id};
</select>
<!--第二种写法:parameterType的写法与第一种有区别-->
<!--
<insert id="create" parameterType="org.openatom.springcloud.entities.Payment" useGeneratedKeys="true" keyProperty="id">
insert into payment(serial) values(#{serial});
</insert>
<resultMap id="BaseResultMap" type="org.openatom.springcloud.entities.Payment">
<id column="id" property="id" jdbcType="BIGINT"/>
<id column="serial" property="serial" jdbcType="VARCHAR"/>
</resultMap>
<select id="getPaymentById" parameterType="Long" resultMap="BaseResultMap">
select * from payment where id=#{id};
</select>
-->
</mapper>
7.4.7.编写模块dao
package org.openatom.springcloud.dao;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.openatom.springcloud.entities.Payment;
/**
* 用于测试Hystrix
*/
@Mapper
public interface PaymentHystrixDao {
int create(Payment payment);
Payment getPaymentById(@Param("id") Long id);
}
7.4.8.编写模块service
package org.openatom.springcloud.service;
import org.apache.ibatis.annotations.Param;
import org.openatom.springcloud.entities.Payment;
/**
* 用于测试Hystrix
*/
public interface PaymentHystrixService {
/**
* 未设置降级和熔断的方法
* @param payment
* @return
*/
int create(Payment payment);
/**
* 未设置降级和熔断的方法
* @param id
* @return
*/
Payment getPaymentByIdOk(@Param("id") Long id);
/**
* 未设置降级和熔断的方法
* @param id
* @return
*/
Payment getPaymentByIdTimeout(@Param("id") Long id);
/**
* 测试服务降级的方法
* @param id
* @return
*/
Payment getPaymentByIdUseHystrixDegradation(@Param("id") Long id);
/**
* 测试服务熔断的方法
* @param id
* @return
*/
Payment getPaymentByIdUseHystrixCircuitBreaker(@Param("id") Long id);
}
7.4.9.编写模块service实现类
package org.openatom.springcloud.service.impl;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.openatom.springcloud.dao.PaymentHystrixDao;
import org.openatom.springcloud.service.PaymentHystrixService;
import org.springframework.stereotype.Service;
import org.openatom.springcloud.entities.Payment;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
/**
* 用于测试Hystrix
*/
@Service
public class PaymentHystrixServiceImpl implements PaymentHystrixService {
@Resource
private PaymentHystrixDao paymentHystrixDao;
@Override
public int create(Payment payment) {
return paymentHystrixDao.create(payment);
}
@Override
public Payment getPaymentByIdOk(Long id) {
return paymentHystrixDao.getPaymentById(id);
}
@Override
public Payment getPaymentByIdTimeout(Long id) {
//睡眠3秒
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
return paymentHystrixDao.getPaymentById(id);
}
/**
* 1.当服务调用超时时使用Hystrix对服务进行降级
* 2.当服务调用出现异常时使用Hystrix对服务进行降级,如代码中含有 int i = 1/0;
* 下面的注解表示:该方法3000ms内没有执行完成,则认为该方法执行不成功
* 3.查看属性name值到HystrixCommandProperties这个类中去看
* @param id
* @return
*/
@HystrixCommand(fallbackMethod = "getPaymentByIdUseHystrixDegradationFallback",
commandProperties = {@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="3000")})
@Override
public Payment getPaymentByIdUseHystrixDegradation(Long id) {
//睡眠5秒
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
return paymentHystrixDao.getPaymentById(id);
}
/**
* 当方法getPaymentByIdTimeOut()执行失败时,执行下面的方法
* @param id
* @return
*/
public Payment getPaymentByIdUseHystrixDegradationFallback(Long id) {
return new Payment(id,"服务提供端:服务降级成功");
}
/**
* 当下游服务(服务提供端)发生故障时对服务下游服务(服务提供端)进行降级
* 10内请求失败,失败率为60%时熔断服务
* @param id
* @return
*/
@HystrixCommand(fallbackMethod = "getPaymentByIdUseHystrixCircuitBreakerFallback",commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled",value = "true"),// 是否开启断路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),// 请求次数
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"), // 时间窗口期
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"),// 失败率达到多少后跳闸
})
@Override
public Payment getPaymentByIdUseHystrixCircuitBreaker(Long id) {
//当ID小于0时,消费端使用不合理的参数多次调用此服务,则服务熔断
if(id<0){
throw new RuntimeException("id不能小于0");
}
return paymentHystrixDao.getPaymentById(id);
}
/**
* 当方法getPaymentByIdUseHystrixCircuitBreaker()执行失败时,执行下面的方法
* @param id
* @return
*/
public Payment getPaymentByIdUseHystrixCircuitBreakerFallback(Long id) {
return new Payment(id,"服务提供端:测试服务熔断成功");
}
}
7.4.10.编写模块controller
package org.openatom.springcloud.controller;
import lombok.extern.slf4j.Slf4j;
import org.openatom.springcloud.service.PaymentHystrixService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.*;
import org.openatom.springcloud.entities.CommonResult;
import org.openatom.springcloud.entities.Payment;
import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* 用于测试Hystrix
*/
@RestController
@Slf4j
public class PaymentHystrixController {
@Resource
private PaymentHystrixService paymentHystrixService;
@Value("${server.port}")
private String serverPort;
//从配置文件中动态获取服务名称
@Value("${spring.application.name}")
private String APPLICATION_NAME;
@Resource
private DiscoveryClient discoveryClient;
@PostMapping(value = "/provider/payment/create")
public CommonResult create(@RequestBody Payment payment) {
int result = paymentHystrixService.create(payment);
log.info("*****插入结果:"+result);
if(result > 0) {
return new CommonResult(200,"插入数据库成功,serverPort: "+serverPort,result);
}else{
return new CommonResult(444,"插入数据库失败",null);
}
}
/**
* 正常获取Payment
* @param id
* @return
*/
@GetMapping(value = "/provider/payment/ok/get/{id}")
public CommonResult<Payment> getPaymentByIdOk(@PathVariable("id") Long id) {
log.info(APPLICATION_NAME + serverPort);
Payment payment = paymentHystrixService.getPaymentByIdOk(id);
if(payment != null){
return new CommonResult(200,"查询成功,serverPort: "+serverPort,payment);
}else{
return new CommonResult(444,"没有对应记录,查询ID: "+id,null);
}
}
/**
* 延时获取Payment
* @param id
* @return
*/
@GetMapping(value = "/provider/payment/timeout/get/{id}")
public CommonResult<Payment> getPaymentByIdTimeout(@PathVariable("id") Long id) {
log.info(APPLICATION_NAME + serverPort);
Payment payment = paymentHystrixService.getPaymentByIdTimeout(id);
if(payment != null){
return new CommonResult(200,"查询成功,serverPort: "+serverPort,payment);
}else{
return new CommonResult(444,"没有对应记录,查询ID: "+id,null);
}
}
/**
* 测试服务提供端服务降级
* 访问路径:http://localhost:8003/provider/payment/degradation_in_provider/get/1
* 当大量线程访问这个接口的时候,服务调用者访问上面的接口getPaymentById()也会受到影响,因为Tomcat的线程池中的处理线程都被访问当前
* 接口的多个请求占据了,导致访问本微服务中的其他接口地址也会变得卡顿,使用Hystrix的在消费端对本微服务中的这个接口进行降级
* @param id
* @return
*/
@GetMapping(value = "/provider/payment/degradation_in_provider/get/{id}")
public CommonResult<Payment> getPaymentByIdUseHystrixDegradation(@PathVariable("id") Long id) {
log.info(APPLICATION_NAME + serverPort);
Payment payment = paymentHystrixService.getPaymentByIdUseHystrixDegradation(id);
if(payment != null){
return new CommonResult(200,"查询成功,serverPort: "+serverPort,payment);
}else{
return new CommonResult(444,"没有对应记录,查询ID: "+id,null);
}
}
/**
* 服务熔断测试方法
* @param id
* @return
*/
@GetMapping(value = "/provider/payment/circuitbreaker/get/{id}")
public CommonResult<Payment> getPaymentByIdUseHystrixCircuitBreaker(@PathVariable("id") Long id) {
log.info(APPLICATION_NAME + serverPort);
Payment payment = paymentHystrixService.getPaymentByIdUseHystrixCircuitBreaker(id);
if(payment != null){
return new CommonResult(200,"查询成功,serverPort: "+serverPort,payment);
}else{
return new CommonResult(444,"没有对应记录,查询ID: "+id,null);
}
}
}
7.4.11.编写模块主启动类
package org.openatom.springcloud;
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;
/**
* 支付接口提供者
* 使用Eureka作为注册中心
*/
@EnableEurekaClient
@SpringBootApplication
@EnableCircuitBreaker//服务提供端启用Hystrix
public class PaymentServiceProviderHystrixClusterNode8003 {
public static void main(String[] args) {
SpringApplication.run(PaymentServiceProviderHystrixClusterNode8003.class, args);
}
}
7.5.搭建服务提供者第二个节点(Hystrix)
7.5.1.模块简介
具有服务熔断和服务降级功能的服务提供者的第二个节点,启动端口: 8004
7.5.2.模块目录结构
springcloud-provider-hystrix-cluster-node-payment8004
|-- src
| •-- main
| |-- java
| | •-- org
| | •-- openatom
| | •-- springcloud
| | |-- controller
| | | •-- PaymentHystrixController.java
| | |-- dao
| | | •-- PaymentHystrixDao.java
| | |-- service
| | | |-- impl
| | | | •-- PaymentHystrixServiceImpl.java
| | | •-- PaymentHystrixService.java
| | •-- PaymentServiceProviderHystrixClusterNode8004.java
| •-- resources
| |-- mapper
| | •-- PaymentMapper.xml
| •-- application.yml
•-- pom.xml
7.5.3.创建模块
在父工程(springcloud-eureka)中创建一个名为springcloud-provider-hystrix-cluster-node-payment8004的maven模块,注意:当前模块创建成功后,在父工程pom.xml中<modules></modules>中会自动生成有关当前模块的信息
7.5.4.编写模块pom.xml
<?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>springcloud-eureka</artifactId>
<groupId>org.openatom</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-provider-hystrix-cluster-node-payment8004</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<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>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--引入公共的工程-->
<dependency>
<groupId>org.openatom</groupId>
<artifactId>springcloud-api-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
</dependencies>
<!--热部署需要加这个-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
<configuration>
<fork>true</fork>
<addResources>true</addResources>
</configuration>
</plugin>
</plugins>
<!--打包多环境-->
<resources>
<resource>
<directory>src/main/resources/</directory>
<includes>
<!--不区分环境:直接加载application.yml配置文件-->
<include>application.yml</include>
<!--不区分环境:直接加载mapper下*.xml配置文件-->
<include>mapper/*.xml</include>
</includes>
</resource>
</resources>
</build>
</project>
7.5.5.编写模块application.yml
server:
port: 8004 #访问端口
spring:
application:
name: SPRINGCLOUD-PROVIDER-HYSTRIX-PAYMENT-SERVICE-CLUSTER #注意:服务名不要出现_
devtools: #热部署开关
restart:
enabled: true
logging:
level: info
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型
driver-class-name: com.mysql.cj.jdbc.Driver # mysql驱动包
url: jdbc:mysql://192.168.0.5:3306/payment?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: Mysql123456_
eureka:
client:
register-with-eureka: true #表示是否将自己注册进EurekaServer默认为true。
fetchRegistry: true #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
service-url:
#单机版
defaultZone: http://localhost:7001/eureka
#集群版
#defaultZone: http://eureka7002:7002/eureka,http://eureka7003:7003/eureka,http://eureka7004:7004/eureka
instance:
instance-id: SPRINGCLOUD-PROVIDER-PAYMENT-SERVICE-CLUSTER-NODE-PAYMENT8004 #Eureka仪表盘中Instances currently registered with Eureka.Status显示的内容
prefer-ip-address: true #访问路径可以显示IP地址,点击Eureka仪表盘中Instances currently registered with Eureka.Status显示的内容地址栏是否显示IP地址
lease-renewal-interval-in-seconds: 30 #Eureka客户端向服务端发送心跳的时间间隔,单位为秒(默认是30秒)
lease-expiration-duration-in-seconds: 90 #Eureka服务端在收到最后一次心跳后等待时间上限,单位为秒(默认是90秒),超时将剔除服务
mybatis:
mapperLocations: classpath:mapper/*.xml
type-aliases-package: org.openatom.springcloud.entities # 所有Entity别名类所在包
7.5.6.编写模块Mybatis配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="org.openatom.springcloud.dao.PaymentHystrixDao">
<!--第一种写法:parameterType不写全路径-->
<insert id="create" parameterType="Payment" useGeneratedKeys="true" keyProperty="id">
insert into payment(serial) values(#{serial});
</insert>
<resultMap id="BaseResultMap" type="Payment">
<id column="id" property="id" jdbcType="BIGINT"/>
<id column="serial" property="serial" jdbcType="VARCHAR"/>
</resultMap>
<select id="getPaymentById" parameterType="Long" resultMap="BaseResultMap">
select * from payment where id=#{id};
</select>
<!--第二种写法:parameterType的写法与第一种有区别-->
<!--
<insert id="create" parameterType="org.openatom.springcloud.entities.Payment" useGeneratedKeys="true" keyProperty="id">
insert into payment(serial) values(#{serial});
</insert>
<resultMap id="BaseResultMap" type="org.openatom.springcloud.entities.Payment">
<id column="id" property="id" jdbcType="BIGINT"/>
<id column="serial" property="serial" jdbcType="VARCHAR"/>
</resultMap>
<select id="getPaymentById" parameterType="Long" resultMap="BaseResultMap">
select * from payment where id=#{id};
</select>
-->
</mapper>
7.5.7.编写模块dao
package org.openatom.springcloud.dao;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.openatom.springcloud.entities.Payment;
/**
* 用于测试Hystrix
*/
@Mapper
public interface PaymentHystrixDao {
int create(Payment payment);
Payment getPaymentById(@Param("id") Long id);
}
7.5.8.编写模块service
package org.openatom.springcloud.service;
import org.apache.ibatis.annotations.Param;
import org.openatom.springcloud.entities.Payment;
/**
* 用于测试Hystrix
*/
public interface PaymentHystrixService {
/**
* 未设置降级和熔断的方法
* @param payment
* @return
*/
int create(Payment payment);
/**
* 未设置降级和熔断的方法
* @param id
* @return
*/
Payment getPaymentByIdOk(@Param("id") Long id);
/**
* 未设置降级和熔断的方法
* @param id
* @return
*/
Payment getPaymentByIdTimeout(@Param("id") Long id);
/**
* 测试服务降级的方法
* @param id
* @return
*/
Payment getPaymentByIdUseHystrixDegradation(@Param("id") Long id);
/**
* 测试服务熔断的方法
* @param id
* @return
*/
Payment getPaymentByIdUseHystrixCircuitBreaker(@Param("id") Long id);
}
7.5.9.编写模块service实现类
package org.openatom.springcloud.service.impl;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.openatom.springcloud.dao.PaymentHystrixDao;
import org.openatom.springcloud.service.PaymentHystrixService;
import org.springframework.stereotype.Service;
import org.openatom.springcloud.entities.Payment;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
/**
* 用于测试Hystrix
*/
@Service
public class PaymentHystrixServiceImpl implements PaymentHystrixService {
@Resource
private PaymentHystrixDao paymentHystrixDao;
@Override
public int create(Payment payment) {
return paymentHystrixDao.create(payment);
}
@Override
public Payment getPaymentByIdOk(Long id) {
return paymentHystrixDao.getPaymentById(id);
}
@Override
public Payment getPaymentByIdTimeout(Long id) {
//睡眠3秒
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
return paymentHystrixDao.getPaymentById(id);
}
/**
* 1.当服务调用超时时使用Hystrix对服务进行降级
* 2.当服务调用出现异常时使用Hystrix对服务进行降级,如代码中含有 int i = 1/0;
* 下面的注解表示:该方法3000ms内没有执行完成,则认为该方法执行不成功
* 3.查看属性name值到HystrixCommandProperties这个类中去看
* @param id
* @return
*/
@HystrixCommand(fallbackMethod = "getPaymentByIdUseHystrixDegradationFallback",
commandProperties = {@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="3000")})
@Override
public Payment getPaymentByIdUseHystrixDegradation(Long id) {
//睡眠5秒
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
return paymentHystrixDao.getPaymentById(id);
}
/**
* 当方法getPaymentByIdTimeOut()执行失败时,执行下面的方法
* @param id
* @return
*/
public Payment getPaymentByIdUseHystrixDegradationFallback(Long id) {
return new Payment(id,"服务提供端:服务降级成功");
}
/**
* 当下游服务(服务提供端)发生故障时对服务下游服务(服务提供端)进行降级
* 10内请求失败,失败率为60%时熔断服务
* @param id
* @return
*/
@HystrixCommand(fallbackMethod = "getPaymentByIdUseHystrixCircuitBreakerFallback",commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled",value = "true"),// 是否开启断路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),// 请求次数
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"), // 时间窗口期
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"),// 失败率达到多少后跳闸
})
@Override
public Payment getPaymentByIdUseHystrixCircuitBreaker(Long id) {
//当ID小于0时,消费端使用不合理的参数多次调用此服务,则服务熔断
if(id<0){
throw new RuntimeException("id不能小于0");
}
return paymentHystrixDao.getPaymentById(id);
}
/**
* 当方法getPaymentByIdUseHystrixCircuitBreaker()执行失败时,执行下面的方法
* @param id
* @return
*/
public Payment getPaymentByIdUseHystrixCircuitBreakerFallback(Long id) {
return new Payment(id,"服务提供端:测试服务熔断成功");
}
}
7.5.10.编写模块controller
package org.openatom.springcloud.controller;
import lombok.extern.slf4j.Slf4j;
import org.openatom.springcloud.service.PaymentHystrixService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.*;
import org.openatom.springcloud.entities.CommonResult;
import org.openatom.springcloud.entities.Payment;
import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* 用于测试Hystrix
*/
@RestController
@Slf4j
public class PaymentHystrixController {
@Resource
private PaymentHystrixService paymentHystrixService;
@Value("${server.port}")
private String serverPort;
//从配置文件中动态获取服务名称
@Value("${spring.application.name}")
private String APPLICATION_NAME;
@Resource
private DiscoveryClient discoveryClient;
@PostMapping(value = "/provider/payment/create")
public CommonResult create(@RequestBody Payment payment) {
int result = paymentHystrixService.create(payment);
log.info("*****插入结果:"+result);
if(result > 0) {
return new CommonResult(200,"插入数据库成功,serverPort: "+serverPort,result);
}else{
return new CommonResult(444,"插入数据库失败",null);
}
}
/**
* 正常获取Payment
* @param id
* @return
*/
@GetMapping(value = "/provider/payment/ok/get/{id}")
public CommonResult<Payment> getPaymentByIdOk(@PathVariable("id") Long id) {
log.info(APPLICATION_NAME + serverPort);
Payment payment = paymentHystrixService.getPaymentByIdOk(id);
if(payment != null){
return new CommonResult(200,"查询成功,serverPort: "+serverPort,payment);
}else{
return new CommonResult(444,"没有对应记录,查询ID: "+id,null);
}
}
/**
* 延时获取Payment
* @param id
* @return
*/
@GetMapping(value = "/provider/payment/timeout/get/{id}")
public CommonResult<Payment> getPaymentByIdTimeout(@PathVariable("id") Long id) {
log.info(APPLICATION_NAME + serverPort);
Payment payment = paymentHystrixService.getPaymentByIdTimeout(id);
if(payment != null){
return new CommonResult(200,"查询成功,serverPort: "+serverPort,payment);
}else{
return new CommonResult(444,"没有对应记录,查询ID: "+id,null);
}
}
/**
* 测试服务提供端服务降级
* 访问路径:http://localhost:8003/provider/payment/degradation_in_provider/get/1
* 当大量线程访问这个接口的时候,服务调用者访问上面的接口getPaymentById()也会受到影响,因为Tomcat的线程池中的处理线程都被访问当前
* 接口的多个请求占据了,导致访问本微服务中的其他接口地址也会变得卡顿,使用Hystrix的在消费端对本微服务中的这个接口进行降级
* @param id
* @return
*/
@GetMapping(value = "/provider/payment/degradation_in_provider/get/{id}")
public CommonResult<Payment> getPaymentByIdUseHystrixDegradation(@PathVariable("id") Long id) {
log.info(APPLICATION_NAME + serverPort);
Payment payment = paymentHystrixService.getPaymentByIdUseHystrixDegradation(id);
if(payment != null){
return new CommonResult(200,"查询成功,serverPort: "+serverPort,payment);
}else{
return new CommonResult(444,"没有对应记录,查询ID: "+id,null);
}
}
/**
* 服务熔断测试方法
* @param id
* @return
*/
@GetMapping(value = "/provider/payment/circuitbreaker/get/{id}")
public CommonResult<Payment> getPaymentByIdUseHystrixCircuitBreaker(@PathVariable("id") Long id) {
log.info(APPLICATION_NAME + serverPort);
Payment payment = paymentHystrixService.getPaymentByIdUseHystrixCircuitBreaker(id);
if(payment != null){
return new CommonResult(200,"查询成功,serverPort: "+serverPort,payment);
}else{
return new CommonResult(444,"没有对应记录,查询ID: "+id,null);
}
}
}
7.5.11.编写模块主启动类
package org.openatom.springcloud;
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;
/**
* 支付接口提供者
* 使用Eureka作为注册中心
*/
@EnableEurekaClient
@SpringBootApplication
@EnableCircuitBreaker//服务提供端启用Hystrix
public class PaymentServiceProviderHystrixClusterNode8004 {
public static void main(String[] args) {
SpringApplication.run(PaymentServiceProviderHystrixClusterNode8004.class, args);
}
}
7.6.搭建服务消费者(Hystrix)
7.6.1.模块简介
具有服务熔断和服务降级功能的服务消费者,启动端口: 80
7.6.2.模块目录结构
springcloud-consumer-hystrix-loadbalance-openfeign-configuration-order80
|-- src
| •-- main
| |-- java
| | •-- org
| | •-- openatom
| | •-- springcloud
| | |-- config
| | | •-- FeignConfig.java
| | |-- controller
| | | •-- OrderConsumerHystrixController.java
| | |-- service
| | | |-- impl
| | | | •-- PaymentServiceHystrixOpenFeignImpl.java
| | | •-- PaymentServiceHystrixOpenFeign.java
| | •-- OrderServiceConsumerHystrixLoadBalanceOpenFeignConfiguration80.java
| •-- resources
| •-- application.yml
•-- pom.xml
7.6.3.创建模块
在父工程(springcloud-eureka)中创建一个名为springcloud-consumer-hystrix-loadbalance-openfeign-configuration-order80的maven模块,注意:当前模块创建成功后,在父工程pom.xml中<modules></modules>中会自动生成有关当前模块的信息
7.6.4.编写模块pom.xml
<?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>springcloud-eureka</artifactId>
<groupId>org.openatom</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-consumer-hystrix-loadbalance-openfeign-configuration-order80</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<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>
<!--引入公共的工程-->
<dependency>
<groupId>org.openatom</groupId>
<artifactId>springcloud-api-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
</dependencies>
<!--热部署需要加这个-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
<configuration>
<fork>true</fork>
<addResources>true</addResources>
</configuration>
</plugin>
</plugins>
<!--打包多环境-->
<resources>
<resource>
<directory>src/main/resources/</directory>
<includes>
<!--不区分环境:直接加载application.yml配置文件-->
<include>application.yml</include>
</includes>
</resource>
</resources>
</build>
</project>
7.6.5.编写模块application.yml
server:
port: 80
spring:
application:
name: SPRINGCLOUD-CONSUMER-HYSTRIX-LOADBALANCE-OPENFEIGN-CONFIGURATION-ORDER80 #注意:服务名不要出现_
devtools:
restart:
enabled: true
logging: #Spring运行日志配置
level: info
eureka:
client:
register-with-eureka: true #表示是否将自己注册进EurekaServer默认为true。
fetchRegistry: true #是否从EurekaServer抓取已有的注册信息,默认为true。服务提供端是单节点无所谓,是集群必须设置为true才能配合ribbon使用负载均衡,否则报异常No instances available for SPRINGCLOUD-PROVIDER-PAYMENT-SERVICE-CLUSTER
service-url:
#单机版
defaultZone: http://localhost:7001/eureka
#集群版
#defaultZone: http://eureka7002:7002/eureka,http://eureka7003:7003/eureka,http://eureka7004:7004/eureka
instance:
instance-id: ${spring.application.name} #Eureka仪表盘中Instances currently registered with Eureka.Status显示的内容
prefer-ip-address: false #访问路径可以显示IP地址,点击Eureka仪表盘中Instances currently registered with Eureka.Status显示的内容地址栏是否显示IP地址
#通过对Ribbon的配置实现对OpenFeign的配置
SPRINGCLOUD-PROVIDER-HYSTRIX-PAYMENT-SERVICE-CLUSTER: #服务提供端名称
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #Ribbon负载均衡规则类所在的路径,自带七种规则,也可以是自定位规则的类所在的路径
#对OpenFeign进行单独配置
feign:
client:
config:
#这里填具体的服务名称(也可以填default,表示对所有服务生效)
SPRINGCLOUD-PROVIDER-HYSTRIX-PAYMENT-SERVICE-CLUSTER: #服务提供端名称
#connectTimeout和readTimeout这两个得一起配置才会生效
connectTimeout: 10000 #指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
readTimeout: 10000 #指的是建立连接后从服务器读取到可用资源所用的时间
hystrix:
enabled: true #是否全局启动hystrix
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 10000 # 设置hystrix的默认熔断超时时间为10000ms
logging: #OpenFeign增强日志配置
level:
org.openatom.springcloud.services.PaymentServiceHystrixOpenFeign: debug #OpenFeign日志以什么级别监控哪个接口
7.6.6.编写模块config
package org.openatom.springcloud.config;
import feign.Logger;
import feign.Retryer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.TimeUnit;
@Configuration
public class FeignConfig {
/**
* NONE:默认的,不显示任何日志;
* BASIC:仅记录请求方法、URL、响应状态码及执行时间;
* HEADERS:除了BASIC中定义的信息之外,还有请求和响应的头信息;
* FULL:除了HEADERS中定义的信息之外,还有请求和响应的正文及元数据。
* @return
*/
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
7.6.7.编写模块service
package org.openatom.springcloud.service;
import org.openatom.springcloud.entities.CommonResult;
import org.openatom.springcloud.entities.Payment;
import org.openatom.springcloud.service.impl.PaymentServiceHystrixOpenFeignImpl;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
/**
* 用于测试8003这个节点中的服务熔断、服务降级功能
*/
@Component
@FeignClient(name="SPRINGCLOUD-PROVIDER-HYSTRIX-PAYMENT-SERVICE-CLUSTER",fallback = PaymentServiceHystrixOpenFeignImpl.class)
public interface PaymentServiceHystrixOpenFeign {
@PostMapping(value = "/provider/payment/create")
CommonResult create(@RequestBody Payment payment);
@GetMapping(value = "/provider/payment/ok/get/{id}")
CommonResult<Payment> getPaymentByIdOk(@PathVariable("id") Long id);
@GetMapping(value = "/provider/payment/timeout/get/{id}")
CommonResult<Payment> getPaymentByIdTimeout(@PathVariable("id") Long id);
/**
* 测试服务降级
* @FeignClient中:fallback = PaymentServiceHystrixOpenFeign.class
* @param id
* @return
*/
@GetMapping(value = "/provider/payment/degradation_in_provider/get/{id}")
CommonResult<Payment> getPaymentByIdUseHystrixDegradation(@PathVariable("id") Long id);
/**
* 测试服务熔断
* @param id
* @return
*/
@GetMapping(value = "/provider/payment/circuitbreaker/get/{id}")
CommonResult<Payment> getPaymentByIdOkHystrixCircuitBreaker(@PathVariable("id") Long id);
}
7.6.8.编写模块service实现类
package org.openatom.springcloud.service.impl;
import org.openatom.springcloud.entities.CommonResult;
import org.openatom.springcloud.entities.Payment;
import org.openatom.springcloud.service.PaymentServiceHystrixOpenFeign;
import org.springframework.stereotype.Component;
@Component
public class PaymentServiceHystrixOpenFeignImpl implements PaymentServiceHystrixOpenFeign {
//不用这个测试方法,所以代码不做修改
@Override
public CommonResult create(Payment payment) {
return null;
}
//不用这个测试方法,所以代码不做修改
@Override
public CommonResult<Payment> getPaymentByIdOk(Long id) {
return null;
}
//不用这个测试方法,所以代码不做修改
@Override
public CommonResult<Payment> getPaymentByIdTimeout(Long id) {
return null;
}
/**
* 测试Hystrix在Service层进行服务降级
* @param id
* @return
*/
@Override
public CommonResult<Payment> getPaymentByIdUseHystrixDegradation(Long id) {
Payment payment = new Payment(null,"服务消费端:服务提供端宕机了,在服务消费端中Service层对这个服务进行服务降级处理....");
return new CommonResult(10000,"发生了错误",payment);
}
//不用这个测试方法,所以代码不做修改
@Override
public CommonResult<Payment> getPaymentByIdOkHystrixCircuitBreaker(Long id) {
return null;
}
}
7.6.9.编写模块controller
package org.openatom.springcloud.controller;
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 lombok.extern.slf4j.Slf4j;
import org.openatom.springcloud.service.PaymentServiceHystrixOpenFeign;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.openatom.springcloud.entities.CommonResult;
import org.openatom.springcloud.entities.Payment;
import javax.annotation.Resource;
/**
* 用于测试8003这个节点中的服务熔断、服务降级功能
*/
@RestController
@Slf4j
@DefaultProperties(defaultFallback = "defaultGlobalFallback")
public class OrderConsumerHystrixController {
//单机版
// public static final String PAYMENT_URL = "http://localhost:8001";
// public static final String PAYMENT_URL = "http://localhost:8002";
@Resource
private PaymentServiceHystrixOpenFeign paymentServiceHystrixOpenFeign;
@GetMapping("/consumer/payment/create")
public CommonResult<Payment> create(Payment payment) {
return paymentServiceHystrixOpenFeign.create(payment);
}
/**
* 正常获取Payment
* 访问地址:
* http://localhost:/consumer/payment/ok/get/1
* @param id
* @return
*/
@GetMapping("/consumer/payment/ok/get/{id}")
public CommonResult<Payment> getPaymentByIdOk(@PathVariable("id") Long id) {
return paymentServiceHystrixOpenFeign.getPaymentByIdOk(id);
}
/**
* 延时获取Payment
* 访问地址:
* http://localhost/consumer/payment/timeout/get/1
* 当大量线程访问这个接口的时候,服务调用者访问上面的接口getPaymentById()也会受到影响,因为Tomcat的线程池中的处理
* 线程都被访问当前接口的多个请求占据了
* @param id
* @return
*/
@GetMapping("/consumer/payment/timeout/get/{id}")
public CommonResult<Payment> getPaymentByIdTimeout(@PathVariable("id") Long id) {
return paymentServiceHystrixOpenFeign.getPaymentByIdTimeout(id);
}
/**
* 测试服务提供端服务降级,也可以直接在访问服务提供端接口进行测试,这里是为了方便测试,直接从服务消费端发起调用
* 访问地址:
* http://localhost/consumer/payment/degradation_in_provider/get/1
* @param id
* @return
*/
@GetMapping("/consumer/payment/degradation_in_provider/get/{id}")
public CommonResult<Payment> getPaymentByIdUseHystrixDegradationInProvider(@PathVariable("id") Long id) {
return paymentServiceHystrixOpenFeign.getPaymentByIdUseHystrixDegradation(id);
}
/**
* 测试服务消费端服务降级
* 访问地址:
* http://localhost/consumer/payment/degradation_in_consumer/get/1
* 1.当服务调用超时时使用Hystrix对服务进行降级
* 2.当服务调用出现异常时使用Hystrix对服务进行降级,如代码中含有 int i = 1/0;
* 下面的注解表示:该方法2000ms内没有执行完成,则认为该方法执行不成功
* 3.查看属性name值到HystrixCommandProperties这个类中去看
* 4.注意:服务消费端可以在yml配置是否全局启用Hystrix,服务提供端不可以,因为这个配置依赖于OpenFeign这个组件
*
* @param id
* @return
*/
@HystrixCommand(fallbackMethod = "getPaymentByIdUseHystrixDegradationInConsumerFallback",
//修改value值来分别测试提供端服务降级和消费端服务降级,要测试提供端服务降级将value值设置为大于5s,要测试消费端服务降级将value设置为小于5s,理论是这样,最好是设置为1s或2秒
commandProperties = {@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="2000")})
@GetMapping("/consumer/payment/degradation_in_consumer/get/{id}")
public CommonResult<Payment> getPaymentByIdUseHystrixDegradationInConsumer(@PathVariable("id") Long id) {
return paymentServiceHystrixOpenFeign.getPaymentByIdUseHystrixDegradation(id);
}
/**
* 针对于getPaymentByIdUseHystrixDegradationInConsumer()方法的降级方法:当方法getPaymentByIdTimeOut()执行失败时,执行下面的方法
* @param id
* @return
*/
public CommonResult<Payment> getPaymentByIdUseHystrixDegradationInConsumerFallback(Long id) {
Payment payment = new Payment(id,"服务消费端:降级成功");
return new CommonResult(10000,"我是服务消费端",payment);
}
/**
* 测试全局范围内默认的降级回调方法
* 访问地址:
* http://localhost:/consumer/payment/degradation_in_consumer_default/get/1
* @param id
* @return
*/
@HystrixCommand
@GetMapping("/consumer/payment/degradation_in_consumer_default/get/{id}")
public CommonResult<Payment> getPaymentByIdOkTestDefaultGlobalCallback(@PathVariable("id") Long id) {
//模拟发生了异常
int i = 10/0;
return paymentServiceHystrixOpenFeign.getPaymentByIdOk(id);
}
/**
* 全局范围内默认的降级回调方法,只添加了@HystrixCommand,未做关于@HystrixCommand详细配置的方法失败后都会找这个方法
* 用处:可以在这里设置发生了异常后,跳转到一个统一的用户界面
* 特别注意:这个为全局服务降级兜底的方法不要有任何参数,否则会报错
* @return
*/
public CommonResult<Payment> defaultGlobalFallback() {
Payment payment = new Payment(null,"服务消费端:全局范围内默认的降级回调方法....");
return new CommonResult(10000,"我是服务消费端",payment);
}
/**
* 测试在服务提供端Service层实现服务降级
* 访问地址:
* http://localhost:/consumer/payment/degradation_in_consumer_service/get/1
* 测试在Service层实现服务降级,首先关闭8003服务,模拟8003服务宕机,访问下面的地址
* @param id
* @return
*/
@GetMapping("/consumer/payment/degradation_in_consumer_service/get/{id}")
public CommonResult<Payment> getPaymentByIdUseHystrixDegradationInConsumerService(@PathVariable("id") Long id) {
//测试在Service层进行服务降级处理
return paymentServiceHystrixOpenFeign.getPaymentByIdUseHystrixDegradation(id);
}
/**
* 测试服务熔断:
* 1.模拟发生异常熔断服务:
* http://localhost/consumer/payment/circuitbreaker/get/-1
* 2.模拟不发生异常让服务自动恢复:
* http://localhost/consumer/payment/circuitbreaker/get/1
* 测试方式:先多次访问路径1,将服务熔断,再多次访问路径2,刚开始访问依然返回的是异常信息,多次访问后可以看到服务恢复正常
* @param id
* @return
*/
@GetMapping("/consumer/payment/circuitbreaker/get/{id}")
public CommonResult<Payment> getPaymentByIdOkHystrixCircuitBreaker(@PathVariable("id") Long id) {
return paymentServiceHystrixOpenFeign.getPaymentByIdOkHystrixCircuitBreaker(id);
}
}
7.6.10.编写模块主启动类
package org.openatom.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.openfeign.EnableFeignClients;
@EnableEurekaClient
@SpringBootApplication
@EnableFeignClients
@EnableHystrix //消费者端启用Hystrix
public class OrderServiceConsumerHystrixLoadBalanceOpenFeignConfiguration80 {
public static void main(String[] args) {
SpringApplication.run(OrderServiceConsumerHystrixLoadBalanceOpenFeignConfiguration80.class, args);
}
}
7.7.测试服务降级和服务熔断(Hystrix)
启动相关服务
测试未做降级和熔断的服务
http://localhost/consumer/payment/ok/get/1
第一次访问返回结果
{"code":200,"message":"查询成功,serverPort: 8001","data":{"id":1,"serial":"15646546546"}}
第二次访问返回结果
{"code":200,"message":"查询成功,serverPort: 8002","data":{"id":1,"serial":"15646546546"}}
第三次访问返回结果
{"code":200,"message":"查询成功,serverPort: 8002","data":{"id":1,"serial":"15646546546"}}
第四次访问返回结果
{"code":200,"message":"查询成功,serverPort: 8001","data":{"id":1,"serial":"15646546546"}}
可以看到四次访问返回的结果中,四次返回结果是没有规律的,因为采用的MyRoundRobinRule(自定义策略,这个策略的效果也是随机调用),实际返回结果可能不是上面的情况,但是一定是随机进行服务调用的
测试在服务提供端对服务进行降级
在浏览器中访问
http://localhost/consumer/payment/degradation_in_provider/get/1
返回结果
{"code":200,"message":"查询成功,serverPort: 8003","data":{"id":1,"serial":"服务提供端:服务降级成功"}}
具体降级过程,请根据访问地址追踪代码,查看具体降级是如何处理的,代码中有详细的注释
测试在服务消费端对服务进行降级
在浏览器中访问
http://localhost/consumer/payment/degradation_in_consumer/get/1
返回结果
{"code":10000,"message":"我是服务消费端","data":{"id":1,"serial":"服务消费端:降级成功"}}
具体降级过程,请根据访问地址追踪代码,查看具体降级是如何处理的,代码中有详细的注释
测试全局范围内默认的降级回调方法(这种处理方式可以应用于服务提供端和服务消费端,这里演示的是在服务消费端进行处理)
在浏览器中访问
http://localhost:/consumer/payment/degradation_in_consumer_default/get/1
返回结果
{"code":10000,"message":"我是服务消费端 ","data":{"id":null,"serial":"服务消费端:全局范围内默认的降级回调方法...."}}
具体降级过程,请根据访问地址追踪代码,查看具体降级是如何处理的,代码中有详细的注释
测试在服务提供端Service层实现服务降级
本次测试较为特殊,首先关闭服务提供者8003和服务提供者8004,模拟服务提供者8003和服务提供者8004发生了宕机
在浏览器中访问
http://localhost:/consumer/payment/degradation_in_consumer_service/get/1
返回结果
{"code":10000,"message":"发生了错误","data":{"id":null,"serial":"服务消费端:服务提供者宕机了,在服务消费端中Service层对这个服务进行服务降级处理...."}}
具体降级过程,请根据访问地址追踪代码,查看具体降级是如何处理的,代码中有详细的注释
测试在服务提供端实现服务熔断
模拟发生异常熔断服务,路径1
http://localhost/consumer/payment/circuitbreaker/get/-1
模拟不发生异常让服务自动恢复,路径2
http://localhost/consumer/payment/circuitbreaker/get/1
测试方式:先多次访问路径1,将服务熔断,再多次访问路径2,刚开始访问依然返回的是异常信息,多次访问后可以看到服务恢复正常
服务熔断(下游服务发生了异常)->断路器半开(放开一定的访问流量,探测一下服务是否恢复正常)->断路器全开(放开全部访问流量)->服务恢复正常
评论