第十章 Hystrix断路器详解+服务熔断

目录

一、服务熔断介绍

二、商品服务端熔断案例

1、引入依赖

2、开启熔断,在入口类加入注解

3、指定熔断后的备选方案

4、注册服务

5、进行测试

三、订单服务端熔断案例

1、引入依赖

2、开启openfeign,在入口类加入注解

3、OpenFeign远端调用接口加入注解

4、熔断备选方案

5、调用 openfeign 接口

6、开启 hystrix 熔断服务

7、进行测试

商品服务 宕机情况:

商品服务 异常情况

商品服务熔断

总结:


一、服务熔断介绍

        服务熔断作用:就是防止服务雪崩现象出现。

        服务熔断:正常情况出现超时、异常、宕机等情况就是服务降级,只有当默认10秒内超过20个请求次数 或者 默认10内超过50%的请求失数 的情况,才是熔断,此时才开启熔断器。

        服务熔断机制:所有微服务都需要引入Hystrix组件,即每个服务都有自己的监控器。当某个微服务请求达到阈值,则开启断路器,进行服务熔断。具体如下:

  • 1、当满足一定的阈值的时候(默认10秒内超过20个请求次数)
  • 2、当失败率达到一定的与之(默认10内超过50%的请求失数)
  • 3、达到以上的两个阈值,断路器将会开启,所调用的方法熔断,所有请求都不会进行转发。
  • 4、一段时间之后(默认是5秒),断路器是半开状态,允许让其中一个请求进行转发。
  • 5、如果成功,断路器会关闭,其他请求继续,若失败,断路器继续开启,重复4和5。

第十章 Hystrix断路器详解+服务熔断_第1张图片

        此案例是以 商品服务 和 订单服务为基础,先演示商品服务的熔断。在演示订单服务的熔断。

二、商品服务端熔断案例

        商品服务端熔断,我们通过浏览器(来模拟订单服务)访问的商品服务,看 商品服务容端的过程。

1、引入依赖




  
    springcloudbase
    com.hwadee.springcloud2022
    0.0.1-SNAPSHOT
  
  4.0.0

  com.hwadee.springcloud
  productServer9001
  0.0.1-SNAPSHOT

  
    
    
      org.springframework.cloud
      spring-cloud-starter-netflix-hystrix
    
    
    
    
      org.springframework.boot
      spring-boot-starter-web
    
    
    
      com.hwadee.springcloud
      springcloud-api
      0.0.1-SNAPSHOT
    
    
    
      org.springframework.cloud
      spring-cloud-starter-netflix-eureka-client
    
    
    
      org.projectlombok
      lombok
    
  


2、开启熔断,在入口类加入注解

     开启熔断,在入口类使用注解 @EnableCircuitBreaker 激活 Hystrix 熔断服务,代码如下:

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;

@SpringBootApplication
@EnableEurekaClient// 启动 eureka 客户端
@EnableCircuitBreaker // 主启动类激活 Hystrix
public class ProductServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProductServerApplication.class, args);
    }
}

3、指定熔断后的备选方案

       本案例中以第一种方式为案例。

  • 第一中方式

       在异常方法上使用注解 @HystrixCommand(),并指定备选方案(即降级方法)。

@RequestMapping(value = "/select/{id}")

// @HystrixCommand 中指定备选方案

@HystrixCommand(fallbackMethod= "selectHystrixBreakerFallback")

public Product selectHystrixBreaker(@PathVariable Long id) { ....... }


// 备选方案 即 降级方法

public Product selectHystrixBreakerFallback(Long id) { ..... }

完整代码:

import com.hwadee.springcloud.entity.Product;
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.math.BigDecimal;

@RestController
@RequestMapping("/product")
public class ProductController {
    //方便后面讲负载均衡,查看ip,此处获取配置中的端口号和ip
    @Value("${server.port}")
    private String port;
    @Value("${spring.cloud.client.ip-address}")
    private String ip;


