微服务架构Day09-SpringBoot之缓存

2022-01-22 10:32:09 浏览数 (1)

JSR107

Java Caching定义5个核心接口:CachingProvider,CacheManager,Cache,Entry,Expiry

  • CachingProvider:定义了创建,配置,获取,管理和控制多个CacheManager.一个应用可以在运行期间访问多个CachingProvider
  • CacheManager:定义了创建,配置,获取,管理和控制多个唯一命名的Cache,这些Cache存在于CacheManager的上下文中,一个CacheManager仅被一个CacheProvider所拥有
  • Cache:是一个类似Map的数据结构并临时存储以key为索引的值,一个Cache仅被一个CacheManager所拥有
  • Entry:是一个存储在Cache中的key-value键值对
  • Expiry:每一个存储在Cache中的条目有一个定义的有效期,一旦超过这个时间,条目为过期状态.一旦过期,条目将不可访问,更新和删除.缓存有效期可以通过ExpiryPolicy设置
代码语言:javascript复制
<dependency>
	<groupId>javax.cache</groupId>
	<artifactId>cache-api</artifactId>
</dependency>

搭建缓存基本环境

代码语言:javascript复制
1.导入数据库文件,创建,创建表
2.创建javabean封装数据
3.整合MyBatis操作数据库
	1.配置数据源信息
	2.使用Mybatis,创建数据库操作语句

缓存的使用

  • 步骤
代码语言:javascript复制
1.开启基于注解的缓存 		@EnableCaching
2.标注缓存注解		

缓存重要概念

  • Cache: 缓存接口,定义缓存操作.实现有:RedisCache,EhCacheCache,ConcurrentMapCache
  • CacheManager: 缓存管理器,管理各种缓存组件
  • keyGenerator: 缓存数据时key生成策略
  • serialize: 缓存数据时value序列化策略

缓存注解

  • @Cacheable: 主要针对方法配置,根据方法的请求参数对结果进行缓存:以后再要相同的数据,直接从缓存中获取,不再调用方法
代码语言:javascript复制
属性:
	cacheNames/value:指定缓存组件的名字,将方法的返回结果放在缓存中,是数组的方式,可以指定多个缓存(CacheManager管理多个Cache组件,对缓存的真正CRUD操作在Cache组件中,每一个缓存组件有自己唯一一个名字)
	key:缓存数据使用的key,可以用key指定键值,默认使用方法参数的值(SpEL:methodName(当前被调用的方法名),
																  method(当前被调用的方法),
																  target(当前被调用的目标对象),
																  targetClass(当前被调用的目标对象类),
																  args(当前被调用的方法参数列表),
																  caches(当前方法调用使用的缓存列表),
																  argument name(方法参数的名字-#参数),
																  result(方法执行后的结果返回值))
	keyGenerator:key的生成器,可以指定key的生成器组件.
    cacheManager:指定缓存管理器,
    cacheResolver:指定缓存解析器,和缓存管理器一样
    condition:指定符合条件的情况下才进行缓存
    unless:否定缓存-当unless指定的条件为true,方法的返回值就不会缓存.可以获取到结果进行判断
    sysnc:是否使用异步模式,不支持unless属性

	key和keyGenerator只要出现一个
	cacheManager和cacheResolver只要出现一个
  • @CacheEvict: 清空缓存
  • @CachePut: 保证方法被调用,结果被缓存,更新缓存数据
  • @EnableCaching: 开启基于注解的缓存

缓存的工作原理

默认使用的是ConcurrentMapCache组件中的CacheManager进行缓存的,将数据保存在 ConcurrentMap<object,object> 中

自动配置类:

代码语言:javascript复制
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration

缓存配置类

代码语言:javascript复制
org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration
org.springframework.boot.autoconfigure.cache.JCacheCacheConfiguration	(JSR107)
org.springframework.boot.autoconfigure.cache.EhCacheCacheConfiguration
org.springframework.boot.autoconfigure.cache.HazelcastCacheConfiguration
org.springframework.boot.autoconfigure.cache.InfinispanCacheConfiguration
org.springframework.boot.autoconfigure.cache.CouchbaseCacheConfiguration
org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration
org.springframework.boot.autoconfigure.cache.CaffeineCacheConfiguration
org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration
org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration
  • 默认SimpleCacheConfiguration生效: 1.给容器中注册CacheManager:ConcurrentMapCacheManager 2.ConcurrentMapCacheManager可以获取和创建ConcurrentMapCache类型的缓存组件 3.ConcurrentMapCache的作用是将数据保存在ConcurrentMap中

缓存的运行流程

@Cacheable

方法运行之前,CacheManager先获取相应的缓存,按照CacheNames指定的名字获取,,第一次获取缓存如果没有Cache组件会自动创建缓存组件

在Cache中使用key查找缓存内容,key是通过KeyGenerator生成的,默认就是方法的参数

如果没有查到缓存内容就会调用目标方法

将目标方法返回的结果,放进缓存中

流程核心:

  • 使用CacheManager(ConcurrentMapCacheManager)按照CacheNames指定的名字获取Cache(ConcurrentMapCache)组件
  • key是使用keyGenerator生成的,默认使用SimpleKeyGenerator生成key

@CachePut 既调用方法,又更新缓存数据(对数据库修改操作,会将结果保存在缓存中)

@CacheEvict 清空缓存:当数据库中数据被删除时,执行删除缓存中的数据


@Caching

  • 定义复杂的缓存规则
  • 如果在定义的复杂缓存规则中有 @CachePut 或则 @CacheEvict(beforInvocation=true) ,这个注解就是在目标方法执行后执行
代码语言:javascript复制
@Caching(
          cacheable = {
                  @Cacheable(value = "emp",key = "#emp.lastName"),
                  @Cacheable(value = "emp",key = "#emp.id")
          },
          put = {
                  @CachePut(value = "emp",key = "#emp.lastName"),
                  @CachePut(value = "emp",key = "#emp.id")
          },
          evict = {
                  @CacheEvict(value = "emp",key = "#emp.lastName"),
                  @CacheEvict(value = "emp",key = "#emp.id")
          }
  )

  • @CacheConfig
    • 配置缓存中的所有的公共配置

缓存中间件 Redis

  • Redis: Redis是一个开源的内存中的数据结构存储系统,可以用作数据库,缓存消息中间件
  • 操作工具:Redis Desktop Manager

Redis配置

  • 下载Redis-Windows版本
  • 启动Redis服务端:
    • 直接双击redis-server.exe文件
    • 或者在命令行中输入start redis-server.exe
  • 启动Redis客户端:
    • 启动Redis服务端完成之后
    • 直接双击redis-cli.exe文件
    • 或者在命令行输入start redis-cli.exe
  • 设置Redis密码:
    • 命令方式:
      • 获取旧密码 : config get requirepass
      • 设置新密码 : config set requirepass
    • config文件方式:
      • 打开配置文件redis.windows.conf
      • 修改配置文件中的reqiurepass

整合Redis缓存

  • 在pom.xml中引入redis依赖
代码语言:javascript复制
<dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  • 配置redis,在application.properties中配置redis
代码语言:javascript复制
spring.redis.host=192.168.32.242
  • RedisTemplate:(操作k-v都是对象)
代码语言:javascript复制
	@Bean
    @ConditionalOnMissingBean(
        name = {"redisTemplate"}
    )
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        RedisTemplate<Object, Object> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }
  • 保存对象时,默认使用的是JDK的序列化机制,将序列化后的数据保存到redis中
  • 为了增强Redis数据库中的数据可读性:
    • 将对象数据以json方式保存:
      • 将对象转化为json
      • 配置redisTemplatejson序列化规则,使用Jackson2JsonRedisSerializer进行数据的序列化
