【微服务笔记07】微服务组件之Hystrix实现请求缓存功能

这篇文章,主要介绍微服务组件之Hystrix实现请求缓存功能。

目录

一、Hystrix组件介绍

1.1、什么是Hystrix

1.2、Hystrix的功能

二、Hystrix实现请求缓存(了解)

2.1、请求缓存介绍

2.2、引入依赖

2.3、服务层代码

(1)第一种缓存实现方式

(2)第二种缓存实现方式

2.4、控制层代码

(1)控制层使用HystrixRequestContext

(2)过滤器使用HystrixRequestContext

2.5、开启hystrix功能

2.6、缓存删除


一、Hystrix组件介绍

1.1、什么是Hystrix

在微服务环境下,服务和服务之间的调用可能会非常的频繁,如果某个微服务的请求非常多,并且这个微服务又无法处理过来,那么此时就可能导致这台服务器出现宕机的情况,一旦出现宕机,那么调用这个微服务的其他微服务系统,也将受到影响,微服务中的请求一直积压,就会导致所有的微服务系统发生崩溃,我们把这种情况称作是:服务雪崩。

为了避免服务雪崩,以及提高微服务的高可用性,于是就出现了Hystrix服务容错组件,Hystrix提供了多种功能,例如:请求缓存、请求合并、线程池隔离、信号量隔离、服务降级、服务熔断、服务监控等等。

  • PS:Hystrix英文单词是豪猪的意思。

1.2、Hystrix的功能

Hystrix提供的功能大概有下面这几个:

  • 请求缓存:可以根据请求的URI将其返回结果暂时缓存起来,下次请求相同的URI就会从缓存中获取数据。
  • 请求合并:可以将客户端多个请求,合并成一个请求,采用批处理的方式调用服务端接口。
  • 线程池隔离:将不同的请求采用不同的线程池隔离,这样即使其中一个线程池的请求出现问题,也不会影响其他线程池的请求。
  • 信号量隔离:项目中存在太多的线程池会降低性能,因为线程池需要来回切换线程,所以就提出了信号量隔离机制。
  • 服务降级:当服务调用过程中出现问题,可以调用备选方法实现快速失败返回。
  • 服务熔断:Hystrix中有个熔断器,当其他微服务不可用的时候,会开启熔断器,此时所有的调用请求都将被阻断,直接快速返回失败。
  • 服务监控:Hystrix提供了一个dashboard控制面板,可以监控并查看请求的调用情况。

二、Hystrix实现请求缓存(了解)

Hystrix提供的请求缓存是一个本地缓存,在分布式环境下不适用,并且实际开发中,也不会使用hystrix的请求缓存功能,因为这个功能太多余了,Hystrix的请求缓存只能够在同一个request请求作用域下才生效,下一次的request请求,会将上一次request的请求缓存给清空。下面介绍如何使用Hystrix实现请求缓存功能。

2.1、请求缓存介绍

hystrix请求缓存是指:在同一个request请求之下,多次调用其他微服务的时候,会将第一次调用结果缓存起来,然后之后每次都是从缓存里面获取数据结果。

可以这么理解,hystrix缓存的是service层方法的返回结果,当在controller层中同一个HTTP请求里面,多次调用service层方法的时候,此时会从缓存中获取数据。

什么叫做同一次HTTP请求呢???

  • 一个用户访问地址【/api/demo】,此时请求进入到controller层,然后我们在controller层方法中,调用了两次service层的方法,那么此时第二次调用service层的方法返回值,将从hystrix缓存里面获取。
  • 在上面这个案例中,就只在一次HTTP请求里面,如果用户访问了两次【/api/demo】,那么此时就不是同一次HTTP请求了,第一次的缓存数据将会被第二次的缓存数据覆盖掉。
  • 当然,前提是这个方法开启了请求缓存功能。

2.2、引入依赖

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

2.3、服务层代码

在service层中,可以有两种方式指定需要开启请求缓存的功能,分别是:

  • 第一种:使用【@HystrixCommand】注解、【@CacheResult】注解以及@CacheResult注解中的cacheKeyMethod属性。
  • 第二种:使用【@HystrixCommand】注解、【@CacheResult】注解、【】注解。

两种方式的区别在于:设置缓存key的方式不同,一个是通过注解指定缓存key,一个是通过自定义方法。

(1)第一种缓存实现方式

package com.gitcode.hystrix.service;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheResult;
import org.springframework.stereotype.Service;

import java.security.SecureRandom;

/**
 * @version 1.0.0
 * @Date: 2023/4/7 20:54
 * @Copyright (C) ZhuYouBin
 * @Description: hystrix 请求缓存案例
 */
@Service
public class HystrixCacheService {


    // 将当前方法的返回保持到缓存里面
    @CacheResult(cacheKeyMethod = "getCacheKey")
    @HystrixCommand(commandKey = "requestCacheKey") // 声明当前方法需要执行 hystrix 命令
    public String requestCache(String id) {
        // TODO 这里可以调用其他的微服务完成一些业务逻辑功能
        // 为了简单些,我这里就生产一个随机数作为返回值
        SecureRandom random = new SecureRandom();
        return random.nextInt() + "";
    }

    /**
     * 定义获取 缓存key 的方法
     * <p>
     *     注意:获取缓存key的方法参数必须和开启请求缓存方法的参数列表相同
     * </p>
     */
    public String getCacheKey(String id) {
        return id;
    }

}

(2)第二种缓存实现方式

