首页 » 网站建设 » phpredislistsort技巧_Redis篇之操作lettuce客户端Spring集成以及Spring Boot设备

phpredislistsort技巧_Redis篇之操作lettuce客户端Spring集成以及Spring Boot设备

访客 2024-11-09 0

扫一扫用手机浏览

文章目录 [+]

一、Redis简介

1.1 数据构造的操作

phpredislistsort技巧_Redis篇之操作lettuce客户端Spring集成以及Spring Boot设备

1.2 主要观点剖析

phpredislistsort技巧_Redis篇之操作lettuce客户端Spring集成以及Spring Boot设备
(图片来自网络侵删)

二、Redis客户端

2.1 简介

2.2 连接

2.3 基本用法

2.4 同步与异步

2.5 消费RedisFuture<T>

2.6 利用消费者监听器

2.7 发布订阅(Pub/Sub)

2.8 事务(Transaction)

2.9 主从复制(Master/Replica)

2.10 集群

三、Spring Data Redis操作

3.1 简介

3.2 配置Lettuce连接

3.3 主写从读模式配置

3.4 Redis哨兵模式(Sentinel)

3.5 发布订阅(Pub/Seb)

3.6 事务(Transaction)

3.7 流水线(Pipelining)

3.8 集群(Cluster)

3.9 序列化与反序列化

四、源码浅析

4.1 RedisTemplate

4.2 Operations类和Commands类

4.3 数据构造

4.4 SessionCallback接口

4.5 RedisCallbacke接口

4.6 总结

五、Spring Boot整合Redis

5.1 pom.xml文件

5.2 spring-boot-autoconfigure配置

5.3 RedisProperties类

5.4 LettuceConnectionConfiguration类

5.5 RedisAutoConfiguration类

六、总结

七、附录 干系网址

一、Redis简介

官方给出的定义是:

Redis 是一个开源(BSD容许)的,内存中的数据构造存储系统,它可以用作数据库、缓存和中间件。
它支持多种类型的数据构造,如 字符串(strings), 散列(hashes), 列表(lists), 凑集(sets), 有序凑集(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。
Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事宜(LRU eviction),事务(transactions)供应不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)供应高可用性(high availability)

那么接下来,逐步剖析官方给出的定义的主要观点。

1.1 数据构造的操作

下面大略先容常见命令:

(1)字符串(strings):

添加:set key value取值:get key(整型)递增: incr key(多值)添加:mset key1 val1 key2 val2 key3 val3(多值)取值:mget key1 key2 key3修正:set key newVal查询(是否存在):exists key删除:del key查询类型:type key(创建值后)设置超时(time韶光后将key对应值删除):expire key time(创建值时)设置超时:set key val ex time去除超时:persist key查看超时剩余韶光:ttl key

(2)散列(hashes):

添加多值:hmset yourhash field val [field val ...]添加单值:hset yourhash field val取多值:hmget yourhash field [field ...]取单值:hget yourhash field取全值: hgetall yourhash删除值:hdel yourhash field [field ...]

(3)列表(lists):

链表左边添加:lpush list val链表右边添加:rpush list val范围内取值:lrange list index_start index_end截取范围内值:ltrim list index_start index_end添加多值:rpush list val1 val2 val3 val4左边删除:lpop list右边删除: rpop list壅塞式访问左删除:blpop list 或者 blpop list1 list2 list3壅塞式访问右删除:brpop list 或者 brpop list1 list2 list3原子性地返回并移除存储在 list1 的列表的末了一个元素(列表尾部元素), 并把该元素放入存储在 list2的列表的第一个元素位置(列表头部) : rpoplpush list1 list2壅塞版RPOPLPUSH:brpoplpush list1 list2

(4)凑集(sets): String 的无序排列 , 适宜用于表示工具间的关系 。

添加: sadd myset val1 val2 val3查询元素:smembers myset查询特定元素: sismember myset val删除(随机):spop myset

(5)有序凑集(sorted sets):

添加(更新):zadd mysortedset score val [score val ...]范围内取值:zrange mysortedset score_begin score_end取索引值:zscore mysortedset val删除索引值最大的值: zpopmax mysortedset删除索引值最小的值: zpopmin mysortedset

(6)bitmaps:不是实际的数据构造,而是一个字符串类型定义的面向比特的凑集。

添加值:setbit key offset [offset ...]取值:getbit key offset

(7)hyperloglogs:是一种概率的数据构造,用于打算唯一的数据。

添加:pfadd key element [element ...]合并: pfmerge key key1 key2

想详细理解可通过点击下面链接:

redis官方命令大全

https://redis.io/commands

1.2 主要观点剖析

(1)复制(基于主从构造)

机制:

当主从连接正常时,master通过传输命令流来保持slave的数据同步;当主从连接由于网络缘故原由或连接超时涌现中断时,slave重新连接并试图部分重新同步(同步中断之后的数据);当部分同步无效后,slave将会申请完备同步。

特性:

Redis采取异步复制,而主从也是异步地确认须要处理的数据量;一个master可以有多个slave;Slave可以连接其他slave;在master处,复制是非壅塞式的;在slave处,大部分复制也是非壅塞式的;复制可以用在实现Redis系统伸缩性,让多个slave卖力供应只读查询,也可以用于提高数据安全和系统高可用性。
可以利用复制来避免master将全部数据写入磁盘的开销。

(2)事务

担保机制:

事务中所有命令都被序列化并会按序实行,并不可打断;Readis事物是原子性的,若实行则全部实行,若失落败则全部失落败。

利用:

MULTI命令开缘由务;EXEC命令实行事务。