代码语言:javascript复制
 @Configuration
 public class MyRedisConfig {
 @Bean
 public RedisTemplate<Object, Employee> empRedisTemplate(RedisConnectionFactory redisConnectionFactory){
     RedisTemplate<Object,Employee> redisTemplate=new RedisTemplate<Object,Employee>();
     redisTemplate.setConnectionFactory(redisConnectionFactory);
     Jackson2JsonRedisSerializer<Employee> serializer = new Jackson2JsonRedisSerializer<Employee>(Employee.class);
     redisTemplate.setDefaultSerializer(serializer);
     return redisTemplate;
 }
}
代码语言:javascript复制
Redis常见的数据类型:
	String-字符串
	List-列表
	Set-集合
	Hash-散列
	ZSet-有序集合

redisTemplate.opsForValue()--String(字符串)
redisTemplate.opsForList()--List(列表)
redisTemplate.opsForSet()--Set(集合)
redisTemplate.opsForHash()--Hash(散列)
redisTemplate.opsForZSet()--ZSet(有序集合)
  • StringRedisTemplate(操作k-v都是字符串) 在RedisAutoConfiguration中:
代码语言:javascript复制
@Bean
    @ConditionalOnMissingBean
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

在StringRedisTemplate中:

代码语言:javascript复制
public class StringRedisTemplate extends RedisTemplate<String, String> {
    public StringRedisTemplate() {
        this.setKeySerializer(RedisSerializer.string());
        this.setValueSerializer(RedisSerializer.string());
        this.setHashKeySerializer(RedisSerializer.string());
        this.setHashValueSerializer(RedisSerializer.string());
    }

    public StringRedisTemplate(RedisConnectionFactory connectionFactory) {
        this();
        this.setConnectionFactory(connectionFactory);
        this.afterPropertiesSet();
    }

    protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) {
        return new DefaultStringRedisConnection(connection);
    }
}
代码语言:javascript复制
Redis常见的数据类型:
	String-字符串
	List-列表
	Set-集合
	Hash-散列
	ZSet-有序集合

stringRedisTemplate.opsForValue()--String(字符串)
stringRedisTemplate.opsForList()--List(列表)
stringRedisTemplate.opsForSet()--Set(集合)
stringRedisTemplate.opsForHash()--Hash(散列)
stringRedisTemplate.opsForZSet()--ZSet(有序集合)

Redis服务治理

  • Redis默认是惰性删除:
    • 过期的Redis的不会删除
    • 获取key时会检查key是否有效,如果无效才会删除
  • Redis的惰性删除机制会造成Redis缓存中保存很多过期的key. 可以使用定时删除过期的key的策略解决
缓存雪崩
  • 缓存雪崩: 同一时刻rediskey全部失效
  • 解决方案:Redis的过期时间设置为随机
缓存击穿
  • 缓存击穿: 同一时刻缓存中没有数据,请求转向数据库
  • 解决方案:
    • 第一次请求如果没有数据,直接返回空
    • 然后发送一个消息队列进行数据库同步
    • 第二次请求再从缓存中获取数据

自定义CacheManager

  • CacheManagerCustomizers: 定制缓存规则
  • 可以在方法中使用缓存管理器获取缓存,调用操作API对缓存中的数据进行操作

0 人点赞