十三、异步并发进修——请求缓存与请求合并实战

十三、异步并发进修——请求缓存与请求合并实战

在这里插入图片描述

1. 请求缓存

在高并发系统中,针对同一请求的重复调用会造成资源浪费,尤其是在查询操作中。如果对同一商品或数据进行多次查询,我们可以通过请求缓存机制来避免重复调用,从而提升系统的性能。以下是使用 Hystrix 和 CompletableFuture 实现请求缓存的具体方法。

1.1 使用 Hystrix 请求级别缓存

Hystrix 提供了请求级别的缓存功能,这意味着在一个请求的上下文中,可以通过共享缓存来避免重复调用。这种机制在对同一数据进行多次查询时非常有用。

代码实现

import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandProperties;
import com.netflix.hystrix.HystrixRequestContext;

public class GetProductServiceCommand extends HystrixCommand<String> {
   

    private ProductService productService;
    private Long id;

    public GetProductServiceCommand(ProductService productService, Long id) {
   
        super(Setter.withGroupKey(() -> "ProductServiceGroup")
                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                        .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD)
                        .withRequestCacheEnabled(true)));  // 启用请求缓存
        this.productService = productService;
        this.id = id;
    }

    @Override
    protected String run() {
   
        // 调用真实的服务来获取产品信息
        return productService.getProduct(id);
    }

    @Override
    protected String getCacheKey() {
   
        // 返回用于缓存的唯一键
        return "product-" + id;
    }
}

// 使用 Hystrix 请求缓存
public void executeCommands() {
   
    HystrixRequestContext context = HystrixRequestContext.initializeContext();
    try {
   
        ProductService productService = new ProductService();
        GetProductServiceCommand command1 = new GetProductServiceCommand(productService, 1L);
        GetProductServiceCommand command2 = new GetProductServiceCommand(productService, 1L);

        String result1 = command1.execute();
        String result2 = command2.execute();

        // 第一个请求不使用缓存,第二个请求命中缓存
        assert !command1.isResponseFromCache();  // 第一次请求,不使用缓存
        assert command2.isResponseFromCache();   // 第二次请求,使用缓存
    } finally {
   
        context.shutdown();
    }
}

关键点解析

  1. 缓存键配置:通过实现 getCacheKey 方法,定义用于缓存的唯一键。在此示例中,"product-" + id 是缓存的键。

  2. 启用缓存:通过配置 withRequestCacheEnabled(true) 启用请求级别的缓存功能。这样在同一个请求上下文中,多次调用相同的命令会直接从缓存中获取结果。

  3. 缓存使用验证:通过 isResponseFromCache 方法,可以验证某个请求是否命中了缓存。第一次调用通常不会命中缓存,而后续相同的调用将会从缓存中读取数据。

场景应用

这种请求缓存的机制非常适合在查询类服务中使用,特别是在电商、库存查询等场景中。如果系统需要频繁查询同一商品的库存状态,通过请求级别的缓存可以避免重复调用,从而提升响应效率并减少对后端服务的压力。

1.1.1 Hystrix 请求级别缓存实现

在 Spring 项目中,可以通过整合 Hystrix 来实现请求级别的缓存功能。Hystrix 提供了强大的请求缓存机制,可以在一个请求的上下文中对相同的服务调用进行缓存,从而避免重复调用,提高系统性能。

1. 依赖与配置

在 Spring 项目中使用 Hystrix 请求缓存,需要添加以下依赖:

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

确保 Spring Boot 项目已经启用了 Hystrix 支持,通常在主类上加上 @EnableCircuitBreaker@EnableHystrix 注解:

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

2. Hystrix 请求缓存实现

通过继承 HystrixCommand 来实现带有请求级别缓存的服务调用。

Step 1: 实现 HystrixCommand 并配置缓存

import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandProperties;

public class GetProductCommand extends HystrixCommand<String> {
   

    private final ProductService productService;
    private final Long productId;

