SpringCloud_05

1 Spring Cloud Hystrix

1.1 雪崩效应

1.1.1 概念

对于一个复杂的分布式系统,包含的应用可能多达数十个,这些应用有许多依赖项目,每个依赖项目在某个时刻不

可避免会失败导致故障,如果不对这些故障进行隔离,整个分布式系统都可能会崩溃。

在高流量情况下,一个后端的依赖延迟可能会导致所有服务的资源在数秒内变的饱和,这也就意味着,后续如果再

有请求将无法提供服务,应用会出现故障。比故障更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,从

而备份队列、线程和其他资源,从而导致整个系统出现更多级联故障

1.1.2 解决方案

【1】降级

超时降级、资源不足时(线程或信号量)降级,降级后可以配合降级接口返回托底数据。实现一个 fallback 方法, 当

请求后端服务出现异常的时候, 接收 fallback 方法返回的值。

保证:服务出现问题整个项目还可以继续运行

【2】熔断

当失败率(如因网络故障/超时造成的失败率高)达到阀值自动触发降级,熔断器触发的快速失败会进行快速恢复。

熔断就是具有特定条件的降级

保证:服务出现问题整个项目还可以继续运行

【3】缓存

在调用端使用 SpringCache 对服务端的返回结果进行缓存,出现大量请求服务端也不会出现高负载。

保证:减少对 Application Server 的调用

【4】请求合并

将一定时间短内的同一目标请求进行合并,解决了 Application Server 的负载激增的问题。

保证:减少对 Application Server 的调用

1.2 HyStrix

Hystrix 封装了每个依赖项,每个依赖项彼此隔离,当延迟发生时,它会被限制在资源中,并包含回退逻辑,该逻

辑决定在依赖发生任何类型故障时应作出何种响应。

Hystrix 被设计的目标是阻止级联故障,对通过第三方客户端访问的依赖项的延迟和故障进行保护和控制。Hystrix

实现这一目标的大致思路具体如下:

(1) 将外部依赖的访问请求封装在独立的线程中,进行资源隔离。

(2) 对于超出设定阈值的服务调用,直接进行超时,不允许其耗费过长时间阻塞线程。

(3) 每个依赖服务维护一个独立的线程池,一旦线程池满了,直接拒绝服务的调用。

(4) 统计依赖服务调用的成功次数、失败次数、拒绝次数、超时次数等结果。

(5) 在一段时间内,如果服务调用的异常次数超过一定阈值,就会触发熔断停止对特定服务的所有请求,在一定

时间内对服务调用直接降级,一段时间后再次进行自动尝试恢复。

(6) 如果某个服务出现调用失败、被拒绝、超时等异常情况,自动调用 fallback 降级机制。

(7) 实时监控指标和配置变化。

1.3 搭建 HyStrix

1.3.1 搭建注册中心 eureka-server

启动 eureka-server

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DMuUekub-1624770135380)(E:\Java\img\image-20210125201726668.png)]

1.3.2 搭建 hystrix-provider

1.3.2.1 添加依赖
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
    </dependencies>
1.3.2.2 添加配置文件
server:
  port: 8080

eureka:
  instance:
    hostname: localhost
  client:
    service-url:
      defaultZone: http://localhost:7000/eureka/
spring:
  application:
    name: hystrix-provider
1.3.2.3 新建控制器
package com.cl.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author chenlan
 * @Description TODO
 * @Date 2021/1/25 19:56
 * @Version 1.0
 */

@RestController
public class HystrixController {

    @GetMapping("/hi")
    public String sayHi(){
        return "Hello,Java";
    }
}
1.3.2.4 启动 hystrix-provider

20210125201552286

1.3.3 搭建 eureka-hystrix-client

1.3.3.1 添加依赖
 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</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>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
1.3.3.2 添加注解
package com.cl;

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;

/**
 * @author chenlan
 *
 * hystrix 消费者客户端
 * @EnableHystrix 启动 hystrix 熔断功能
 */
@SpringBootApplication
@EnableEurekaClient
@EnableHystrix
public class EurekaHystrixClientApplication {