(3)持久化

持久化办法:

RDB持久化办法能够在指定的韶光间隔进行数据集快照存储;AOF持久化办法会记录每一个sever的写操作,当server重启时,将重新实行记录的写操作构建原始数据集;如果只希望数据存在于server运行时,可以不进行持久化;可以结合RDB和AOF进行持久还。

(4)哨兵(Sentinel)

功能:

监控(monitoring):Sentinel不断检讨master和slave以确保他们按预期运行;提醒(notification):当被监控的Redis涌现问题时,将会通过api向用户或者另一个程序发送关照;自动故障转移(automatic failover):当master失落效时,Sentinel会开启故障转移处理,将一个slave提升为master,原寄托于故障master的slaves将重新配置寄托于新的master,并在利用Redis server的运用连接时,奉告其利用master的新地址;配置供应者(Configuretion provider):Sentinel充当客户端(clilent)做事创造的根据来源,客户端连接Sentinel,为一个指定的做事要求当前Redis master相应的地址(若发生故障转移,则会向所有连接Sentinel的客户端奉告master的新地址)。

(5)分区(Partitioning)

目的:

利用多个打算机供应的内存总和来构建更大的数据库(若没有分区,用户将只能利用单台打算机供应的内存量);拓展打算能力到多核和多打算机,将网络带宽拓展到多打算机和多适配器。

办法:

客户端分区:客户端直接在精确的节点读取或写入key(大部分客户端已实现);代理分区:客户端向代理端要求数据,由代理端访问精确的节点;查询路由:客户端向一个随机实例要求查询,该实例会将要求转移到精确节点上。

二、Redis客户端

现在用得比较多的我比较关注的Redis Java客户端是Jedis和Lettuce,而在Spring Data框架以及Spring Boot中也是利用这两种客户端。
因此,我将关注与Spring Data是如何整合Redis(实现Redis的操作和功能)以及Spring Boot中是如何实现自动配置的(包括供应的配置项)。
在Jedis和Lettuce中,我对Lettuce更感兴趣(可能由于我比较喜好吃生菜吧),以是,接下来的内容,将环绕者Lettuce客户端进行剖析,以及之后的框架整合紧张也因此lettuce为主。

2.1 简介

Lettuce是可拓展的线程安全的Redis客户端,供应同步、异步和相应式APIs。
如果避免利用壅塞和事务性操作(例如,BLPOP和MULTI/EXEC),多个线程可以共享一个连接。
Nttey的nio框架对多个连接供应了有效的管理。
支持Redis的高等特性,例如哨兵(Sential)、集群(Cluster)以及Redis数据构造。

2.2 连接

Lettuce中RedisURI是创建连接的关键,那么,接下来,看看RedisURI是怎么创建的。

(1)构建办法:

