基于Eureka搭建Springcloud微服务-7.使用Hystrix实现服务降级和熔断

lingwh原创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,刚开始访问依然返回的是异常信息,多次访问后可以看到服务恢复正常

服务熔断(下游服务发生了异常)->断路器半开(放开一定的访问流量,探测一下服务是否恢复正常)->断路器全开(放开全部访问流量)->服务恢复正常
上次编辑于: 2022/9/6 00:07:50
贡献者: lingwh
评论