首页 > 其他分享 >接口数据做缓存,响应飞快似天神

接口数据做缓存,响应飞快似天神

时间:2024-12-16 15:31:50浏览次数:6  
标签:缓存 return name 天神 接口 import public String

概述

在商业中 “现金为王”,而在互联网整个软件世界中,有一个与之相近的说法是“缓存为王”。

本文我们重点说明缓存方向:将方法的返回值缓存起来,下次再调用该方法时,如果方法的参数与之前调用时的参数相同,则会直接返回缓存中的结果,而不会再执行方法体。这样可以提高方法的执行效率

优点

  • 提高性能:缓存可以将方法的结果存储在内存中,后续对相同参数的方法调用可以直接从缓存中获取结果,避免重复计算,提高方法的执行效率。
  • 减轻数据库压力:对于需要频繁访问数据库的方法,可以将结果缓存在内存中,减轻数据库的压力,提高数据库的响应速度。
  • 简化代码逻辑:通过使用缓存,可以避免编写复杂的手动缓存代码或条件判断逻辑,使代码更简洁、可读性更好。

缺点

  • 内存占用:缓存的数据存储在内存中,因此如果缓存的数据量过大,会消耗大量的内存资源。在使用缓存时需要合理安排系统的内存和缓存容量。
  • 数据一致性:使用缓存后,需要注意维护数据的一致性。当数据发生变化时,需要及时更新缓存,以避免脏数据的问题。可通过合理设计缓存策略和使用缓存失效机制来解决这个问题。
  • 缓存失效:缓存的有效期限需要合理设置。如果缓存的有效期太长,可能导致数据更新不及时;如果缓存的有效期太短,可能增加重复执行代码的次数。需要根据具体业务场景来选择合适的缓存有效期。
  • 缓存击穿:当某个缓存条目在缓存失效时,同时有大量的并发请求到达时,可能会导致缓存击穿问题,即大量请求直接击穿到数据库。可以通过加锁或使用分布式锁等机制来避免缓存击穿。

注解介绍

Spring缓存机制通过 @EnableCaching开启,配合 @Cacheable、 @CachePut、 @CacheEvict等注解,为Java应用提供了一种声明式管理缓存的方式。这些注解使得缓存配置变得简洁明了,允许开发者轻松实现数据的自动缓存、更新和清除,从而优化应用性能,减少不必要的计算和数据访问开销。

1. 启用缓存支持

@EnableCaching 注解用于在Spring配置类上启用缓存管理功能。

  • 注解属性介绍

无属性。

  • 注解业务案例
1.  @Configuration
    
2.  @EnableCaching
    
3.  public class CacheConfig {
    
4.      // 配置其他Bean`
    
5.  }
    

2. 缓存结果

@Cacheable 注解用于声明一个方法的结果是可缓存的。

  • 注解属性介绍

  • valuecacheNames: 指定缓存名称,可以是单个或多个。

  • key: 指定缓存键的SpEL表达式。

  • condition: 指定缓存的条件SpEL表达式。

  • unless: 指定不缓存的条件SpEL表达式。

  • 注解业务案例 单一缓存名称和键:

1.  @Cacheable("books")
    
2.  public Book findBookById(String id) {
    
3.      // 业务逻辑
    
4.  }

多个缓存名称和条件:

1.  @Cacheable(value = {"books", "archivedBooks"}, condition = "#id.length() > 10")
2.  public Book findBookWithComplexKey(String id) {
3.      // 业务逻辑
4.  }
    
6.  @Service
7.  public class MyService {
    
9.      /**
    
10.       `* 一个使用 @Cacheable 所有属性的案例。`
    
11.       `*` 
    
12.       `* @param id 用户ID`
    
13.       `* @return 返回用户对象`
    
14.       `*/
    
15.      @Cacheable(
    
16.          value = "users",          // 缓存名称`
    
17.          key = "#id",              // 缓存键,使用SpEL表达式`
    
18.          condition = "#id.length() > 3",  // 缓存条件,只有当ID长度大于3时才缓存`
    
19.          unless = "#result == null" // 除非条件,如果结果为null,则不缓存`
    
20.      )
21.      public User findUserById(String id) {
    
22.          // 模拟数据库查询操作,这里假设ID长度小于3时没有结果`
    
23.          if (id == null || id.length() <= 3) {
    
24.              return null;
    
25.          }
    
26.          return performDatabaseQuery(id);
    
27.      }
    

29.      private User performDatabaseQuery(String id) {
    
30.          // 模拟数据库查询逻辑`
    
31.          return new User(id, "Name based on ID");
    
32.      }
    
33.  }

