目录
Table of Contents
一、SpringBoot与缓存
1.JSR-107(Java缓存规范):CachingProvider、CacheManager、Cache、Entry、Expiry
2.Spring缓存抽象
二、缓存(redis)
三、MQ(rabbitmq)
1.mq核心概念
2.RabbitMQ简介
大纲:JSR-107(底层了解)、Spring缓存抽象(掌握)、整合Redis(熟练)
CachingProvider定义了创建、配置、获取、管理和控制多个CacheManager。一个应用可以在运行期访问多个CachingProvider。
CacheManager定义了创建、配置、获取、管理和控制多个唯一命名的Cache,这些Cache存在于CacheManager的上下文中。一个CacheManager仅被一个CachingProvider所拥有。
Cache是一个类似Map的数据结构并临时存储以Key为索引的值。一个Cache仅被一个CacheManager所拥有。
Entry是一个存储在Cache中的key-value对。
Expiry 每一个存储在Cache中的条目有一个定义的有效期。一旦超过这个时间,条目为过期的状态。一旦过期,条目将不可访问、更新和删除。缓存有效期可以通过ExpiryPolicy设置。
Spring从3.1开始定义了org.springframework.cache.Cache和org.springframework.cache.CacheManager接口来统一不同的缓存技术;并支持使用JCache(JSR-107)注解简化我们开发;
Cache接口为缓存的组件规范定义,包含缓存的各种操作集合
Cache接口下Spring提供了各种xxxCache的实现;如RedisCache,EhCacheCache , ConcurrentMapCache等
每次调用需要缓存功能的方法时,Spring会检查检查指定参数的指定的目标方法是否已经被调用过;如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓存结果后返回给用户。下次调用直接从缓存中获取
使用Spring缓存抽象时我们需要关注以下两点;
1、确定方法需要被缓存以及他们的缓存策略
2、从缓存中读取之前缓存存储的数据
2)几个常用的缓存注解(划重点)
@Cacheable |
主要针对方法配置,能够根据方法的请求参数对其结果进行缓存 |
@CacheEvict |
清空缓存 |
@CachePut |
保证方法被调用,又希望结果被缓存。 |
@EnableCaching |
开启基于注解的缓存 |
其他:keyGenerator(缓存数据时key生成策略),serialize(缓存数据时value序列化策略)
@Cacheable/@CachePut/@CacheEvict 主要的参数(公有属性) |
||
value |
缓存的名称,在 spring 配置文件中定义,必须指定至少一个 |
例如: |
key
|
缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 |
例如: |
condition |
缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存/清除缓存,在调用方法之前之后都能判断 |
例如: |
allEntries (@CacheEvict ) |
是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存 |
例如: |
beforeInvocation (@CacheEvict) |
是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存 |
例如: @CachEvict(value=”testcache”,beforeInvocation=true) |
unless (@CachePut) (@Cacheable) |
用于否决缓存的,不像condition,该表达式只在方法执行之后判断,此时可以拿到返回值result进行判断。条件为true不会缓存,fasle才缓存 |
例如: |
缓存中使用SPEL表达式
@Cacheable缓存目标对象
开启:
1、开启基于注解的缓存 @EnableCaching(在启动类上开启)
2、标注缓存注解即可@Cacheable、@CacheEvict、@CachePut
使用:
@Cacheable(value = "sysDataPage")
public PageInfo findPage(Integer start, Integer size) {
解析:将方法的运行结果进行缓存;以后再要相同的数据,直接从缓存中获取,不用调用方法;
原理:
1.自动配置类;CacheAutoConfiguration
2.缓存的配置类:默认SimpleCacheConfiguration
3.给容器中注册了一个CacheManager:ConcurrentMapCacheManager
4.以获取和创建ConcurrentMapCache类型的缓存组件;他的作用将数据保存在ConcurrentMap中;
运行流程:
@Cacheable:
1、方法运行之前,先去查询Cache(缓存组件),按照cacheNames指定的名字获取;
(CacheManager先获取相应的缓存),第一次获取缓存如果没有Cache组件会自动创建。
2、去Cache中查找缓存的内容,使用一个key,默认就是方法的参数;
key是按照某种策略生成的;默认是使用keyGenerator生成的,默认使用SimpleKeyGenerator生成key;
SimpleKeyGenerator生成key的默认策略;
如果没有参数;key=new SimpleKey();
如果有一个参数:key=参数的值
如果有多个参数:key=new SimpleKey(params);
3、没有查到缓存就调用目标方法;
4、将目标方法返回的结果,放进缓存中
@Cacheable标注的方法执行之前先来检查缓存中有没有这个数据,默认按照参数的值作为key去查询缓存,
如果没有就运行方法并将结果放入缓存;以后再来调用就可以直接使用缓存中的数据;
属性:
cacheNames/value:指定缓存组件的名字;将方法的返回结果放在哪个缓存中,是数组的方式,可以指定多个缓存;
key:缓存数据使用的key;可以用它来指定。默认是使用方法参数的值 1-方法的返回值
编写SpEL; #i d;参数id的值 #a0 #p0 #root.args[0]
getEmp[2]
keyGenerator:key的生成器;可以自己指定key的生成器的组件id
key/keyGenerator:二选一使用;
cacheManager:指定缓存管理器;或者cacheResolver指定获取解析器
condition:指定符合条件的情况下才缓存;
,condition = "#id>0"
condition = "#a0>1":第一个参数的值》1的时候才进行缓存
unless:否定缓存;当unless指定的条件为true,方法的返回值就不会被缓存;可以获取到结果进行判断
unless = "#result == null"
unless = "#a0==2":如果第一个参数的值是2,结果不缓存;
sync:是否使用异步模式
@CacheEvict清空指定缓存
@CacheEvict(value="sysDataPage",beforeInvocation = true)
@CacheEvict(value = "sysDataPage", allEntries = true)
属性:
key:指定要清除的数据
allEntries = true:指定清除这个缓存中所有的数据
beforeInvocation = false:缓存的清除是否在方法之前执行
默认代表缓存清除操作是在方法执行之后执行;如果出现异常缓存就不会清除
beforeInvocation = true:
代表清除缓存操作是在方法运行之前执行,无论方法是否出现异常,缓存都清除
@CachePut更新缓存
@CachePut(value = "sysDataPage", key = "#result.id")
作用:
既调用方法,又更新缓存数据;同步更新缓存
修改了数据库的某个数据,同时更新缓存;
运行时机:
1、先调用目标方法
2、将目标方法的结果缓存起来
#result.id是更新后返回的对象id,用于更新缓存中的数据(必须要@Cacheable缓存时的key是id)
@CacheConfig标注在类上面抽取缓存的公共配置
@CacheConfig(cacheNames="emp", cacheManager = "employeeCacheManager")
@Caching 定义复杂的缓存规则
@Caching(
cacheable = {
@Cacheable(/*value="emp",*/key = "#lastName")
},
put = {
@CachePut(/*value="emp",*/key = "#result.id"),
@CachePut(/*value="emp",*/key = "#result.email")
}
)
配置一个自定义的KeyGenerator(key生成器)
@Configuration
public class MyCacheConfig {
@Bean("myKeyGenerator")
public KeyGenerator keyGenerator(){
return new KeyGenerator(){
@Override
public Object generate(Object target, Method method, Object... params) {
return method.getName()+"["+ Arrays.asList(params).toString()+"]";
}
};
}
}
使用:
@Cacheable(value = {"emp"},keyGenerator = "myKeyGenerator",condition = "#a0>1",unless = "#a0==2")
yml配置
spring:
redis:
host: localhost
# 连接超时时间(记得添加单位,Duration)
timeout: 10000ms
# Redis默认情况下有16个分片,这里配置具体使用的分片
# database: 0
lettuce:
pool:
# 连接池最大连接数(使用负值表示没有限制) 默认 8
max-active: 8
# 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1
max-wait: -1ms
# 连接池中的最大空闲连接 默认 8
max-idle: 8
# 连接池中的最小空闲连接 默认 0
min-idle: 0
cache:
# 一般来说是不用配置的,Spring Cache 会根据依赖的包自行装配
type: redis
操作
1.引入spring-boot-starter-data-redis
2.application.yml配置redis连接地址
3.使用ReditTemplate操作redis
1>redisTemplate.opsForValue();//操作字符串
stringRedisTemplate.opsForValue();//字符串专用模板
2>redisTemplate.opsForHash();//操作hash
3>redisTemplate.opsForList();//操作list
4>redisTemplate.opsForSet();//操作set
5>redisTemplate.opsForZSet();//操作有序set
4.配置缓存、CacheManagerCustomizers
5.测试使用缓存、切换缓存、 CompositeCacheManager
redis配置序列化规则
@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
@EnableCaching
public class RedisConfig {
/**
* 默认情况下的模板只能支持RedisTemplate,也就是只能存入字符串,因此支持序列化
*/
@Bean
public RedisTemplate redisCacheTemplate(LettuceConnectionFactory redisConnectionFactory) {
RedisTemplate template = new RedisTemplate<>();
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
1.作用:异步处理、应用解耦、流量削峰
2.消息服务中两个重要概念:消息代理(message broker)和目的地(destination),当消息发送者发送消息以后,将由消息代理接管,消息代理保证消息传递到指定目的地。
3.消息队列主要有两种形式的目的地:
1>队列:点对点消息通信(point-to-point)
2>主题:发布(publish)/订阅(subscribe)消息通信
4.点对点式:
消息发送者发送消息,消息代理将其放入一个队列中,消息接收者从队列中获取消息内容,消息读取后被移出队列
消息只有唯一的发送者和接受者,但并不是说只能有一个接收者
5.发布订阅式:
发送者(发布者)发送消息到主题,多个接收者(订阅者)监听(订阅)这个主题,那么就会在消息到达时同时收到消息
6.JMS(Java Message Service)JAVA消息服务:java开发
基于JVM消息代理的规范。ActiveMQ、HornetMQ是JMS实现
7.AMQP(Advanced Message Queuing Protocol):非java开发
高级消息队列协议,也是一个消息代理的规范,兼容JMS
RabbitMQ是AMQP的实现
8.spring提供的支持
spring-jms提供了对JMS的支持
spring-rabbit提供了对AMQP的支持
需要ConnectionFactory的实现来连接消息代理
提供JmsTemplate、RabbitTemplate来发送消息
@JmsListener(JMS)、@RabbitListener(AMQP)注解在方法上监听消息代理发布的消息
@EnableJms、@EnableRabbit开启支持
9.spring的自动配置
JmsAutoConfiguration
RabbitAutoConfiguration
RabbitMQ是一个由erlang开发的AMQP(Advanved Message Queue Protocol)的开源实现。
Message
消息,消息是不具名的,它由消息头和消息体组成。消息体是不透明的,而消息头则由一系列的可选属性组成,这些属性包括routing-key(路由键)、priority(相对于其他消息的优先权)、delivery-mode(指出该消息可能需要持久性存储)等。
Publisher
消息的生产者,也是一个向交换器发布消息的客户端应用程序。
Exchange
交换器,用来接收生产者发送的消息并将这些消息路由给服务器中的队列。
Exchange有4种类型:direct(默认),fanout, topic, 和headers,不同类型的Exchange转发消息的策略有所区别
Queue
消息队列,用来保存消息直到发送给消费者。它是消息的容器,也是消息的终点。一个消息可投入一个或多个队列。消息一直在队列里面,等待消费者连接到这个队列将其取走
Binding
绑定,用于消息队列和交换器之间的关联。一个绑定就是基于路由键将交换器和消息队列连接起来的路由规则,所以可以将交换器理解成一个由绑定构成的路由表。
Exchange 和Queue的绑定可以是多对多的关系。
Connection
网络连接,比如一个TCP连接。
Channel
信道,多路复用连接中的一条独立的双向数据流通道。信道是建立在真实的TCP连接内的虚拟连接,AMQP 命令都是通过信道发出去的,不管是发布消息、订阅队列还是接收消息,这些动作都是通过信道完成。因为对于操作系统来说建立和销毁 TCP 都是非常昂贵的开销,所以引入了信道的概念,以复用一条 TCP 连接。
Consumer
消息的消费者,表示一个从消息队列中取得消息的客户端应用程序。
Virtual Host
虚拟主机,表示一批交换器、消息队列和相关对象。虚拟主机是共享相同的身份认证和加密环境的独立服务器域。每个 vhost 本质上就是一个 mini 版的 RabbitMQ 服务器,拥有自己的队列、交换器、绑定和权限机制。vhost 是 AMQP 概念的基础,必须在连接时指定,RabbitMQ 默认的 vhost 是 / 。
Broker
表示消息队列服务器实体
2>RabbitMQ的运行机制:
•AMQP 中消息的路由过程和 Java 开发者熟悉的 JMS 存在一些差别,AMQP 中增加了 Exchange 和 Binding 的角色。生产者把消息发布到 Exchange 上,消息最终到达队列并被消费者接收,而 Binding 决定交换器的消息应该发送到那个队列。
3>Exchange交换器类型:direct、fanout、topic、headers
direct:点对点
fanout:发布/订阅(fanout 类型转发消息是最快的。)
topic:消息的路由键匹配(#匹配0个或多个单词,*匹配一个单词。)
1.下载镜像
docker pull rabbitmq:3.7.7-management
2.运行容器
docker run -d -p 5671:5617 -p 5672:5672 -p 4369:4369 -p 15671:15671 -p 15672:15672 -p 25672:25672 --name rabbit-3.7.7 rabbitmq:3.7.7-management
3.进入容器
docker exec -it rabbit-3.7.7 /bin/bash
4.给容器安装 下载工具 wget
apt-get update
apt-get install wge
5.下载插件包,因为我们的 RabbitMQ 版本为 3.7.7 所以我们安装 3.7.x 版本的延迟队列插件
root@f72ac937f2be:/plugins# wget https://dl.bintray.com/rabbitmq/community-plugins/3.7.x/rabbitmq_delayed_message_exchange/rabbitmq_delayed_message_exchange-20171201-3.7.x.zip
6.给容器安装 解压工具 unzip
apt-get install -y unzip
7.解压插件包
root@f72ac937f2be:/plugins# unzip rabbitmq_delayed_message_exchange-20171201-3.7.x.zip
Archive: rabbitmq_delayed_message_exchange-20171201-3.7.x.zip
inflating: rabbitmq_delayed_message_exchange-20171201-3.7.x.ez
8.启动延迟队列插件
root@f72ac937f2be:/plugins# rabbitmq-plugins enable rabbitmq_delayed_message_exchange
9.推出容器
exit
10.停止容器
docker stop rabbit-3.7.7
11.启动容器
docker startrabbit-3.7.7
12.图形管理界面
http://192.168.25.131:15672 账号:guest 密码:guest