SpringCloud Hystrix要点及Geteway网关

  • Post author:
  • Post category:其他





前言

Hystrix(英文含义:豪猪)由Netflix公司开发的,后来由Spring Cloud Hystrix基于这款框架实现了断路器、线程隔离等一系列服务保护功能,该框架的目标在于通过控制访问远程系统、服务和第三方库的节点,从而延迟和故障提供更强大的容错能力。

Hystrix具备服务降级、服务熔断、线程和信号隔离、请求缓存、请求合并以及服务监控等强大功能。是一种微服务的保护机制。

Hystrix防止某个单元出现故障.从而引起依赖关系引发故障的蔓延,最终导致整个系统的瘫痪。

Spring Cloud Gateway 里明确的区分了 Router 和 Filter,并且一个很大的特点是内置了非常多的开箱即用功能,并且都可以通过 SpringBoot 配置或者手工编码链式调用来使用。




提示:以下是本篇文章正文内容,下面案例可供参考



一、服务降级

服务降级:服务器忙碌或者网络拥堵时,不让客户端等待并立刻返回一个友好提示,fallback(备选方案)。

场景:调用服务超时,服务本身 内部错误,服务的线程资源耗尽



1.局部服务降级

1.1 在服务提供方设置

在服务提供方进行局部服务设置,首先设置自身调用超时的峰值,峰值内正常运行,超出峰值需要有兜底的方法处理,作服务降级fallback调用。

(1)引入Hystrix依赖

<!-- hystrix -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

(2)在service中对超时方法进行设置,访问超时调用兜底方法


( 降级方法(逻辑)编写的原则是方法的形参和返回值必须要和被降级的方法保持一致。)

   /**
     超时访问的方法,添加@HystrixCommand注解
     */
    @HystrixCommand(fallbackMethod = "timeoutHandler",commandProperties = {
            //设置峰值,超过 3 秒,就会调用兜底方法,这个时间也可以由feign控制
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")
    })
    public String paymentInfo_Timeout(Integer id){
    	//设置超时调用时间
        int interTime = 5;
        //int i = 10/0;
        try{
            TimeUnit.SECONDS.sleep(interTime);
        }catch (Exception e){
            e.printStackTrace();
        }
        return "线程池:" + Thread.currentThread().getName() + "  ,paymentInfo_Timeout,id:" + id + "耗时" + interTime + "秒钟";
    }
   // 定义服务出现异常之后,兜底的方法
   //降级方法(逻辑)编写的原则是方法的形参和返回值必须要和被降级的方法保持一致。
    public String timeoutHandler(Integer id){
        return "服务异常,请重试......";
    }
}

(3)在启动类上加上@EnableCircuitBreaker 作用:开启服务熔断

(4)运行测试结果

(5)同样在service的方法中设置一个异常(替换上一步的超时设置),用于模拟测试

 public String paymentInfo_Timeout(Integer id){
        //int interTime = 5;
        //定义一个异常,用于测试
        int i = 10/0;
}        

(6)测试结果。


在发生服务不可用(调用超时或方法内部异常)时,都会进行降级调用来处理问题。

1.2 在消费方设置

(1)首先取消上述对于服务提供方的降级调用设置

(2)引入Hystrix依赖

<!-- hystrix -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

(3)在启动类上加上@EnableCircuitBreaker 作用:开启服务熔断

(4) 在controller层设置服务降级的逻辑编写,访问超时调用兜底方法

    @HystrixCommand(fallbackMethod = "handeException", commandProperties = {
            //设置峰值,超过 1.5 秒,就会调用兜底方法
            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds", value = "1500")
    })
    @GetMapping("/consumer/payment/hystrix/timeout/{id}")
    public String paymentInfo_Timeout(@PathVariable("id")Integer id){
        log.info("paymentInfo_timeout");
        return orderService.paymentInfo_Timeout(id);
    }
	//定义兜底方法
    public String handeException(Integer id){
        return "服务调用异常,请稍后再试.....";
    }

(5)运行测试结果

(6)在服务提供方的service的方法中设置一个异常,返回一个值,用于测试结果