    public GetProductCommand(ProductService productService, Long productId) {
   
        super(Setter.withGroupKey(() -> "ProductServiceGroup")
            .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                .withRequestCacheEnabled(true)));  // 启用请求级别缓存
        this.productService = productService;
        this.productId = productId;
    }

    @Override
    protected String run() throws Exception {
   
        return productService.getProductById(productId);
    }

    @Override
    protected String getCacheKey() {
   
        return "product_" + productId;
    }
}

Step 2: 在请求中使用 Hystrix 缓存

public class ProductService {
   

    public String getProductById(Long id) {
   
        // 模拟服务调用,可能是一个远程服务或数据库查询
        return "Product_" + id;
    }

    public void executeCommands() {
   
        HystrixRequestContext context = HystrixRequestContext.initializeContext();
        try {
   
            GetProductCommand command1 = new GetProductCommand(this, 1L);
            GetProductCommand command2 = new GetProductCommand(this, 1L);

            String result1 = command1.execute();  // 第一次请求
            String result2 = command2.execute();  // 第二次请求

            assert !command1.isResponseFromCache(); // 第一次请求不使用缓存
            assert command2.isResponseFromCache();  // 第二次请求命中缓存
        } finally {
   
            context.shutdown();
        }
    }
}

3. 关键点解析

  1. 缓存键配置:通过重写 getCacheKey 方法为每个请求生成唯一的缓存键,Hystrix 会根据这个键来存储和检索缓存数据。
  2. 启用缓存:通过 withRequestCacheEnabled(true) 配置启用请求缓存。
  3. 请求上下文:使用 HystrixRequestContext 来初始化请求上下文,确保同一请求中的缓存数据能够被复用。

4. Hystrix 缓存的工作原理

Hystrix 的请求缓存是通过 HystrixRequestContext 实现的。在同一个请求上下文中,所有使用相同缓存键的请求会共享缓存数据。这种方式适用于在一个请求中需要多次访问同一服务的场景,可以有效减少重复调用,提高系统性能。

通过这种方式,Spring 项目可以实现类似 Hystrix 的请求级别缓存功能,简化了复杂的请求缓存管理逻辑。

1.1.2 Spring 项目中的应用

以下是一个更符合实际开发环境的完整示例,展示如何在 Spring Boot 项目中应用 Hystrix 请求级别缓存,并结合实际的 Spring Bean 管理。

1. 项目结构

项目结构如下:

src
├── main
│   ├── java
│   │   ├── com
│   │   │   └── example
│   │   │       ├── HystrixApplication.java
│   │   │       ├── controller
│   │   │       │   └── ProductController.java
│   │   │       ├── service
│   │   │       │   ├── ProductService.java
│   │   │       │   ├── GetProductServiceCommand.java
│   │   │       │   └── impl
│   │   │       │       ├── ProductServiceImpl.java
│   │   │       │       └── GetProductServiceCommand.java
│   │   └── resources
│       └── application.yml

2. 配置依赖

pom.xml 中添加依赖:

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

3. 创建主类

HystrixApplication 中启用 Hystrix:

package  com.yyl.study;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;

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

4. 创建 ProductService 接口和实现类

package com.yyl.study.service;

/**
 * 产品信息获取接口
 *
 * @author yangjian
 * @date 2024-08-20 20:16:38
 */
public interface ProductService {
   
    String getProductById(Long id);
}

package  com.yyl.study.service.impl;

import  com.yyl.study.service.ProductService;
import org.springframework.stereotype.Service;

@Service
public class ProductServiceImpl implements ProductService {
   
    @Override
    public String getProductById(Long id) {
   
        // 模拟从数据库或远程服务获取产品信息
        return "Product Info for ID: " + id;
    }
}

5. 创建 GetProductServiceCommand

package  com.yyl.study.service.impl;

import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandProperties;
import  com.yyl.study.service.ProductService;

public class GetProductServiceCommand extends HystrixCommand<String> {
   

    private final ProductService productService;
    private final Long productId;

    public GetProductServiceCommand(ProductService productService, Long productId) {
   
        super
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

呆萌宝儿姐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值