Hystrix的使用4-和OpenFeign结合使用

落花浮王杯 提交于 2020-08-12 05:19:19

Hystrix的使用4-和OpenFeign结合使用

1.简介

OpenFeign中已经集成了Hystrix,不用再引入Hystrix依赖。

2.代码实现

2.1 父工程POM文件

  <packaging>pom</packaging>

  <modules>
    <!-- 公共工程,请看我的github -->
    <module>cloud-api-commons</module>
    <!-- Eureka注册中心 -->
    <module>cloud-eureka-server7001</module>
    <!-- 服务提供者 -->
    <module>cloud-provider-payment-hystrix8001</module>
    <!-- 服务消费者 -->
    <module>cloud-consumer-order-hystrix80</module>
  </modules>

  <!-- 统一管理jar包版本 -->
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <junit.version>4.12</junit.version>
    <log4j.version>1.2.17</log4j.version>
    <lombok.version>1.16.18</lombok.version>
    <spring.boot.version>2.2.2.RELEASE</spring.boot.version>
    <spring.cloud.version>Hoxton.SR1</spring.cloud.version>
    <hutool-all.version>5.1.0</hutool-all.version>
    <liuhangs.cloud.api.version>1.0.0-SNAPSHOT</liuhangs.cloud.api.version>
  </properties>

  <distributionManagement>
    <site>
      <id>website</id>
      <url>scp://webhost.company.com/www/website</url>
    </site>
  </distributionManagement>

  <!-- 子模块继承之后,提供作用:锁定版本 + 子module不用谢groupId和version -->
  <dependencyManagement>
    <dependencies>
      <!--spring boot 2.2.2-->
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-dependencies</artifactId>
        <version>2.2.2.RELEASE</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
      <!--spring cloud Hoxton.SR1-->
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>Hoxton.SR1</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
      <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>${lombok.version}</version>
      </dependency>
      <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>${hutool-all.version}</version>
      </dependency>
    </dependencies>
  </dependencyManagement>

2.2 服务提供者cloud-provider-payment-hystrix8001

2.2.1 服务提供者POM文件

因为我们不在服务提供者代码里测试Hystrix,所以不用引入Hystrix依赖。

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>com.liuhangs.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${liuhangs.cloud.api.version}</version>
        </dependency>
        <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.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

2.2.2 服务提供者application.yml

server:
  port: 8001

spring:
  application:
    name: cloud-provider-payment-hystrix
eureka:
  client:
    #是否注册到eureka
    register-with-eureka: true
    # 是否在本地缓存注册表信息
    fetch-registry: true
    service-url:
      # 配置eureka的地址,如果多台则逗号分隔。
      defaultZone: http://eureka7001.com:7001/eureka

2.2.3 服务提供者service

service接口:

import com.liuhangs.springcloud.api.entities.CommonResult;

/**payment服务接口
 */
public interface PaymentHystrixService {

    /**
     * 正常方法
     * @param id
     * @return
     */
    public CommonResult getPaymentOK(Long id);

    /**
     * 给80服务消费者准备的超时方法,避免和前面的服务降级方法混淆
     */
    public CommonResult getPaymentTimeOutFor80(Long id);
}

service实现类:

import com.liuhangs.springcloud.api.entities.CommonResult;
import com.liuhangs.springcloud.payment.service.PaymentHystrixService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;

/**这个类里一个需要5秒才能返回值的接口 */
@Service
@Slf4j
public class PaymentHystrixServiceImpl implements PaymentHystrixService {

    @Value("${server.port}")
    private String SERVER_PORT;

    @Override
    public CommonResult getPaymentOK(Long id)
    {
        return new CommonResult(200, "请求ID为:" + id + ", 当前线程是:" + Thread.currentThread().getName() +
                "," +
                " " +
                "服务端口:" +
                SERVER_PORT);
    }