    @RequestMapping(value = "/select/{id}")
    @HystrixCommand(fallbackMethod = "selectHystrixBreakerFallback", commandProperties = {
            @HystrixProperty(name = "circuitBreaker.enabled", value = "true"),//是否开启断路器
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "20"),//请求次数
            @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"),//时间窗口期
            @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60"),//失败率达到多少后跳闸
    })
    public Product selectHystrixBreaker(@PathVariable Long id) {
        // 程序异常 测试异常熔断
        if (id<=0) {
            throw new RuntimeException("id无效");
        }

        // 程序正常执行
        Product product = new Product();
        product.setId(id);
        // 后面需要测试负载均衡,所以返回 ip 地址及端口号
        product.setName("当前访问服务地址:" + ip + ":" + port + "  " + "从购物车删除订单,订单号:" + id);
        product.setPrice(new BigDecimal(10000.0));
        System.out.println(product);

        //测试超时熔断
        try {
            //测试并发熔断
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return product;
    }

    public Product selectHystrixBreakerFallback(Long id) {
        Product product = new Product();
        product.setId(id);
        product.setName("当前访问服务地址:" + ip + ":" + port + "  " + "查询订单异常,通过注解 @HystrixCommand()指定的备选方案进行服务熔断");
        product.setPrice(new BigDecimal(10000.0));
        return product;
    }
}
  • 第二中方式:

       在异常方法上使用注解 @HystrixCommand(),使用默认备选方案(即全局降级方法)。

@RequestMapping(value = "/select/{id}")
// @HystrixCommand 中指定备选方案

@HystrixCommand(defaultFallback= "selectHystrixFallback")

public Product selectHystrixBreaker(@PathVariable Long id) { ....... }


// 备选方案 即 全局降级方法,无参数

public Product selectHystrixFallback() { ..... }

完整代码

package com.hwadee.springcloud.controller;

import com.hwadee.springcloud.entity.Product;
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.math.BigDecimal;

@RestController
@RequestMapping("/product")
public class ProductController {
    //方便后面讲负载均衡,查看ip,此处获取配置中的端口号和ip
    @Value("${server.port}")
    private String port;
    @Value("${spring.cloud.client.ip-address}")
    private String ip;

    @RequestMapping(value = "/select/{id}")
    @HystrixCommand(defaultFallback = "selectHystrixFallback")
    public Product selectHystrixBreaker(@PathVariable Long id) {
        // 程序异常 测试异常熔断
        if (id<=0) {
            throw new RuntimeException("id无效");
        }
        //测试超时熔断
        try {
            //测试并发熔断
            Thread.sleep(900);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 程序正常执行
        Product product = new Product();
        product.setId(id);
        // 后面需要测试负载均衡,所以返回 ip 地址及端口号
        product.setName("当前访问服务地址:" + ip + ":" + port + "  " + "从购物车删除订单,订单号:" + id);
        product.setPrice(new BigDecimal(10000.0));
        System.out.println(product);

        return product;
    }

    public Product selectHystrixFallback() {
        Product product = new Product();
        product.setName("当前访问服务地址:" + ip + ":" + port + "  " + "查询订单异常,通过注解 @HystrixCommand()指定的默认备选方案进行服务熔断");
        product.setPrice(new BigDecimal(10000.0));
        System.out.println(product);
        return product;
    }
}
  • 第三中方式: 

       在Controller类上使用注解 @DefaultProperties(),使用默认备选方案(即全局降级方法)。

@RestController

@RequestMapping("/product")

@DefaultProperties(defaultFallback = "hystrixFallback") // 中指定备选方案

public class ProductController {

    @RequestMapping(value = "/select/{id}")

    @HystrixCommand

    public Product selectHystrixBreaker(@PathVariable Long id) { ....... }


    // 备选方案 即 全局降级方法,无参数

    public Product hystrixFallback() { ..... }

}

完整代码

package com.hwadee.springcloud.controller;

import com.hwadee.springcloud.entity.Product;
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.math.BigDecimal;

@RestController
@RequestMapping("/product")
@DefaultProperties(defaultFallback = "hystrixFallback")
public class ProductController {
    //方便后面讲负载均衡,查看ip,此处获取配置中的端口号和ip
    @Value("${server.port}")
    private String port;
    @Value("${spring.cloud.client.ip-address}")
    private String ip;