public String paymentInfo_Timeout(Integer id){
    	//设置超时调用时间
        //int interTime = 5;
        //定义异常
        int i = 10/0;
        /*try{
            TimeUnit.SECONDS.sleep(interTime);
        }catch (Exception e){
            e.printStackTrace();
        }
        return "线程池:" + Thread.currentThread().getName() + "  ,paymentInfo_Timeout,id:" + id + "耗时" + interTime + "秒钟";*/
        return "发生内部异常";
    }



2.全局服务降级

(1)全局服务降级

1.在消费方的启动类上面要加上一个@EnableCircuitBreaker

2.在消费方的controller里面,定义一个全局降级的方法。

//全局服务降级处理在服务消费方上面实现
// 全局降级处理方法
public String globalHandler(){
    return "这是全局处理降级逻辑的方法.......";
}

3.在controller上面 添加一个注解@DefaultProperties(defaultFallback = “handleAll”)

4.在指定的被降级的方法上添加一个@HystrixCommand

@RestController
@Slf4j
@DefaultProperties(defaultFallback = "globalHandler") //开启全局降级处理
public class OrderController {
    @Autowired
    OrderService orderService;
    
	@HystrixCommand //不写fallbackMethod属性,默认是全局
	@GetMapping("/consumer/payment/hystrix/timeout/{id}") //服务超时
	public String paymentInfo_Timeout(@PathVariable("id")Integer id){
   		log.info("paymentInfo_timeout");
    	return orderService.paymentInfo_Timeout(id);
    }	
}


注意:降级逻辑的优先级 局部的降级逻辑>全局的降级逻辑

(2)服务降级方法抽取

  1. 在消费方的启动类上面要加上一个@EnableCircuitBreaker
  2. 在配置文件里面,开启feign对Hystrix的支持
feign:
  hystrix:
    enabled: true
  1. 定义一个类,实现Feign客户端。重写里面的方法(编写降级处理方法)
@Component
public class FallBackService implements OrderService {
    @Override
    public String paymentInfo_OK(Integer id) {
        return "进行paymentInfo_OK方法降级处理......";
    }
    @Override
    public String paymentInfo_Timeout(Integer id) {
        return "进行paymentInfo_Timeout方法降级处理";
    }
}
  1. 使用@FeignClient注解,将这个类绑定到Feign客户端上去
@Component
@FeignClient(value = "cloud-payment-service",fallback = FallBackService.class)
public interface OrderService {
    @GetMapping("/payment/hystrix/{id}")
    public String paymentInfo_OK(@PathVariable("id")Integer id);
    
    @GetMapping("/payment/hystrix/timeout/{id}")
    public String paymentInfo_Timeout(@PathVariable("id")Integer id);
}
  1. 进行测试,查看降级处理结果



二、服务熔断


代码如下(示例):



1.服务熔断的概述

当用户访问某个服务,达到了最大的访问量之后,直接拒绝用户访问。

在SpringCloud框架中,熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状况。当失败的调用到一定阈值时,缺省是5秒内20次调用失败,就会启动熔断机制。熔断机制的注解是@HystrixCommand。



2.熔断有哪几种状态

熔断状态机3个状态:

(1)Closed:关闭状态,所有请求都正常访问。

(2)Open:打开状态,所有请求都会被降级。Hystix会对请求情况计数,当一定时间内失败请求百分比达到阈值,则触发熔断,断路器会完全打开。默认失败比例的阈值是50%,请求次数最少不低于20次。

(3)Half Open:半开状态,open状态不是永久的,打开后会进入休眠时间(默认是5S)。随后断路器会自动进入半开状态。此时会释放部分请求通过,若这些请求都是健康的,则会完全关闭断路器,否则继续保持打开,再次进行休眠计时。



3.断路器的工作原理

统计用户在指定的时间范围(默认10s)之内的请求总数达到指定的数量之后,如果不健康的请求(超时、异常)占总请求数量的百分比(50%)达到了指定的阈值之后,就会触发熔断。触发熔断后,断路器就会打开(open),此时所有请求都不能通过。在5s之后,断路器会恢复到半开状态(half open),会允许少量请求通过,如果这些请求都是健康的,那么断路器会回到关闭状态(close).如果这些请求还是失败的请求,断路器还是恢复到打开的状态(open)。