3. 更新缓存

@CachePut 注解用于在方法执行后更新缓存。

  • 注解属性介绍

@Cacheable相同。

  • 注解业务案例
 1.  @CachePut("books")

 2.  public Book updateBookDetails(String id, Book details) {

 3.  // 业务逻辑

 4.  }

 5.  

 6.  @Service

 7.  public class MyService {

 8.  

 9.  /**

 10. * 使用 @CachePut 所有属性的案例。

 11. *

 12. * @param user 用户对象,包含ID

 13. * @return 更新后的用户对象

 14. */

 15. @CachePut(

 16. 	value = "users", // 缓存名称

 17. 	key = "#user.id", // 缓存键,使用SpEL表达式

 18. 	condition = "#user.age > 18" // 条件,只有当用户年龄大于18时才更新缓存

 19. )

 20. public User updateUserProfile(User user) {

 21. // 模拟更新用户信息的业务逻辑

 22. return performUpdate(user);

 23. }

 24. 

 25. private User performUpdate(User user) {

 26. // 模拟更新逻辑

 27. user.setName("Updated Name");

 28. return user;

 29. }

 30. }

4. 清除缓存

@CacheEvict 注解用于在方法执行后清除缓存。

注解属性介绍

  • valuecacheNames: 指定缓存名称,可以是单个或多个。

  • allEntries: 清除所有缓存项。

  • condition: 指定清除缓存的条件SpEL表达式。

注解业务案例

清除特定缓存名称的条目:

1.  @CacheEvict("books")
    
2.  public void deleteBook(String id) {
    
3.      // 业务逻辑
    
4.  }

清除所有缓存名称的所有条目:

1.  @CacheEvict(allEntries = true)
    
2.  public void clearAllCaches() {
    
3.      // 业务逻辑
    
4.  }

5. 组合缓存操作

@Caching 注解用于组合多个缓存操作。

  • 注解属性介绍

value: 包含多个缓存操作的数组。

  • 注解业务案例

1.  @Caching(
    
2.      cacheable = {@Cacheable("books")},
    
3.      put = {@CachePut("books")},
    
4.      evict = {@CacheEvict("archivedBooks")}
    
5.  )
    
6.  public Book processBook(String id, Book details) {
    
7.      // 业务逻辑
    
8.  }
    

代码演示

redis依赖安装

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

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>2.0.21</version>
        </dependency>
  • redis配置文件
spring:
  redis:
    # 连接地址
    host: "127.0.0.1"
    # 端口
    port: 6379
    # 数据库
    database: 1
    password: helloworld
    # 连接超时
    connect-timeout: 5s
    # 读超时
    timeout: 5s

    # Lettuce 客户端的配置
    lettuce:
      # 连接池配置
      pool:
        # 最小空闲连接
        min-idle: 0
        # 最大空闲连接
        max-idle: 8
        # 最大活跃连接
        max-active: 8
        # 从连接池获取连接 最大超时时间,小于等于0则表示不会超时
        max-wait: -1ms

接口缓存配置类

框架给我们提供了 @Cacheable 注解用于缓存方法返回内容。但是 @Cacheable 注解 不能 定义缓存 有效期。这样的话在一些需要自定义缓存有效期的场景就不太实用。

框架给我们提供的 RedisCacheManager 类,是 准对这一块的 就配置类, 我们可以根据此类,自己实现 缓存有效期 的功能。

package com.xuzhongkj.configrations;


import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCache;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import javax.validation.constraints.NotNull;
import java.time.Duration;
import java.util.Objects;


@Configuration
@EnableCaching
public class CustomRedisCacheManager {

    private String keyPreFix = "whero";

    private RedisSerializer<String> keySerializer() {
        return new StringRedisSerializer();
    }

    private RedisSerializer<Object> valueSerializer() {
        // fastjson 中的类
        return new GenericFastJsonRedisSerializer();
    }


    /*
     * @description 配置自定义 缓存管理器: RedisCacheManager
     **/
    @Bean
    public RedisCacheManager redisCacheManager(RedisTemplate redisTemplate) {
        // 缓存注解 配置:
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                //设置 key 为String
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))
                //设置 value 为自动转Json的Object
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer()))
                // 使用 prefixCacheNameWith 需要注意系统自动拼接的双”:“问题
                .computePrefixWith(cacheName -> keyPreFix + ":" + cacheName + ":");


        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(Objects.requireNonNull(redisTemplate.getConnectionFactory()));

        RedisCacheManager redisCacheManager = new CustomRedisCacheManagerToolsClass(redisCacheWriter, config);
        return redisCacheManager;
    }
}