// 将当前方法的返回保持到缓存里面
@CacheResult
@HystrixCommand(commandKey = "requestCacheKey") // 声明当前方法需要执行 hystrix 命令
public String requestCache(@CacheKey String id) { // 将当前参数作为缓存key
    // TODO 这里可以调用其他的微服务完成一些业务逻辑功能
    // 为了简单些,我这里就生产一个随机数作为返回值
    SecureRandom random = new SecureRandom();
    return random.nextInt() + "";
}

注意:如果没有指定缓存key,那么此时hystrix会将当前方法中的所有参数作为缓存的key。

2.4、控制层代码

因为hystrix的请求缓存必须是在同一次请求里面,所以必须使用hystrix提供的【HystrixRequestContext】类,初始化请求上下文,并且在每次请求结束之后,需要清除缓存。

如果每一次都在controller里面使用HystrixRequestContext类初始化上下文,那么就会出现很多的重复代码,所以为了简化代码,一般可以在一个过滤器里面使用HystrixRequestContext类完成上下文的初始化和缓存清除功能。

  • 注意:如果没有使用HystrixRequestContext类,那么会抛出异常。
Request caching is not available. Maybe you need to initialize the HystrixRequestContext?

(1)控制层使用HystrixRequestContext

package com.gitcode.hystrix.controller;

import com.gitcode.hystrix.service.HystrixCacheService;
import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @version 1.0.0
 * @Date: 2023/4/7 21:09
 * @Copyright (C) ZhuYouBin
 * @Description:
 */
@RestController
@RequestMapping("/api/hystrix")
public class HystrixController {
    
    @Autowired
    private HystrixCacheService hystrixCacheService;

    @GetMapping("/cache")
    public String hystrixCache() {
        // 1、开启hystrix请求上下文
        HystrixRequestContext context = HystrixRequestContext.initializeContext();
        // 2、TODO 在这之间,就可以多次调用service层代码
        String rtn = hystrixCacheService.requestCache("1001");
        System.out.println("第一次调用返回结果: " + rtn);

        // 参数相同的情况下,第二次调用的返回值是从缓存获取的,所以必定和第一次的返回值相同
        rtn = hystrixCacheService.requestCache("1001");
        System.out.println("第二次调用返回结果: " + rtn);
        
        // 注意: 入参不同
        rtn = hystrixCacheService.requestCache("1002");
        System.out.println("第三次调用返回结果: " + rtn);

        // 3、清除缓存
        context.close();
        return "success";
    }

}

(2)过滤器使用HystrixRequestContext

package com.gitcode.hystrix.filter;

import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext;

import javax.servlet.*;
import java.io.IOException;

/**
 * @version 1.0.0
 * @Date: 2023/4/7 21:23
 * @Copyright (C) ZhuYouBin
 * @Description: hystrix请求缓存过滤器
 */
public class HystrixCacheFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HystrixRequestContext context = null;
        try {
            // 开启 hystrix 请求上下文
            context = HystrixRequestContext.initializeContext();
            // 继续执行后面代码
            chain.doFilter(request, response);
        } finally {
            if (null != context) {
                // 每次请求结束,清除缓存
                context.close();
            }
        }
    }
}
  • SpringBoot将这个过滤器注入IOC容器即可。
package com.gitcode.hystrix.config;

import com.gitcode.hystrix.filter.HystrixCacheFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;

/**
 * @version 1.0.0
 * @Date: 2023/4/7 21:40
 * @Copyright (C) ZhuYouBin
 * @Description:
 */
@Configuration
public class FilterConfig {

    @Bean
    public FilterRegistrationBean<HystrixCacheFilter> hystrixCacheFilter() {
        FilterRegistrationBean<HystrixCacheFilter> registration = new FilterRegistrationBean<>();
        registration.setFilter(new HystrixCacheFilter());
        registration.addUrlPatterns("/*");
        registration.setName("HystrixCacheFilter");
        // 设置优先级别
        registration.setOrder(Ordered.LOWEST_PRECEDENCE);
        return registration;
    }

}

2.5、开启hystrix功能

在启动类上面,需要使用【@EnableCircuitBreaker】注解或者【@EnableHystrix】注解,开启hystrix熔断器功能,否则hystrix将不生效。两个注解的作用是相同的,@EnableHystrix注解就已经包含了@EnableCircuitBreaker注解,但是官方使用的是@EnableCircuitBreaker注解,所以我们也就按照官方的用法,直接使用@EnableCircuitBreaker注解开启功能即可。

2.6、缓存删除

hystrix也提供了删除缓存的注解@CacheRemove,当进行一些新增、删除、更新操作的时候,此时缓存中的数据就可能不是最新的了,所以这个时候就需要将缓存中的数据删除。使用【@CacheRemove】注解,同时指定【commandKey】属性,这就是告诉hystrix需要删除哪个命令下的所有缓存。

  • 注意:这里的commanKey需要和请求缓存中@HystrixCommand注解中指定的commanKey属性相同。
/**
 * 删除 commandKey 等于 requestCacheKey 中的所有缓存
 */
@CacheRemove(commandKey = "requestCacheKey")
public void updateData() {
    // TODO 做一些更新操作
}

到此,Hystrix实现请求缓存就介绍完啦。

综上,这篇文章结束了,主要介绍微服务组件之Hystrix实现请求缓存功能。

猜你喜欢

转载自blog.csdn.net/qq_39826207/article/details/130019705
今日推荐