    public static void main(String[] args) {
        SpringApplication.run(EurekaHystrixClientApplication.class, args);
    }
}
1.3.3.3 添加配置文件
spring:
  application:
    name: eureka-hystrix-clinet
server:
  port: 80
eureka:
  client:
    service-url: 
      defaultZone: http://localhost:7000/eureka/
  instance:
    hostname: localhost
1.3.3.4 配置负载均衡
package com.cl.config;

import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

/**
 * @Author chenlan
 * @Description TODO
 * @Date 2021/1/25 20:31
 * @Version 1.0
 */

@Configuration
public class HystrixConfig {

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(RestTemplateBuilder builder){
        return builder.build();
    }
}
1.3.3.5 新建业务层以及控制层
package com.cl.service;

/**
 * @Author chenlan
 * @Description TODO
 * @Date 2021/1/25 20:34
 * @Version 1.0
 */
public interface ClientService {

    /**
     * 调用远程服务接口方法
     *
     * @return
     */
    String sayHi();
}
package com.cl.service.impl;

import com.cl.service.ClientService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

/**
 * @Author chenlan
 * @Description TODO
 * @Date 2021/1/25 20:35
 * @Version 1.0
 */

@Service
public class ClientServiceImpl implements ClientService {

    @Autowired
    private RestTemplate restTemplate;

    /**
     * @HystrixCommand 启动熔断器功能
     * fallbackMethod 定义处理回退方法
     *
     * @return
     */
    @HystrixCommand(fallbackMethod = "sayHiError")
    @Override
    public String sayHi() {
        return restTemplate.getForObject("http://hystrix-provider/hi", String.class);
    }

    /**
     * 处理回退方法
     *
     * @return
     */
    public String sayHiError(){
        return "bad request";
    }
}
package com.cl.controller;

import com.cl.service.ClientService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author chenlan
 * @Description TODO
 * @Date 2021/1/25 20:38
 * @Version 1.0
 */

@RestController
public class ClientController {

    @Autowired
    private ClientService clientService;

    @GetMapping("/hi")
    public String sayHi(){
        return clientService.sayHi();
    }
}
1.3.3.6 测试结果

启动 eureka-provider

20210125205006434

关闭 eureka-provider

20210125205901311

服务提供者不可用的情况下,服务消费者发送请求失败,此时会开启熔断器。熔断器打开后,请求会直接执fallbackMethod逻辑,通过快速失败,做到及时处理请求,避免线程被阻塞

1.4 熔断

当一定时间内,异常请求比例(请求超时、网络故障、服务异常等)达到阀值时,启动熔断器,熔断器一旦启动,

则会停止调用具体服务逻辑,通过 fallback 快速返回托底数据,保证服务链的完整。

熔断有自动恢复机制说,如:当熔断器启动后,每隔5秒,尝试将新的请求发送给Application Service,如果服务

可正常执行并返回结果,则关闭熔断器,服务恢复。如果仍旧调用失败,则继续返回托底数据,熔断器持续开启状

态。

降级是出错了返回托底数据,而熔断是出错后如果开启了熔断将会一定时间不在访问 application server

1.4.1 配置熔断参数

package com.cl.service.impl;

import com.cl.service.ClientService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.netflix.hystrix.contrib.javanica.conf.HystrixPropertiesManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

/**
 * @Author chenlan
 * @Description TODO
 * @Date 2021/1/25 20:35
 * @Version 1.0
 */

@Service
public class ClientServiceImpl implements ClientService {

    @Autowired
    private RestTemplate restTemplate;

    /**
     * @HystrixCommand 启动熔断器功能
     * fallbackMethod 定义处理回退方法
     *
     * @return
     */
    @HystrixCommand(fallbackMethod = "sayHiError",commandProperties = {
            // 请求数量达到 3 个
            @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD,value = "3"),
            // 判断时间 每 10s 为一个单位
            @HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_THREAD_TIMEOUT_IN_MILLISECONDS,value = "10000"),
            // 失败率达到 50%
            @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE,value = "50"),
            // 开启熔断后,30s 不再请求远程服务
            @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS,value = "30000")
    })
    @Override
    public String sayHi() {
        return restTemplate.getForObject("http://hystrix-provider/hi", String.class);
    }

    /**
     * 处理回退方法
     *
     * @return
     */
    public String sayHiError(){
        return "bad request";
    }
}