/**
 * @Title: 自定义redis缓存管理器, 为了实现 缓存有效期的 动态性
 */
class CustomRedisCacheManagerToolsClass extends RedisCacheManager {

    public static final String SEPARATOR = "#";

    public CustomRedisCacheManagerToolsClass(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {
        super(cacheWriter, defaultCacheConfiguration);
    }

    @Override
    protected RedisCache createRedisCache(@NotNull(message = "缓存名称不能为空") String name, RedisCacheConfiguration cacheConfiguration) {
        // 举例说明: name = "bookCourse:getCourseId#7D"
        if (name.contains(SEPARATOR)) {
            String[] spelStr = name.split(SEPARATOR);
            String key = spelStr[0];
            String valueStr = spelStr[1];

            int length = valueStr.length();

            if (length >= 2) {
                String cycleTimeStr = valueStr.substring(0, length - 1);

                if (cycleTimeStr.matches("\\d+")) {
                    long cycleTime = Long.parseLong(cycleTimeStr);

                    String cycleUnit = valueStr.substring(length - 1, length);

                    if (cycleUnit.equals("D")) {//表示天
                        return super.createRedisCache(key, cacheConfiguration.entryTtl(Duration.ofDays(cycleTime)));
                    }
                    if (cycleUnit.equals("H")) {//表示小时
                        return super.createRedisCache(key, cacheConfiguration.entryTtl(Duration.ofHours(cycleTime)));
                    }
                    if (cycleUnit.equals("M")) {//表示分钟
                        return super.createRedisCache(key, cacheConfiguration.entryTtl(Duration.ofMinutes(cycleTime)));
                    }
                    if (cycleUnit.equals("S")) {//表示秒
                        return super.createRedisCache(key, cacheConfiguration.entryTtl(Duration.ofSeconds(cycleTime)));
                    } else {
                        // 都不是则使用默认配置
                        return super.createRedisCache(name, cacheConfiguration);
                    }
                }
            }
        }
        return super.createRedisCache(name, cacheConfiguration);
    }
}

这里面简单对 RedisCacheConfiguration 缓存配置做一下说明:

  • serializeKeysWith():设置 Redis 的 key 的序列化规则。
  • erializeValuesWith():设置 Redis 的 value 的序列化规则。
  • computePrefixWith():计算 Redis 的 key 前缀。
  • cacheConfiguration.entryTtl(): 设置 @Cacheable 注解缓存的有效期。

测试使用

service层

package com.xuzhongkj.services.impls;

import com.xuzhongkj.pojo.Userswtt;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
public class WttService {

    @Cacheable(cacheNames = {"bookCourse:getCourseId#7M"}, key = "#name")
    public Userswtt getName (String name) {
        System.out.println("-------  创建 -----------"+name);
        Userswtt u = new Userswtt();
        u.setOpenid("qwejlksdfjkdf");
        u.setAge(11);
        u.setAvatar("https:/asdfj;askdf");
        u.setUid(123L);
        return  u;
    }

    @CachePut(cacheNames = {"bookCourse:getCourseId#7M"}, key = "#name")
    public Userswtt EdtName (String name) {
        System.out.println("---------- 更新 --------"+name);
        Userswtt u = new Userswtt();
        u.setOpenid("qwejlksdfjkdf");
        u.setAge(22);
        u.setAvatar("https:/asdfj;askdf");
        u.setUid(123L);
        return  u;
    }

    @CacheEvict(cacheNames = {"bookCourse:getCourseId#7M"}, key = "#name")
    public Userswtt DelName (String name) {
        System.out.println("--------- 删除 ---------"+name);
        Userswtt u = new Userswtt();
        return  u;
    }
}

controller 层

@RestController
@Slf4j
@RequestMapping("/test")
public class WttTest {

    @Autowired
    private WttService wttService;

    @GetMapping("/wtt")
    public Userswtt a1() {
            Userswtt name = wttService.getName("testResObj");
            return name;
    }

    @GetMapping("/wtt2")
    public Userswtt a2() {
            Userswtt name = wttService.EdtName("testResObj");
            return name;
    }

    @GetMapping("/wtt3")
    public Userswtt a3() {
            return wttService.DelName("testResObj");
    }

redis中键值对的存储情况:

  • key:

whero:bookCourse:getCourseId:testResObj

  • value

{“@type”:“com.xuzhongkj.pojo.Userswtt”,“age”:11,“avatar”:“https:/asdfj;askdf”,“openid”:“qwejlksdfjkdf”,“uid”:123}

标签:缓存,return,name,天神,接口,import,public,String
From: https://blog.csdn.net/weixin_45541665/article/details/144502630

相关文章

  • H5清除页面缓存
    if($request_filename~.*\.(htm|html)$){add_headerCache-Control"no-cache,no-store";} vue打包的话,js|css|png/jpg等文件名都加上了hash值。但是index.html文件并没有,index.html是一个主入口,里面加载的是js等其他文件。主要就是缓存了这个html文......
  • IO应用程序接口&设备驱动程序接口
    IO应用程序接口&设备驱动程序接口‍​​‍一、输入/输出应用程序接口背景:在设备独立软件层向上提供各种各样的输入/输出应用程序接口的原因是:用户层的应用程序无法用一个统一的系统调用接囗来完成所有类型设备的I/O​​‍三种输入/输出应用程序接口:字符设备接口块设......
  • 与后端联调接口时,你总结下会出现哪些问题及如何解决?
    在与后端联调接口时,前端开发可能会遇到的问题多种多样。以下是一些常见的问题及其解决方法:1.接口返回数据格式问题问题:后端接口返回的数据格式可能与前端期望的不一致,如返回字符串而非JSON对象。解决方法:确保后端返回正确的数据格式,如JSON。前端在接收数据时进行格式校验,如......
  • 探秘 IIC 与 SPI:软件模拟与硬件接口的抉择之谜
    一、IIC软件模拟:受限中的灵活应变在嵌入式系统的通信世界里,IIC常采用软件模拟的方式开展工作,这背后有着诸多考量。首先,硬件资源的限制是一个重要因素。不少微控制器并没有内置功能完备的IIC硬件模块,甚至压根就不存在这样的模块。而软件模拟IIC则巧妙地绕开了这一硬件短......
  • 为什么 Spring 循环依赖需要三级缓存,二级不够吗?
    Spring循环依赖解决机制中引入了三级缓存,这是因为仅使用二级缓存无法灵活处理代理Bean的早期暴露需求。以下是为什么需要三级缓存的详细分析:1.二级缓存的局限性二级缓存通常用于存储早期暴露的未完全初始化的Bean实例。但在AOP代理场景下,Bean可能需要在完全初始化之前暴露其代......
  • 探索Spring之利剑:ApplicationContext接口
    嘿,开发者们!你是否曾在构建Spring应用时,感到困惑于那些复杂的配置和神秘的容器?今天,我们将揭开Spring中一个核心接口——ApplicationContext​的神秘面纱。这不仅是一篇技术文章,更是一次深入Spring心脏的探险之旅。系好安全带,我们即将启程!......
  • java如何请求接口然后终止某个线程
    Java请求接口并终止线程在Java开发中,处理多线程操作是常见需求。有时我们需要在请求某个接口后,根据接口返回结果或其他条件,终止某个线程的执行。本文将详细介绍如何在Java中请求接口并终止特定线程的方法。一、请求接口1.1使用 HttpURLConnectionJava提供了多种方式进行HTTP......
  • C++ OCR文字识别api接口
    一.引言文字识别,也称为光学字符识别(OpticalCharacterRecognition,OCR),是一种将不同形式的文档(如扫描的纸质文档、PDF文件或数字相机拍摄的图片)中的文字转换成可编辑和可搜索的数据的技术。随着技术的发展,文字识别技术已经成为信息管理、自动化办公和智能系统的关键组成部分......
  • 易语言OCR文字识别api接口
     一.引言文字识别,也称为光学字符识别(OpticalCharacterRecognition,OCR),是一种将不同形式的文档(如扫描的纸质文档、PDF文件或数字相机拍摄的图片)中的文字转换成可编辑和可搜索的数据的技术。随着技术的发展,文字识别技术已经成为信息管理、自动化办公和智能系统的关键组成部......
  • Python OCR文字识别api接口
     一.引言文字识别,也称为光学字符识别(OpticalCharacterRecognition,OCR),是一种将不同形式的文档(如扫描的纸质文档、PDF文件或数字相机拍摄的图片)中的文字转换成可编辑和可搜索的数据的技术。随着技术的发展,文字识别技术已经成为信息管理、自动化办公和智能系统的关键组成部......