利用uri:RedisURI.create(\"大众redis://localhost/\"大众);利用Builder:RedisURI.Builder.redis(\公众localhost\公众,6379).auth(\"大众password\"大众).database(1).build();直策应用布局方法:new RedisURI(\公众localhost\公众, 6379, 60, TimeUnit.SECONDS);

(2)RedisURI句式:

Redis standalone模式:redis :// [: password@] host [: port] [/ database][? [timeout=timeout[d|h|m|s|ms|us|ns]] [&_database=database_]]Redis standalone模式 (SSL):rediss :// [: password@] host [: port] [/ database][? [timeout=timeout[d|h|m|s|ms|us|ns]] [&_database=database_]]Redis哨兵模式:redis-sentinel :// [: password@] host1[: port1] [, host2[: port2]] [, hostN[: portN]] [/ database][?[timeout=timeout[d|h|m|s|ms|us|ns]] [&_sentinelMasterId=sentinelMasterId_] [&_database=database_]

2.3 基本用法

RedisClient client = RedisClient.create(redisURI); (1)StatefulRedisConnection<String, String> connection = client.connect(); (2)RedisCommands<String, String> commands = connection.sync(); (3)String value = commands.get(\公众key\公众); (4)...connection.close(); (5)client.shutdown(); (6)

(1)根据给出的redisURI(创建方法在2.2节)创建Redis客户端(默认连接6379端口);

(2)打开Redis standalone模式(模式由给出的RedisURI决定的,即redis、rediss和redis-sentinel的差异)连接。

(3)得到同步实行的命令API;

(4)发出一个GET命令获取“foo”对应的值;

(5)关闭连接;

(6)关掉客户端。

2.4 同步与异步

(1)RedisFuture<T>和CompleteableFuture<T>简介

每一个异步的API命令的调用都会创建一个可以取消、等待和订阅的RedisFuture<T>。
而一个RedisFuture<T>或CompleteableFuture<T>是一个指向值打算尚未完成的最初未知结果的指针。
一个RedisFuture<T>供应异步和链接的操作。

异步通过RedisFuture<T>进行操作,同步直接通过同步实行命令进行操作。

(2)创建RedisFuture<T>(Lettuce中)

获取同步实行命令API

//根据redisURI创建客户端RedisClient client = RedisClient.create(redisURI); //创建连接 StatefulRedisConnection<String, String> connection = client.connect(); //获取同步实行命令RedisCommands<String, String> sync = connection.sync(); //发送get要求,获取值 String value = sync.get(\公众key\"大众);... //关闭连接connection.close();//关掉客户端client.shutdown(); 获取异步实行命令API

RedisClient client = RedisClient.create(redisURI);//创建连接StatefulRedisConnection<String, String> connection = client.connect(); //获取异步实行命令apiRedisAsyCommands<String, String> commands = connection.async(); //获取RedisFuture<T>RedisFuture<String> future = commands.get(\"大众key\"大众);

2.5 消费RedisFuture<T>:

没有设置超时(拉模式)

RedisFuture<String> future = commands.get(\"大众key\"大众);//利用拉模式调用get方法,壅塞调用线程,直到打算结果完成,//最坏的情形是线程一贯壅塞String value = future.get();System.out.println(value); 设置超时:

//获取异步实行api

RedisAsyncCommands<String, String> async = client.connect().async();

//发送set要求

RedisFuture<String> set = async.set(\"大众key\"大众, \"大众value\"大众);

//发送get要求

RedisFuture<String> get = async.get(\公众key\"大众);

//设置set超时

set.await(1, SECONDS) == true

set.get() == \"大众OK\"大众

//设置get超时

get.get(1, TimeUnit.MINUTES) == \公众value\"大众

2.6 利用消费者监听器:

非壅塞

//发送get要求RedisFuture<String> future = commands.get(\公众key\"大众);//设置监听器future.thenAccept(new Consumer<String>() {//该方法将会在future.complete()方法实行后,自动实行 @Override public void accept(String value) { ... }});壅塞同步

try { RedisFuture<String> future = commands.get(\公众key\"大众); //设置超时 String value = future.get(1, TimeUnit.MINUTES); System.out.println(value);} catch (Exception e) { //超时后,将抛出TimeoutException e.printStackTrace();} 推模式

RedisFuture<String> future = commands.get(\"大众key\"大众);//在future完成实行(即future.complete())时,将触发该方法//这样的实行流程便是推模式future.thenAccept(new Consumer<String>() { @Override public void accept(String value) { System.out.println(value); }});

2.7 发布订阅(Pub/Sub)

同步订阅

//创建发布订阅连接StatefulRedisPubSubConnection<String, String> connection = client.connectPubSub()//添加监听器connection.addListener(new RedisPubSubListener<String, String>() { ... })//获取同步发布订阅实行命令APIRedisPubSubCommands<String, String> sync = connection.sync();//订阅channel通道信息sync.subscribe(\"大众channel\"大众);//向channel通道发送message信息,//暂时没找到该命令,等后期补充...//下面是自定义的业务代码...异步订阅

StatefulRedisPubSubConnection<String, String> connection = client.connectPubSub()connection.addListener(new RedisPubSubListener<String, String>() { ... })//获取异步发布订阅实行命令APIRedisPubSubAsyncCommands<String, String> async = connection.async();//获取向通道channel订阅的futureRedisFuture<Void> futureSub = async.subscribe(\"大众channel\公众);//获取向通道channel发布message的futureRedisFuture<Void> futurePub = async.push(\"大众channel\"大众,\"大众message\"大众);//自定义业务代码业务代码...Redis Cluster发布订阅

//创建Redis Cluster发布订阅连接StatefulRedisClusterPubSubConnection<String, String> connection = clusterClient.connectPubSub()//向连接中添加监听器connection.addListener(new RedisPubSubListener<String, String>() { ... })//获取发布订阅同步实行代码RedisPubSubCommands<String, String> sync = connection.sync();//向连接中订阅channel通道信息sync.subscribe(\"大众channel\"大众);//自定义业务代码...

2.8 事务(Transaction)

Lettuce通过WATCH, UWATCH,EXEC, MULTI 和DISCARD来掌握事务(Transaction),同时许可同步、异步、相应式和集群利用事务。
那么,下面将剖析同步和异步事务。

同步利用事务

//第一段代码//创建同步连接RedisCommands<String, String> redis = client.connect().sync();//开缘由务redis.multi() //成功,则返回值为\"大众OK\公众redis.set(key, value) //未实行,返回为nullredis.exec() //实行事务,返回list(\公众OK\"大众)//第二段代码RedisCommands<String, String> redis = client.connect().sync();//开缘由务redis.multi() //成功,返回\"大众OK\"大众redis.set(key1, value) //未实行,返回nullredis.set(key2, value) //未实行, 返回nullredis.exec() //事务实行成功,返回list(\公众OK\"大众, \"大众OK\公众) 异步利用事务

//获取异步实行命令APIRedisAsyncCommands<String, String> async = client.connect().async();//获取发送开缘由务的futureRedisFuture<String> multi = async.multi();//实行future的set命令设置值RedisFuture<String> set = async.set(\"大众key\"大众, \公众value\"大众);//获取提交实行事务的futureRedisFuture<List<Object>> exec = async.exec();//获取实行事务的结果List<Object> objects = exec.get();//获取set的实行结果String setResult = set.get();//测试事务操作与set操作结果是否同等//即事务操作是否成功 objects.get(0) == setResult相应式利用事务

//创建相应式连接RedisReactiveCommands<String, String> reactive = client.connect().reactive();//开缘由务reactive.multi().subscribe(multiResponse -> { //编写事务操作 reactive.set(\"大众key\"大众, \"大众1\"大众).subscribe(); reactive.incr(\公众key\"大众).subscribe(); //提交实行事务 reactive.exec().subscribe();});

2.9 主从复制(Master/Replica)

(1)Redis standalone模式

//创建客户端RedisClient redisClient = RedisClient.create();//创建主从连接StatefulRedisMasterSlaveConnection<String, String> connection = MasterSlave.connect(redisClient, new Utf8StringCodec(), RedisURI.create(\"大众redis://localhost\"大众));//从ReadFrom.MASTER_PREFERRED中读取并复制,//ReadFrom.MASTER_PREFERRED是一个ReadFromMasterPreferred类实例的引用connection.setReadFrom(ReadFrom.MASTER_PREFERRED);System.out.println(\"大众Connected to Redis\公众);//关闭连接connection.close();//关掉客户端redisClient.shutdown();

(2)Redis哨兵模式

RedisClient redisClient = RedisClient.create();//创建哨兵模式主从复制连接StatefulRedisMasterSlaveConnection<String, String> connection = MasterSlave.connect(redisClient, new Utf8StringCodec(), RedisURI.create(\"大众redis-sentinel://localhost:26379,localhost:26380/0#mymaster\"大众));//从ReadFrom.MASTER_PREFERRED中读取并进行复制connection.setReadFrom(ReadFrom.MASTER_PREFERRED);System.out.println(\"大众Connected to Redis\"大众);connection.close();redisClient.shutdown();

2.10 集群(Cluster)

(1)lettuce对集群的支持

支持所有的CLUSTER命令;基于命令见hash slot的命令路由;所选集群命令的高等抽象;多集群节点的命令操作;通过slot和host/port获取集群节点的直接连接SSL和身份验证;周期性灯芯集群拓扑图;发布/订阅。

(2)利用NodeSelection API

//创建Redis集群的高等异步连接RedisAdvancedClusterAsyncCommands<String, String> async = clusterClient.connect().async();//利用NodeSelection API连接所有副本AsyncNodeSelection<String, String> replicas = connection.slaves();//从所有副本中获取所有的keys(密钥)AsyncExecutions<List<String>> executions = replicas.commands().keys(\公众\公众);//遍历得到的keysexecutions.forEach(result -> result.thenAccept(keys -> System.out.println(keys)));

(3)连接到一个集群

RedisURI redisUri = RedisURI.Builder.redis(\"大众localhost\"大众).withPassword(\"大众authentication\"大众).build();//创建集群客户端RedisClusterClient clusterClient = RedisClusterClient.create(rediUri);//创建连接StatefulRedisClusterConnection<String, String> connection = clusterClient.connect();//获取同步实行命令apiRedisAdvancedClusterCommands<String, String> syncCommands = connection.sync();...connection.close();clusterClient.shutdown();

(4)连接到多个子节点的Redis集群

RedisURI node1 = RedisURI.create(\公众node1\公众, 6379);RedisURI node2 = RedisURI.create(\"大众node2\公众, 6379);//创建拥有多个子节点的集群客户端RedisClusterClient clusterClient = RedisClusterClient.create(Arrays.asList(node1, node2));//创建连接StatefulRedisClusterConnection<String, String> connection = clusterClient.connect();//获取同步实行命令apiRedisAdvancedClusterCommands<String, String> syncCommands = connection.sync();...connection.close();clusterClient.shutdown();

(5)开启周期性拓扑图更新

RedisClusterClient clusterClient = RedisClusterClient.create(RedisURI.create(\"大众localhost\公众, 6379));//创建周期性拓扑图更新操作的配置操作ClusterTopologyRefreshOptions topologyRefreshOptions = ClusterTopologyRefreshOptions.builder() .enablePeriodicRefresh(10, TimeUnit.MINUTES) .build();//向客户端设置刚才配置好的操作clusterClient.setOptions(ClusterClientOptions.builder() .topologyRefreshOptions(topologyRefreshOptions) .build());...clusterClient.shutdown();

(6)开启自适应拓扑图更新

RedisURI node1 = RedisURI.create(\"大众node1\"大众, 6379);RedisURI node2 = RedisURI.create(\公众node2\"大众, 6379);RedisClusterClient clusterClient = RedisClusterClient.create(Arrays.asList(node1, node2));//配置自适应拓扑图更新操作ClusterTopologyRefreshOptions topologyRefreshOptions = ClusterTopologyRefreshOptions.builder() .enableAdaptiveRefreshTrigger(RefreshTrigger.MOVED_REDIRECT, RefreshTrigger.PERSISTENT_RECONNECTS) .adaptiveRefreshTriggersTimeout(30, TimeUnit.SECONDS) .build();//向集群客户端中设置刚才配置好的操作clusterClient.setOptions(ClusterClientOptions.builder() .topologyRefreshOptions(topologyRefreshOptions) .build());...clusterClient.shutdown();

(7)获取一个节点

RedisURI node1 = RedisURI.create(\"大众node1\公众, 6379);RedisURI node2 = RedisURI.create(\"大众node2\"大众, 6379);//创建集群客户端RedisClusterClient clusterClient = RedisClusterClient.create(Arrays.asList(node1, node2));//创建连接StatefulRedisClusterConnection<String, String> connection = clusterClient.connect();//获取指定节点的同步实行命令apiRedisClusterCommands<String, String> node1 = connection.getConnection(\"大众host\"大众, 7379).sync();...//不须要关闭节点连接connection.close();clusterClient.shutdown();

三、Spring Data Redis操作

3.1 简介

关注于当前最新的集成版本是Spring Data Redis,该框架通过对Jedis和Lettuce的封装集成,将所有的Redis操作和数据类型都封装成为了一个个单独的操作类(Operation)和凑集类(例如,RedisList,RedisMap等),同时供应各配置类(Configuration)来让用户自定义期望得到的客户端,对用户供应高等抽象的客户端RedisTemplate,用户不必要理解底层如何实现,只需通过RedisTemplate对Redis进行操作即可,而在最新一版Spring Data Redis 2.1中,通过Lettuce支持了主写从读的设置,而这正是这一版我比较感兴趣的一点。

接下来的几节里,先从操作层面考试测验Spring Data Redis是如何利用而实现Redis的功能特性,然后再从源码层面去看框架是如何封装Redis的特性和操作的,末了再谈论Spring Data Redis整合Redis的优点和不敷。

3.2 配置Lettuce连接

在Spring Data中通过供应RedisStandaloneConfiguration和RedisSentinelConfiguration两个配置类,来供应Redis Standalone模式配置和Redis Sentinel模式配置。

Redis standalone模式配置

@Configurationclass LettuceConnectConfig { @Bean public LettuceConnectionFactory redisConnectionFactory() { //通过配置RedisStandaloneConfiguration实例来 //创建Redis Standolone模式的客户端连接创建工厂 //配置hostname和port return new LettuceConnectionFactory(new RedisStandaloneConfiguration(\公众server\公众, 6379)); }}

3.3 主写从读模式配置

@Configuration

class WriteToMasterReadFromReplicaConfiguration {

//配置Lettuce连接工厂

@Bean

public LettuceConnectionFactory redisConnectionFactory() {

LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()

.readFrom(SLAVE_PREFERRED) //配置从读

.build();

//配置hostname和port

RedisStandaloneConfiguration serverConfig = new RedisStandaloneConfiguration(\"大众server\"大众, 6379);

//通过配置创建Lettuce连接工厂

return new LettuceConnectionFactory(serverConfig, clientConfig);

}

}

3.4 Redis 哨兵模式

@Configurationpublic class LettuceSentinelConfig{ @Bean public RedisConnectionFactory lettuceConnectionFactory() { //创建哨兵配置 RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration() .master(\"大众mymaster\"大众) //设置主机的NameNode .sentinel(\"大众127.0.0.1\公众, 26379) //配置哨兵的ip和端口 .sentinel(\"大众127.0.0.1\"大众, 26380); return new LettuceConnectionFactory(sentinelConfig); }}

也可以在application.yml(项目配置文件)中通过配置spring.redis.sentinel.mater配置主节点的名字,和配置spring.redis.sentinel.nodes配置逗号分隔host:port的列表来配置节点。

3.5 发布订阅(Pub/Sub)

(1)发布(Publish)

在Spring Data Redis中,供应了两种方法进行发布和订阅,即低级的RedisConnection和高等的RedisTemplate,实行效果是一样的。
那么,下面我们看看,他们的操作实例吧。

// 利用ReidsConnection发布信息RedisConnection con = ...//序列化发布的信息byte[] msg = ...//序列化发往的channel通道byte[] channel = ...//调用RedisConnection进行发布//请把稳,官方文档的该参数顺序涌现颠倒con.publish(channel, msg); // 利用RedisTemplate进行序列化和发布RedisTemplate template = ...//该方法对序列化和发布进行了封装//hello为channel通道名,//world是要发送的messagetemplate.convertAndSend(\公众hello!\"大众, \"大众world\"大众);

(2)订阅(Subscribe)

到目前为止,框架只供应低级的RedisConnection的subscribe(指定通道发布)和psubscribe(指定模式发布,即正则表达式)两种发布方法。
而且在Spring Data Redis框架下,订阅操作是壅塞的,一旦开启订阅,一个connection将会等待,此时调用除了subscribe、psubscribe、unsubscribe和punsubscribe之外的命令,都会抛出非常。
接下来,我们来看看,利用上有什么不同。

subscribe方法

//创建连接RedisConnection con = ...//创建序列化工具RedisSerializer<String> stringSerializer = RedisSerializer.string();//序列化通道Byte[] channel = stringSerializer.serialize(\"大众hello\"大众);//创建信息监听器,实现接口下onMessage方法//在吸收到redis发送过来的后实行//利用RedisConnection进行订阅必须实现该方法//吸收的参数从源码上来看第二个参数都是Byte[]类型MessageListener listener = (massage,channel)->System.out.println(\"大众Received message from \"大众+channel);//接下来便可以订阅con.subscribe(listener, message);psubscribe方法

//创建连接RedisConnection con = ...//创建序列化工具RedisSerializer<String> stringSerializer = RedisSerializer.string();//序列化正则表达式Byte[] pattern = stringSerializer.serialize(\"大众hello\"大众);//创建信息监听器,实现接口下onMessage方法//在吸收到redis发送过来的后实行//利用RedisConnection进行订阅必须实现该方法//吸收的参数从源码上来看第二个参数都是Byte[]类型MessageListener listener = (massage,pattern)->System.out.println(\"大众Received message from \公众+pattern);//接下来便可以利用正则表达式订阅con.psubscribe(listener,pattern);

3.6 事务(Transaction)

(1)利用

RedisTemplate通过execute方法供应事务功能,但不担保所有的事务操作在同一个连接中进行。

//实行一次事务操作

//调用RedisTemplte的execute(SessionBack<T> session)方法

//须要实现SessionBack中的execute(RedisOperations<K,V> operations)方法

//利用lamda表达式

List<Object> txResults = redisTemplate.execute((RedisOperations<K, V> operations)->{

//开缘由务

operations.multi();

//添加操作

operations.opsForSet().add(\"大众key\"大众, \公众value1\"大众);

//这个将会返回事务操作的所有结果

return operations.exec();

}

});

//打印结果

System.out.println(\"大众Number of items added to set: \"大众 + txResults.get(0));

(2)配置

//开启声明式事务管理@Configuration@EnableTransactionManagement public class RedisTxContextConfiguration { //配置RedisTemplate @Bean public StringRedisTemplate redisTemplate() { StringRedisTemplate template = new StringRedisTemplate(redisConnectionFactory()); //显式启用事务支持 template.setEnableTransactionSupport(true); return template; } //配置Redis连接工厂 @Bean public RedisConnectionFactory redisConnectionFactory() { // jedis || Lettuce } //配置事务管理器,但Spring Data Redis不供应事务管理器 //须要我们自己创建实现配置事务管理器 //若利用JDBC连接,则直策应用已存在的事务管理器 @Bean public PlatformTransactionManager transactionManager() throws SQLException { return new DataSourceTransactionManager(dataSource()); } //配置数据源 @Bean public DataSource dataSource() throws SQLException { // ... }}

3.7 流水线(Pipelining)

Redis的pipelining功能,可以一次向server发送多个命令而不须要等待相应,之后通过一个步骤就可以得到所有相应。
当须要连续发送多个命令时(如向同一个List添加多个元素),pipelining流水线功能将会提高运用性能。

该框架通过供应RedisTemplate的executePipelined方法,支持pipelining功能。
下面来看看它的示例。

//从一个行列步队中删除指天命量的元素List<Object> results = stringRedisTemplate.executePipelined( //RedisCallback内部类 //实现doInRedis方法 //此处亦可以创建SessionCallack的内部类 //由用户自己决定 new RedisCallback<Object>() { public Object doInRedis(RedisConnection connection) throws DataAccessException { //创建连接 StringRedisConnection stringRedisConn = (StringRedisConnection)connection; for(int i=0; i< batchSize; i++) { //循环调用rpop进行batchSize次右删除 stringRedisConn.rPop(\"大众myqueue\"大众); } return null; }});

3.8 集群(Cluster)

Spring Data Redis 框架供应了Redis Cluster的配置类,即RedisClusterConfiguration类,供应两项spring.redis.cluster.nodes和spring.redis.cluster.max-redirects属性配置,配置集群节点和许可集群重定向的最大数量。
可以通过该RedisClusterConfiguration类进行创建集群创建工厂,然后创建集群连接。

当然,在官方文档上,也供应了让我们自己配置以及读取创建连接工厂的方法,下面来看看怎么自定义创建。

//创建自定义集群配置属性//从application的配置文件中//读取前缀为spring.redis.cluster相应的属性//注册成为Spring bean@Component@ConfigurationProperties(prefix = \"大众spring.redis.cluster\公众)public class ClusterConfigurationProperties { //集群节点列表 List<String> nodes; public List<String> getNodes() { return nodes; } public void setNodes(List<String> nodes) { this.nodes = nodes; }}//配置创建集群连接工厂@Configurationpublic class LettuceClusterAppConfig { //自动装置配置Bean @Autowired ClusterConfigurationProperties clusterProperties; @Bean public RedisConnectionFactory connectionFactory() { //根据上面配置bean创建连接工厂 return new LettuceConnectionFactory( new RedisClusterConfiguration(clusterProperties.getNodes())); }}

3.9 序列化与反序列化

Spring Data Redis供应了RedisSerializer接口,同时供应了多个实现该接口的序列化工具,如JdkSerializationRedisSerializer、Jackson2JsonRedisSerializer和GenericToStringSerializer等。
通过调用实在例的serialize和deserialize方法进行序列化和反序列化。

四、Spring Data Redis源码

从上一章中,我们创造由多个常涌现的由Spring Data Redis 封装了的类和操作。
那么,这一章紧张来看其是如何封装客户端操作的。

4.1 RedisTemplate

该类是Spring Data Redis供应给用户的最高级的抽象客户端,用户可直接通过RedisTemplate进行多种操作,那么,我们先来看看RedisTemplate封装了哪些操作。
下面这列表是RedisTemplate的继续关系和所有方法(已过滤重载方法,共有81个方法)

//类继续关系//RedisAccessor是RedisTemplate定义普通属性的基类,不直策应用//RedisOperations是指定RedisTemplate实现的Redis connection操作的凑集接口//BeanClassLoaderAware是给实在现类是设置类加载器的接口1.RedisTemplate<K, V> extends RedisAccessor implements RedisOperations<K, V>, BeanClassLoaderAware//方法//配置默认序列化与反序列化工具类2.afterPropertiesSet//根据参数实行干系operation操作,例如,事务3.execute//实行pipelining流水线干系操作4.executePipelined//实行指定connection连接的干系操作5.executeWithStickyConnection//实行session内的execute方法6.executeSession//创建RedisConnection代理类7.createRedisConnectionProxy//connection连接的预处理8.preProcessConnection//结果的后处理,默认什么都不做9.postProcessResult//是否向RedisCallback暴露本地连接10.isExposeConnection//设置是否向RedisCallback暴露本地连接11.setExposeConnection//12到26都是设置和获取干系序列化工具类12.isEnableDefaultSerializer13.setEnableDefaultSerializer14.getDefaultSerializer15.setDefaultSerializer16.setKeySerializer17.getKeySerializer18.setValueSerializer19.getValueSerializer20.getHashKeySerializer21.setHashKeySerializer22.getHashValueSerializer23.setHashValueSerializer24.getStringSerializer25.setStringSerializer26.setScriptExecutor//27到34为私有方法,不对外供应利用27.rawKey28.rawString29.rawValue30.rawKeys31.deserializeKey32.deserializeMixedResults33.deserializeSet34.convertTupleValues//实行事务35.exec36.execRaw//删除操作37.delete//打仗链接38.unlink//查看是否含有指定key39.hasKey40.countExistingKeys//设置过期韶光41.expire42.expireAt//转换成字节流并向channel发送message43.convertAndSend//获取过期韶光44.getExpire//根据传入的正则表达式返回所有的key46.keys//取消指定key的过期韶光47.persist//移动指定的key和index到数据库中48.move//从键空间随机获取一个key49.randomKey//将指定key改成目标key50.rename//key不存在时,将指定key改成目标key51.renameIfAbsent//设置存储在指定key的类型52.type//检索存储在key的值的序列化版本53.dump//实行Redis的restore的命令54.restore//标记事务壅塞的开始55.multi//丢弃所有在multi之后发出的命令56.discard//不雅观察指定key在事务处理开始即multi之后的修正情形57.watch//刷新先前不雅观察的所有key58.unwatch//为key元素排序59.sort//关闭客户端连接60.killClient//要求连接客户真个干系信息和统计数据61.getClientList//变动复制配置到新的master62.slaveOf//将本机变动为master63.slaveOfNoOne//64到79都是获取相对应的操作64.opsForCluster65.opsForGeo66.boundGeoOps67.boundHashOps68.opsForHash69.opsForHyperLogLog70.opsForList71.boundListOps72.boundSetOps73.opsForSet74.opsForStream75.boundStreamOps76.boundValueOps77.opsForValue78.boundZSetOps79.opsForZSet//设置是否支持事务80.setEnableTransactionSupport//设置bean的类加载器81.setBeanClassLoader

4.2 Operations类和Commands类

在Spring Data Redis中,将对应的操作都封装成为了对应的Operations接口和实现类(在org.springframework.data.redis.core包中),有ListOperations接口、HashOperations接口、GeoOperatons接口、ClusterOperations接口、HyperLoglogOperations接口等等接口以及对应的DefaultListOperations类、DefaultHashOperations类等默认实现类。
Command类是对redis命令的第一次封装,即由Redis的第三方Java客户端供应的,例如lettuce,这些Command类的命令就在在org.springframework.data.redis.connection.lettuce包内。

下面剖析一下DefaultListOperations类:

leftPop方法源码

//左删除链表public V leftPop(K key) { return execute(new ValueDeserializingRedisCallback(key) { @Override protected byte[] inRedis(byte[] rawKey, RedisConnection connection) { //调用connection的lpop(rawKey)方法, //即调用底层创建出的Lettuce连接或者Jedis连接的lpop方法 //获取同步或异步实行命令API,然后实行lpop方法 return connection.lPop(rawKey); } }, true);}leftPush方法源码

//左添加链表//key为链表,value为值public Long leftPush(K key, V value) { //序列化链表名 byte[] rawKey = rawKey(key); //序列化链表值 byte[] rawValue = rawValue(value); //调用底层实现的Lettuce连接或者Jedis连接 //获取同步或异步实行命令API,然后实行lPush命令 return execute(connection -> connection.lPush(rawKey, rawValue), true);}

与此相同, 其他的操作末了都会调用底层创建的Lettuce或Jedis连接,即LettuceConnection或JedisConnection,然后通过相应的方法获取相应的命令封装类即Commands类(例如,LettuceListCommands类),末了调用Command类的方法(例如,LettuceCommands类的lPush或lPop方法)进行操作。
在此之外,还有LettuceClusterConnection类(集群连接)和LettuceSentinelConnection类(哨兵连接),同样的Jedis也有相同实现,不再赘言。

4.3 数据构造

在org.springframework.data.redis.support下的collections包里实现了列表,散列表,有序凑集,凑集,以及有序凑集,即DefaultRedisList、DefaultRedisMap、DefaultRedisSet以及DefaultRedisZSet等,都绑定了相应的绑定类(例如,列表的绑定类为BoundLustOperations)。

在org.springframework.data.redis.support下的actomic包里,由Redis事务的watch和multi方法实现CAS的操作更新键值。
同时,供应了Double、Integer和Long的原子操作类型。

4.5 SessionCallback接口

该接口是Redistemplate的execute方法中的参数类型,通过实现该接口唯一方法,RedisTemplate的execute(sessionCallback)方法将会实行在实现方法中的所有操作。
可以通过该办法实现事务,即通过实行multi,discard,exec,watch和unwatch命令实现。

4.6 RedisCallback接口

该接口和SessionCallback接口一样,也是Redistemplate的execute方法中的参数类型,但是是低级的redis回调方法。
利用方法和上面接口一样,该接口有一个exec方法,常日将实在现为匿名类给execute方法利用,而最常用是链接多个get/set/trim操作等等。

4.7 总结

该框架实现了Spring和Redis的整合,供应了高等抽象的客户端以及相应的配置,降落了用户的入门本钱,以上是这框架的优点。
而在阅读该项目源码时,也创造在项目源代码构造上的一些不敷,也或许是自己没理解代码者的行为吧。
例如,org.springframework.data.redis.core包下的各种Operations操作类完备可以将其放入一个operations包内;org.springframework.data.redis.config包内居然存放的不是配置类,而是解析类。
就目前而言我创造,mybatis和spring框架的源码文件和注释是最清晰的最整洁的,作为Spring下的项目都该当秉持着这样的态度和风格。

五、Spring Boot 整合redis

Spring Boot供应starter的来向用户供应完成自动配置的redis,那么,关于Spring Boot对redis的整合紧张关心它所供应配置选项。
下面从源码来看看是如何整合的。

5.1 pom.xml文件

在spring-boot-starter-data-redis项目中,紧张通过pom.xml进行项目依赖。
下面是项眼前的pom.xml依赖源码。

<dependencies> //添加spring-boot-starter依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> //添加spring-data-redis依赖 <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> <exclusions> //打消jcl-over-slf4j依赖 <exclusion> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> </exclusion> </exclusions> </dependency> <dependency> //添加Lettuce客户端依赖 <groupId>io.lettuce</groupId> <artifactId>lettuce-core</artifactId> </dependency></dependencies>

而在spring-boot-starter依赖中又引入了许多干系的依赖,尤其是自动配置的依赖。

<dependencies> //添加spring-boot依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot</artifactId> </dependency> //添加spring-boot-autoconfigure项目依赖 //这个依赖很主要,和整合各式运用有关的配置都在该依赖下 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> </dependency> //添加日志依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </dependency> //添加表明依赖 <dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> </dependency> //添加spring核心依赖,例如ioc、aop等等 <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> </dependency> //添加yaml依赖 <dependency> <groupId>org.yaml</groupId> <artifactId>snakeyaml</artifactId> <scope>runtime</scope> </dependency></dependencies>

下一节,重点剖析在spring-boot-autoconfigure项眼前的源码配置。

5.2 spring-boot-autoconfigure项目

在该项眼前的org.springframework.boot.autoconfigure.data.redis包内,供应了许多关于redis的配置,下面是该包下的截图。

由包中的源码可以得知,该包下供应了Jedis和Lettuce的客户端连接配置、Redis基本连接配置、相应式配置、Redis存储仓库配置以及Redis所有的配置属性。

5.3 RedisProperties类

该类供应给用户利用,用户可以通过application文件配置Redis干系属性,下面是yml格式的配置列表。

spring: redis: database: url: host: password: port: ssl: timeout: pool: maxIdle: minIdle: maxActive: maxWait: sentinel: master: nodes: cluster: nodes: maxRedirects: jedis: pool: lettuce: shutdownTimeout pool

5.4 LettuceConnectionConfiguration类

该配置类通过表明@ConditionalOnClass(RedisClient.class)进行bean化。
类似表明阐明如下(该阐明摘自https://blog.csdn.net/blueheart20/article/details/81020262):

@ConditionalOnBean(仅仅在当前高下文中存在某个工具时,才会实例化一个Bean)@ConditionalOnClass(某个class位于类路径上,才会实例化一个Bean),该表明的参数对应的类必须存在,否则不解析该表明润色的配置类;@ConditionalOnExpression(当表达式为true的时候,才会实例化一个Bean)@ConditionalOnMissingBean(仅仅在当前高下文中不存在某个工具时,才会实例化一个Bean), 该表明表示,如果存在它润色的类的bean,则不须要再创建这个bean;可以给该表明传入参数例如@ConditionOnMissingBean(name = “example”),这个表示如果name为“example”的bean存在,这该表明润色的代码块不实行。
@ConditionalOnMissingClass(某个class类路径上不存在的时候,才会实例化一个Bean)@ConditionalOnNotWebApplication(不是web运用)@ConditionalOnProperty是指在application.yml里配置的属性是否为true,其他的几个都是对class的判断

以是,在类路径上存在RedisClient才会bean化该类进行配置。

5.5 RedisAutoConfiguration类

该类利用了四个表明:

@ConditionalOnClass(RedisOperations.class) :在类路径上存在RedisOperations.class时,才会bean实例化该类。
@EnableConfigurationProperties(RedisProperties.class) : 当@EnableConfigurationProperties表明运用到你的@Configuration时, 任何被@ConfigurationProperties表明的beans将自动被Environment属性配置,即RedisProperties.class类将会被Environment属性配置。
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class }):将LettuceConnectionConnfiguration和JedisConnectionConfiguration导入成为bean。
@Configuration:配置类。

该类里连个注入方法都利用了@ConditionalOnMissingBean,故只有高下文不存在RedisTemplate或StringRedisTemplate时,才会创建对应的bean。
其他的配置大同小异,有兴趣可以自己查阅源码。

六、总结

至此,Redis基本操作,Redis的Lettuce客户端,Spring Data Redis项目,以及Spring Boot 供应的配置方案的理解剖析,就已经结束了。
我个人认为Lettuce已经供应了比较完善的Redis的操作,并且更贴近于Redis的实行流程和思想,并且有更宽松的定制方案,不出意外的话,我会选择利用Lettuce客户端。
但是,Spring Boot项目供应的Redis配置方案是比较完善的,如果想要自己整合Spring boot和Lettuce客户端,可以学习它的配置思想。

而Spring Data Redis项目就效果而言,该当是很不错的,但就其项目架构而言,算不上幽美,乃至能用稀碎来形容。
不过,它的确降落了用户的学习门槛。
而在这一点上,我更乐意去学习贴近事理的代码。
知道了底层的实现事理,也能在Spring Data Redis的根本上进行自定义变动和优化。
就此翻过Redis篇的学习,文章若有不当之处,欢迎指教。

附录 干系网址

redis中文官方地址:http://www.redis.cn

reids英文官方地址(由于中文官方有些地方翻译的不是很明白,可以查询英文官方):https://redis.io/

lettuce官方地址:https://lettuce.io

Spring Data Redis官方地址:https://spring.io/projects/spring-data-redis

Spring Boot redis 自动配置源码地址:https://github.com/spring-projects/spring-boot/tree/master/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis

标签:

相关文章

广州番禺SEO现状与未来发展前景分析

搜索引擎优化(SEO)已经成为企业网络营销的重要组成部分。广州番禺作为我国经济发展的重要区域,其SEO市场也呈现出蓬勃发展的态势。...

网站建设 2025-04-08 阅读0 评论0

SEO优化视角下的北京租房推荐网站

我国房地产市场的竞争愈发激烈。在众多房地产领域,租房市场成为了备受关注的热点。为了满足广大租房者的需求,众多租房推荐网站应运而生。...

网站建设 2025-04-08 阅读0 评论0