4.如何开启熔断?

1.在指定请求方法上,开启熔断

@HystrixCommand(fallbackMethod = "handleCircuitBreaker",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"),  // 失败率达到多少后跳闸
          //整体意思:10秒内 10次请求,有6次失败,就跳闸
		})
public String enableCircuitBreaker(Integer id){
    if(id < 0){
        throw  new RuntimeException();
    }
    String uuid = UUID.randomUUID().toString();
    return "返回的uuid是:" + uuid;
}

2.使用XML的方式进行熔断的配置

#全局服务降级,feign+hystrix整合,即 service 实现类的方式做全局配置?
hystrix:
  command:
    default:
      circuitBreaker:
        enabled: true
          requestVolumeThreshold: 10
            sleepWindowInMilliseconds: 10000
              errorThresholdPercentage: 60



三、Hystrix DashBoard

除了隔离依赖服务的调用以外,Hystrix还提供了

准时调用监控(Hystrix DashBoard)

,Hystrix 会持续地记录所有通过Hystrix发起的请求的执行信息,并以统计报表和图形的形式展示给用户,包括每秒执行多少请求多少成功,多少失败等。

Netflix通过hystrix-metrics-event-stream项目实现了对以上指标的指控。SpringCloud也提供了Hystrix Dashboard的整合,对监控内容转化为可视化界面。



1.创建仪表盘监控模块

(1)在项目中创建仪表盘监控模块

(2)编写启动类,使用@EnableHystrixDashboard注解开启仪表盘监控

@SpringBootApplication
@EnableHystrixDashboard //开启仪表盘监控
public class HystrixDashboardApplication {
    public static void main(String[] args) {
        SpringApplication.run(HystrixDashboardApplication.class,args);
    }
}

(3)编写配置文件

server:
  port: 9001 #端口号,自定义

(4)引入依赖


注意:所有被监控的服务都需要添加依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

(5)启动测试:访问 http://localhost:9001/hystrix



2. 监控实战

(1)在http://localhost:9001/hystrix页面中访问http://localhost:8001/hystrix.stream

(2)访问:http://localhost:8001/payment/circuit/1 ,请求数据

查看请求量等信息。

类如下图所示:

在这里插入图片描述



四、Geteway网关



1.Geteway网关简介

Spring Cloud Gateway是Spring官方基于Spring 5.0,Spring Boot 2.0和Project Reactor等技术开发的网关,Spring Cloud Gateway旨在为微服务架构提供一种简单而有效的统一的API路由管理方式。Spring Cloud Gateway作为Spring Cloud生态系中的网关,目标是替代ZUUL,其不仅提供统一的路由方式,并且基于Filter链的方式提供了网关基本的功能,例如:安全,监控/埋点,和限流等。



2.Gateway 的核心概念

三大核心概念:

(1)路由(Route):路由是网关的基本模块,由ID,目标URL,一系列的断言和过滤器组成。断言为真,则路由匹配。

(2)断言(Predicate):输入类型是一个ServerWebExchange。开发人员可以使用它匹配HTTP请求中的任何内容,例如headers或参数。如果请求和断言相匹配,则进行路由。

(3)过滤(Filter):Gateway中的Filter分为两种类型,分别是Gateway Filter和Global Filter。过滤器可以在请求被路由前或之后

对请求进行修改。



3.如何简单使用gateway(入门案例)

(1)创建gateway模块

(2)引入POM依赖

 	<!--gateway-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
    <!--eureka-client gateWay作为网关,也要注册进服务中心-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <!-- gateway和web不能同时存在,即web相关jar包不能导入 -->

(2)编写application.yml配置文件

server:
  port: 9527