1.5 请求缓存

Hystrix 为了降低访问服务的频率,支持将一个请求与返回结果做缓存处理。如果再次请求的 URL 没有变化,那么

Hystrix 不会请求服务,而是直接从缓存中将结果返回。这样可以大大降低访问服务的压力

使用 SpringCache + redis 进行请求缓存

1.5.1 添加依赖

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

1.5.2 添加配置文件

spring:
  redis:
    host: 192.168.72.12

1.5.3 开启 SpringCache

// 在启动类添加注解
@EnableCaching

1.5.4 添加缓存

在业务实现类上添加并配置Cacheanle注解
/**
     * @return
     * @HystrixCommand 启动熔断器功能
     * fallbackMethod 定义处理回退方法
     */
    @HystrixCommand(fallbackMethod = "sayHiError", commandProperties = {
            // 请求数量达到 3 个
            @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD, value = "3"),
            // 判断时间 每 10s 为一个单位
            @HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_THREAD_TIMEOUT_IN_MILLISECONDS, value = "10000"),
            // 失败率达到 50%
            @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE, value = "50"),
            // 开启熔断后,30s 不再请求远程服务
            @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS, value = "30000")
    })
    @Cacheable(cacheNames = "com.cl", key = "'sayHi'")
    @Override
    public String sayHi() {
        return restTemplate.getForObject("http://hystrix-provider/hi", String.class);
}

1.5.5 查看缓存数据

**20210126155609211**

1.6 请求合并

1.6.1 分析

优点:

在微服务架构中,我们将一个项目拆分成很多个独立的项目,这些独立的项目通过远程调用来互相配合工作,但

是,在高并发情况下,通信次数的增加会导致总的通信时间增加,同时,线程池的资源也是有限的,高并发环境

导致有大量的线程处于等待状态,进而导致响应延迟,为了解决这些问题,我们需要使用请求合并,将同一时间范

围内的同一请求合并为一条请求,降低 Application Server 的负载。

缺点:

设置请求合并之后,本来一个请求可能 5ms 就搞定了,但是现在必须再等 10ms 看看还有没有其他的请求一起

的,这样一个请求的耗时就从 5ms 增加到 15ms 了,不过,如果我们要发起的命令本身就是一个高延迟的命令,

那么这个时候就可以使用请求合并了,因为这个时候时间窗的时间消耗就显得微不足道了,另外高并发也是请求合

并的一个非常重要的场景。

1.6.2 代码实现

1.6.2.1 配置 Hystrix-provider

ProvderController

 @PostMapping("/list")
    public List<String> showList(@RequestBody List<String> names) {
        ArrayList<String> list = new ArrayList<>();
        for (String name : names) {
            list.add("姓名" + name);
        }
        System.out.println("list = " + list);
        return list;
    }
1.6.2.2 配置 clinet 端

ClientService

/**
     * 调用远程服务接口方法
     *
     * @param name
     * @return
     */
    Future<String> showList(String name);

ClientServiceImpl

/**
     * @HystrixCollapser 使用该注解开启请求合并 返回值类型必须为 Future 使用异步方法进行请求合并
     * batchMethod 值为请求合并后的方法
     * scope 值为请求方式
     * HystrixPropertiesManager.TIMER_DELAY_IN_MILLISECONDS 设置请求时间段
     * HystrixPropertiesManager.MAX_REQUESTS_IN_BATCH 设置最大请求数
     *
     * 在请求时间段内,有最大的请求数的请求进行合并
     *
     * @param name
     * @return
     */
    @HystrixCollapser(batchMethod = "myBatchMethod",
            scope = com.netflix.hystrix.HystrixCollapser.Scope.GLOBAL,
            collapserProperties = {
                    @HystrixProperty(name = HystrixPropertiesManager.TIMER_DELAY_IN_MILLISECONDS, value = "10"),
                    @HystrixProperty(name = HystrixPropertiesManager.MAX_REQUESTS_IN_BATCH, value = "200")
            })
    @Override
    public Future<String> showList(String name) {
        // @HystrixCollapser 标记的方法 方法体返回 null 即可
        return null;
    }


    /**
     * @HystrixCommand 请求合并执行的方法必须有此注解
     *
     * @param nameList
     * @return
     */
    @HystrixCommand
    public List<String> myBatchMethod(List<String> nameList) {
        return restTemplate.postForObject("http://hystrix-provider/list", nameList, List.class);
    }