    @RequestMapping(value = "/select/{id}")
    @HystrixCommand
    public Product selectHystrixBreaker(@PathVariable Long id) {
        // 程序异常 测试异常熔断
        if (id<=0) {
            throw new RuntimeException("id无效");
        }
        //测试超时熔断
        try {
            //测试并发熔断
            Thread.sleep(900);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 程序正常执行
        Product product = new Product();
        product.setId(id);
        // 后面需要测试负载均衡,所以返回 ip 地址及端口号
        product.setName("当前访问服务地址:" + ip + ":" + port + "  " + "从购物车删除订单,订单号:" + id);
        product.setPrice(new BigDecimal(10000.0));
        System.out.println(product);

        return product;
    }

    public Product hystrixFallback() {
        Product product = new Product();
        product.setName("当前访问服务地址:" + ip + ":" + port + "  " + "查询订单异常,通过注解 @HystrixCommand()指定默认的全局备选方案进行服务熔断");
        product.setPrice(new BigDecimal(10000.0));
        return product;
    }

}

4、注册服务

server:
  port: 9001
spring:
  application:
    name: product-service # 为当前商品服务命名
eureka:
  client:
    service-url: # 配置服务注册地址,与 eureka-server 中暴露地址保持一致
      defaultZone: http://localhost:8000/eureka
  instance:
    prefer-ip-address: true  # 是否使用 IP 地址注册,默认 false
    # instance-id: product-service  # 实例 id,服务的唯一标识
    instance-id: ${spring.cloud.client.ip-address}:${server.port} # 如果想在控制页面看到服务地址与端口,可以将 instance-id 这样配置
    lease-renewal-interval-in-seconds: 5  # 发送心跳的间隔,单位秒,默认 30
    lease-expiration-duration-in-seconds: 10 # 续约到期时间,单位秒,默认90

5、进行测试

    在浏览器发送 商品服务 错误请求:http://localhost:9001/product/select/0   20次
    迅速发送正确请求:http://localhost:9001/product/select/1
    持续5s发送正确请求:http://localhost:9001/product/select/1

第十章 Hystrix断路器详解+服务熔断_第2张图片

第十章 Hystrix断路器详解+服务熔断_第3张图片

第十章 Hystrix断路器详解+服务熔断_第4张图片
    观察结果发现,当在浏览器发送20个错误请求后,错误率已经超过50%,断路器开启,再次访问正确请求也会进行服务降级,此时断路器处于开启状态。持续发送正确请求5s后,则会发先可以正常访问。

三、订单服务端熔断案例

     订单服务端熔断,我们通过浏览器访问的订单服务,通过订单服务的接口访问商品服务容端的熔断过程(相当于上面讲的浏览器直接访问商品服务)。在订单服务端的熔断,使用openfeign接口的方式。

1、引入依赖

      因为使用 openfeign 接口远端调用,所以引入 spring-cloud-starter-openfeign 依赖,其中集成了 hysrix,所以不需要额外引入 hysrix 依赖。




    
        springcloudbase
        com.hwadee.springcloud2022
        0.0.1-SNAPSHOT
    
    4.0.0

    com.hwadee.springcloud
    orderServer9000

    
        
        
        
            org.springframework.cloud
            spring-cloud-starter-openfeign
        
        

        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
        
            com.hwadee.springcloud
            springcloud-api
            0.0.1-SNAPSHOT
        
        
        
            org.springframework.cloud
            spring-cloud-starter-netflix-eureka-client
        
        
        
            org.projectlombok
            lombok
        
    

2、开启openfeign,在入口类加入注解

     开启 openfeign,在入口类加入注解 @EnableFeignClients 。

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
@EnableEurekaClient// 启动 eureka 客户端
@EnableFeignClients  // 启动 feign
public class OrderServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServerApplication.class, args);
    }
}

3、OpenFeign远端调用接口加入注解

     创建 OpenFeign 远端调用接口,在接口加入注解@FeignClient ,通过 @FeignClient 指定熔断后的备选方案。

import com.hwadee.springcloud.entity.Product;
import com.hwadee.springcloud.service.impl.OrderFeignServiceFallBack;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

@Component // 让 spring 可以识别,不加也行,但是在注入的时候 IDEA 会报错,不会影响运行,但有条红线让自己不舒服
@FeignClient(value = "PRODUCT-SERVICE",fallback = OrderFeignServiceFallBack.class)
public interface IOrderFeignService {
    @RequestMapping(value = "/product/select/{id}")
    Product selectOrderById(@PathVariable Long id);
}