spring:
  application:
    name: cloud-gateway
  ## GateWay配置
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
        - id: payment_routh  # 路由ID , 没有固定的规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service  # 匹配后提供服务的路由地址,之前http://localhost:8001,换成
          predicates:
            - Path=/payment/**  # 断言,路径相匹配的进行路由

        - id: payment_routh2  # 路由ID , 没有固定的规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service  # 匹配后提供服务的路由地址,之前localhost:8001,cloud-payment-service
          predicates:
            - Path=/payment/lb/**  # 断言,路径相匹配的进行路由,lb 属于GateWay 的关键字,代表是动态uri,即代表使用的是服务注册中心的微服务名,它默认开启使用负载均衡机制
            # 在这个时间之后失效
            #- After=2021-01-18T21:08:21.474+08:00[GMT+08:00]
            #- Before=2021-01-18T21:08:21.474+08:00[GMT+08:00]
            #- Between=2021-01-18T21:08:21.474+08:00[GMT+08:00],2021-01-18T22:08:21.474+08:00[GMT+08:00]
            
# 注册进 eureka Server
eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
    register-with-eureka: true
    fetch-registry: true

(3)创建启动类

@SpringBootApplication
@EnableEurekaClient
public class GatewayMain9527 {
    public static void main(String[] args) {
        SpringApplication.run(GatewayMain9527.class,args);
    }
}

(4)测试结果

可使用网关端口调用业务逻辑,隐藏了业务微服务端口,使用网关服务对微服务进行保护。



4.通过微服务名实现动态路由

默认情况下,Getway会根据注册中心的服务列表,以注册中心上的微服务名为路径创建动态路由进行转发,从而实现动态路由的功能。

所谓的动态配置就是利用服务注册中心,来实现负载均衡的调用多个微服务。这是GateWay 的负载均衡。

(1)设置配置文件

server:
  port: 9527
spring:
  application:
    name: cloud-gateway
  ## GateWay配置
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
      - id: payment_routh  # 路由ID , 没有固定的规则但要求唯一,建议配合服务名
        uri: lb://cloud-payment-service  # 匹配后提供服务的路由地址
        predicates:
        - Path=/payment/**  # 断言,路径相匹配的进行路由

      - id: payment_routh2  # 路由ID , 没有固定的规则但要求唯一,建议配合服务名
        uri: lb://cloud-payment-service  # 匹配后提供服务的路由地址
        predicates:
        - Path=/payment/lb/**  # 断言,路径相匹配的进行路由

# 注册进 eureka Server
eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
    register-with-eureka: true
    fetch-registry: true


注意:

uri: lb://CLOUD-PROVIDER-SERVICE 解释:lb 属于GateWay 的关键字,代表是动态uri,即代表使用的是服务注册中心的微服务名,它默认开启使用负载均衡机制

(2)测试发现实现负载均衡

在这里插入图片描述
在这里插入图片描述



5.断言Predicate

Predicate(断言) 就是进行条件判断, 判断在什么条件下 才能进行路由转发,只有断言都返回真,才会真正的执行路由。

断言工厂的分类:

在这里插入图片描述

在配置文件中定义断言

predicates:
   - Path=/payment/lb/**  # 断言,路径相匹配的进行路由,lb 属于GateWay 的关键字,代表是动态uri,即代表使用的是服务注册中心的微服务名,它默认开启使用负载均衡机制
   # 在这个时间之后失效
   #- After=2021-01-18T21:08:21.474+08:00[GMT+08:00]
   # 在这个时间之前失效
   #- Before=2021-01-18T21:08:21.474+08:00[GMT+08:00]
   # 在这个时间段之间失效
   #- Between=2021-01-18T21:08:21.474+08:00[GMT+08:00],2021-01-18T22:08:21.474+08:00[GMT+08:00]



6.Filter

今天在这里简单分享一下自定义全局过滤器:

(1)自定义一个全局过滤器类,继承GlobalFilter,Ordered接口,并重写其方法。

@Component
@Slf4j
public class GateWayFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String username = exchange.getRequest().getQueryParams().getFirst("username");
        if(username == null){
            log.info("用户名为null,请求不能通过......");
            exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);
            //完成请求调用
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange); //放行
    }

    @Override
    public int getOrder() {
        return 0; //过滤器执行的优先级,数字越小 优先级越高
    }
}

(2)测试过滤器效果,


注意:请求url必须携带username参数,否则不允许通过

在这里插入图片描述




总结


以上就是今天分享的内容,本文仅仅简单介绍了Hystrix与Gateway网关的相关内容及使用。



版权声明:本文为weixin_53059756原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。