ClientController

 @PostMapping("/list")
    public String showName() {
        try {
            return clientService.showList(UUID.randomUUID().toString().substring(0, 5)).get();
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
            return null;
        }
    }

1.7 Hystrix Dashboard

在微服务架构中,为了保证服务的可用性,防止因为某个服务出现故障导致线程阻塞,出现了 Hystrix 熔断器。因

此熔断器成为了一个反映程序健康性的重要指标,Hystrix Dashboard是监控 Hystrix 熔断器健康状况的一个重要

组件,它提供了数据监控和友好的图形化展示界面

1.7.1 添加依赖

<!--hystrix 监控图形化依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
        </dependency>

        <!--监控信息图形化依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

1.7.2 添加注解

package com.cl;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;

/**
 * @author chenlan
 *
 * hystrix 消费者客户端
 * @SpringBootApplication springboot 入口启动类
 * @EnableEurekaClient 启动 eureka client 
 * @EnableHystrix 启动 hystrix 熔断功能
 * @EnableCaching 开启 spring cache 注解支持
 * @EnableHystrixDashboard 启动 hystrix dashboard
 * 
 */
@SpringBootApplication
@EnableEurekaClient
@EnableHystrix
@EnableCaching
@EnableHystrixDashboard
public class EurekaHystrixClientApplication {

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

1.7.3 添加配置文件

management:
  endpoints:
    web:
      exposure:
        include: hystrix.stream

1.8 Feign 中使用 HyStrix 熔断

1.8.1 添加依赖

Feign中包含 Hystrix 中部分功能。所以不需要单独导入 Hystrix 的依赖

<dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

1.8.2 添加配置文件

Feign 自带熔断功能,默认情况下,熔断功能是关闭的,需要在配置中开启熔断功能

server:
  port: 80
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7000/eureka/
spring:
  application:
    name: eureka-feign-client
# feign 开启 hystrix 
feign:
  hystrix:
    enabled: true

1.8.3 添加注解

package com.cl;

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;

/**
 * @author chenlan
 *
 * @EnableEurekaClient 开启 eureka 客户端配置
 * @EnableFeignClients 开启 feign 配置
 *
 */
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class EurekaFeignClientApplication {

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

1.8.4 修改实现类

package com.cl.service;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

/**
 * @Author chenlan
 * @Description TODO
 * @Date 2021/1/22 14:24
 * @Version 1.0
 */

@FeignClient(value = "eureka-provider",fallback = ClientServiceImpl.class)
public interface ClientService {

    /**
     * 调用 注册中心 服务测试方法
     *
     * @return
     */
    @GetMapping(value = "/port")
    String sayHello();


    /**
     * 调用 注册中心 服务并传递参数测试方法
     *
     * @param username
     * @param password
     * @return
     */
    @GetMapping("/login")
    String login(@RequestParam String username, @RequestParam String password);

}


@Component
class ClientServiceImpl implements ClientService{

    @Override
    public String sayHello() {
        return "bad request";
    }

    @Override
    public String login(String username, String password) {
        return "bad request";
    }
}
@FeignClient 在注解中添加fallback属性配置,指定 ClientServiceImpl 为失败逻辑处理类
失败逻辑处理类LocalItemServiceImpl要实现被@FeignClient修饰的LocalItemService接口并使用@Component 注入到Spring 容器中
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值