    /**
     * 给80服务消费者准备的超时方法,避免和前面的服务降级方法混淆
     * 此方法会睡眠5秒
     * @param id
     * @return
     */
    @Override
    public CommonResult getPaymentTimeOutFor80(Long id)
    {
        try
        {
            TimeUnit.MILLISECONDS.sleep(5000);
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        return new CommonResult(200, "请求ID为:" + id + ", 当前线程是:" + Thread.currentThread().getName() + ", 服务端口:" +
                SERVER_PORT + ", 线程睡眠5秒");
    }
}

2.2.4 服务提供者controller

import com.liuhangs.springcloud.api.entities.CommonResult;
import com.liuhangs.springcloud.api.entities.Payment;
import com.liuhangs.springcloud.payment.service.PaymentHystrixService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**服务提供者对外提供的接口
 */
@RequestMapping("provider/payment/hystrix")
@RestController
public class PaymentHystrixController {

    @Autowired
    private PaymentHystrixService paymentHystrixService;

    @GetMapping("/ok/{id}")
    public CommonResult getPaymentOK(@PathVariable("id") Long id)
    {
        return paymentHystrixService.getPaymentOK(id);
    }

    /**
     * 给80服务消费者准备的超时接口
     * @param id
     * @return
     */
    @GetMapping("/timeoutfor80/{id}")
    public CommonResult getPaymentTimeOutFor80(@PathVariable("id") Long id)
    {
        return paymentHystrixService.getPaymentTimeOutFor80(id);
    }
}

2.2.4 服务提供者启动类

/**服务提供者启动类
 */
@SpringBootApplication
public class PaymentHystrixStart8001 {

    public static void main(String[] args)
    {
        SpringApplication.run(PaymentHystrixStart8001.class, args);
    }
}

2.3 Eureka注册中心cloud-eureka-server7001

2.3.1 注册中心POM文件

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <dependency>
            <groupId>com.liuhangs.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${liuhangs.cloud.api.version}</version>
        </dependency>
        <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.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

2.3.2 注册中心application.yml

server:
  port: 7001

spring:
  application:
    name: cloud-eureka-server

eureka:
  instance:
    hostname: eureka7001.com
  client:
    # 是否将当前服务注册到eureka
    registerWithEureka: false
    # 是否在本地缓存注册表信息,默认为true
    fetchRegistry: true
    serviceUrl:
      # 配置自己就是单机,配置其他eureka的地址就是集群,如果多台则逗号分隔。
      defaultZone: http://eureka7001.com:7001/eureka

2.3.3 注册中心启动类

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

/**注册中心启动类
 */
@SpringBootApplication
@EnableEurekaServer
public class EurekaStart7001 {

    public static void main(String[] args)
    {
        SpringApplication.run(EurekaStart7001.class, args);
    }
}

2.4 服务消费者cloud-consumer-order-hystrix80

2.4.1 服务消费者POM文件

    <dependencies>
    <!-- OpenFeign中已经集成了Hystrix,不用再引入Hystrix依赖。 -->
<!--        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</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-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>com.liuhangs.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${liuhangs.cloud.api.version}</version>
        </dependency>

        <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.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

2.4.2 服务消费者application.yml

需要注意:

  • OpenFeign的超时调用时间需要调大(默认为1秒),否则看不到服务降级的效果。通过ribbon.ReadTimeoutribbon.ConnectTimeout设置。
  • 服务提供者需要通过 feign.hystrix.enabled开启Hystrix降级处理。
  • 可以通过hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds设置超时降级时间(Hystrix默认一秒)。
server:
  port: 80

eureka:
  client:
    #是否注册到eureka
    register-with-eureka: true
    # 是否在本地缓存注册表信息
    fetch-registry: true
    service-url:
      # 配置eureka的地址,如果多台则逗号分隔。
      defaultZone: http://eureka7001.com:7001/eureka

#需要将接口超时的时间调大(默认1秒),否则在调用服务消费者时会直接报超时异常。
ribbon:
  #调用接口读取超时时间
  ReadTimeout:  6000
  #调用接口连接超时时间
  ConnectTimeout: 2000

#开启Hystrix降级处理
feign:
  hystrix:
      enabled: true #在Feign中开启Hystrix。如果处理自身的容错就开启。开启方式与生产端不一样。

#设置超时降级时间(spring-cloud-starter-openfeign中的HystrixCommandProperties默认为1000毫秒)
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 3000

#设置hystrix日志级别是DEBUG
logging:
  level:
   com.netflix.hystrix: DEBUG

2.4.3 服务消费者service接口

这里是使用的OpenFeign,所以需要使用接口,并且在接口上加上OpenFeign客户端注解@FeignClient
加上需要使用Hystrix进行服务降级,所以在@FeignClient注解中配置fallback属性,配置统一的服务降级处理类。这里配置的服务降级处理类为PaymentHystrixFallBackService。

import com.liuhangs.springcloud.api.entities.CommonResult;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

/**fallback属性为统一的服务降级处理类
 */
@FeignClient(name="CLOUD-PROVIDER-PAYMENT-HYSTRIX", fallback = PaymentHystrixFallBackService.class)
@Service
public interface PaymentHystrixService {

    @GetMapping("provider/payment/hystrix/ok/{id}")
    public CommonResult getPaymentOK(@PathVariable("id") Long id);

    /**
     * 调用服务提供者的getPaymentTimeOutFor80接口
     * @param id
     * @return
     */
    @GetMapping("provider/payment/hystrix/timeoutfor80/{id}")
    public CommonResult getPaymentTimeOutFor80(@PathVariable("id") Long id);
}

2.4.4 服务消费者统一的服务降级处理类

注意:服务降级处理类需要实现OpenFeign客户端调用类PaymentHystrixService。

/**PaymentHystrixService类对应的服务降级处理类
 * 这个类要实现调用服务消费的OpenFeign接口PaymentHystrixService
 */
@Service
public class PaymentHystrixFallBackService implements PaymentHystrixService {

    @Override
    public CommonResult getPaymentOK(Long id)
    {
        return new CommonResult(200, "使用id:" + id
                +"调用PaymentHystrixService服务的getPaymentOK方法失败,进入降级处理方法getPaymentOK");
    }

    @Override
    public CommonResult getPaymentTimeOutFor80(Long id)
    {
        return new CommonResult(200, "使用id:" + id
                +"调用PaymentHystrixService服务的getPaymentTimeOut方法失败,进入降级处理方法getPaymentTimeOutFor80");
    }
}

2.4.5 服务消费者controller

import com.liuhangs.springcloud.api.entities.CommonResult;
import com.liuhangs.springcloud.order.service.PaymentHystrixService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**服务消费者对外提供的接口
 */
@RequestMapping("/consumer/order/hystrix")
@RestController
public class OrderHystrixController {

    @Autowired
    private PaymentHystrixService paymentHystrixService;

    @GetMapping("/ok/{id}")
    public CommonResult getPaymentOK(@PathVariable("id") Long id) {
        return paymentHystrixService.getPaymentOK(id);
    }

    @GetMapping("/timeout/{id}")
    public CommonResult getPaymentTimeOut(@PathVariable("id") Long id) {
        return paymentHystrixService.getPaymentTimeOutFor80(id);
    }
}

2.4.6 服务消费者启动类

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

/**服务消费者启动类
 */
@SpringBootApplication
@EnableFeignClients //开启OpenFeign
public class OrderHystrixStart80 {

    public static void main(String[] args)
    {
        SpringApplication.run(OrderHystrixStart80.class, args);
    }
}

2.5 测试

启动Eureka、服务提供者、服务消费者。

2.5.1 调用正常返回的方法

浏览器访问:http://localhost/consumer/order/hystrix/ok/1 ,返回如下:

2.5.2 调用超时降级方法

浏览器访问:http://localhost/consumer/order/hystrix/timeout/1 ,返回如下:

3.代码请见

https://github.com/ainydexiaohai/cloud2020

4.参考文章

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!