4、熔断备选方案

     因为 订单服务 是通过 接口调用 商品服务,因此,只需在 OpenFeign 接口的实现类中完成备选方案,将所有的降级方法提取到 OpenFeign 接口的实现类中。当OpenFeign 接口访问有异常 或 超时 或 宕机 时,则使用 OpenFeign 接口中对应的方法实现作为降级或熔断。

@Component
public class OrderFeignServiceFallBack implements IOrderFeignService {
    @Override
    public Product selectOrderById(Long id) {
        Product product = new Product();
        product.setId(id);
        product.setName("当前订单服务访问/order/select/1 请求服务熔断,进行降级:"+id);
        return product;
    }
}

5、调用 openfeign 接口

import com.hwadee.springcloud.entity.Product;
import com.hwadee.springcloud.service.IOrderFeignService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/order")
public class OrderController {
    @Autowired
    IOrderFeignService orderFeignService;

    @RequestMapping(value = "/select/{id}")
    public Product selectOrderById(@PathVariable Long id) {
        Product product = orderFeignService.selectOrderById(id);
        return product;
    }
}

6、开启 hystrix 熔断服务

     开启 openfeign 调用过程中对 hystrix 服务的支持。

server:
  port: 9000
spring:
  application:
    name: order-service # 为当前订单服务命名为 order-service

# 配置eureka客户端信息
eureka:
  client:
    service-url:
      # 配置eureka客户端服务order-service的注册地址,与 eureka-server 中暴露地址要保持一致
      defaultZone: http://localhost:8000/eureka/
  instance:
    prefer-ip-address: true # 是否使用 IP 地址注册,默认 false
    # instance-id: order-service  # 实例 id,服务的唯一标识,会自动的找到order-service的ip和端口
    instance-id: ${spring.cloud.client.ip-address}:${server.port} # 如果想在控制页面看到服务地址与端口,可以将 instance-id 这样配置

# 开启 openfeign 的 hystrix 服务
feign:
  hystrix:
    enabled: true

7、进行测试

      此处测试分三种情况:服务宕机、服务异常和服务熔断

  • 商品服务 宕机情况:

       关闭 商品服务 ,依次访问 商品服务和 订单服务 两个网址,查看效果。

       商品服务 http://localhost:9001/product/select/0 

       订单服务 http://localhost:9000/order/select/1    

第十章 Hystrix断路器详解+服务熔断_第5张图片
第十章 Hystrix断路器详解+服务熔断_第6张图片

         通过观察发现,如果 商品服务 宕机,直接访问会显示错误页面,但是 订单服务  使用了降级熔断,访问时候会在 订单服务 端进行服务降级熔断。    

  • 商品服务 异常情况

       依次访问  订单服务 的两个网址 ,查看效果。

       http://localhost:9000/order/select/0

       http://localhost:9000/order/select/1    

第十章 Hystrix断路器详解+服务熔断_第7张图片

第十章 Hystrix断路器详解+服务熔断_第8张图片

 通过观察发现:

     当访问 订单服务 的 http://localhost:9000/order/select/0 时候,会在 商品服务 端发生异常,商品服务 进行服务降级,返回降级的内容。

     当访问 订单服务 的 http://localhost:9000/order/select/1 时候,在 商品服务 端不发生异常,商品服务 正常返回。

  • 商品服务熔断

       快速访问  订单服务错误请求  http://localhost:9000/order/select/0      20次

       接着快速访问 订单服务正确  http://localhost:9000/order/select/1 

      5s后再访问 订单服务  http://localhost:9000/order/select/1 第十章 Hystrix断路器详解+服务熔断_第9张图片

第十章 Hystrix断路器详解+服务熔断_第10张图片

第十章 Hystrix断路器详解+服务熔断_第11张图片

  通过观察发现:

        快速访问  订单服务错误请求  http://localhost:9000/order/select/0    20次后触发商品服务熔断机制,熔断断路器开启后其他请求如: http://localhost:9000/order/select/1  使用 商品服务 的备选方案。默认5s后可以尝试发送第一个请求 http://localhost:9000/order/select/1  成功,此时熔断器关闭。

总结:

        简单理解,服务熔断在服务超时、异常、宕机情况下,就是服务降级。当默认10秒内超过20个请求次数 或者 默认10内超过50%的请求失数 的情况,才是熔断。

第九章:Hystrix断路器详解+服务降级之全局解耦

第十一章:GetAway服务网关详解

你可能感兴趣的:(springcloud,hystrix,java,微服务,spring,cloud)