Springboot2.x整合Redis以及连接哨兵模式/集群模式

野性酷女 2022-02-22 17:44 354阅读 0赞

依赖:

  1. <!--spirngboot版本为2.x-->
  2. <!-- 加载spring boot redis包,springboot2.0中直接使用jedis或者lettuce配置连接池,默认为lettuce连接池,这里使用jedis连接池 -->
  3. <!-- 加载spring boot redis包 -->
  4. <dependency>
  5. <groupId>org.springframework.boot</groupId>
  6. <artifactId>spring-boot-starter-data-redis</artifactId>
  7. <!-- 排除lettuce包,使用jedis代替-->
  8. <exclusions>
  9. <exclusion>
  10. <groupId>io.lettuce</groupId>
  11. <artifactId>lettuce-core</artifactId>
  12. </exclusion>
  13. </exclusions>
  14. </dependency>
  15. <dependency>
  16. <groupId>redis.clients</groupId>
  17. <artifactId>jedis</artifactId>
  18. <version>2.9.0</version>
  19. </dependency>

application.properties配置

  1. ##配置redis的连接信息
  2. #spring.redis.host=192.168.184.135
  3. #spring.redis.port=6379
  4. #spring.redis.password=123456
  5. #连接超时时间
  6. #spring.redis.timeout=6000ms
  7. ##Redis数据库索引(默认为0)
  8. #spring.redis.database=0
  9. ## 连接池配置,springboot2.0中直接使用jedis或者lettuce配置连接池,默认为lettuce连接池
  10. ##连接池最大连接数(使用负值表示没有限制)
  11. #spring.redis.jedis.pool.max-active=8
  12. ##连接池最大阻塞等待时间(使用负值表示没有限制)
  13. #spring.redis.jedis.pool.max-wait=-1s
  14. ##连接池中的最大空闲连接
  15. #spring.redis.jedis.pool.max-idle=8
  16. ##接池中的最小空闲连接
  17. #spring.redis.jedis.pool.min-idle=0
  18. ##################################
  19. #哨兵模式redis集群配置,就是为了通过redis找主节点,做到无感切换
  20. #spring.redis.password=123456
  21. #spring.redis.sentinel.master=mymaster
  22. #spring.redis.sentinel.nodes=192.168.184.133:26379,192.168.184.135:26379,192.168.184.136:26379
  23. ##连接超时时间
  24. #spring.redis.timeout=6000ms
  25. ##Redis数据库索引(默认为0)
  26. #spring.redis.database=0
  27. ## 连接池配置,springboot2.0中直接使用jedis或者lettuce配置连接池,默认为lettuce连接池
  28. ##连接池最大连接数(使用负值表示没有限制)
  29. #spring.redis.jedis.pool.max-active=8
  30. ##连接池最大阻塞等待时间(使用负值表示没有限制)
  31. #spring.redis.jedis.pool.max-wait=-1s
  32. ##连接池中的最大空闲连接
  33. #spring.redis.jedis.pool.max-idle=8
  34. ##接池中的最小空闲连接
  35. #spring.redis.jedis.pool.min-idle=0
  36. #############################
  37. #连接超时时间
  38. spring.redis.cluster.nodes=192.168.184.133:7000,192.168.184.133:7001,192.168.184.133:7002,192.168.184.133:7003,192.168.184.133:7004,192.168.184.133:7005
  39. spring.redis.password=123456
  40. spring.redis.timeout=6000ms
  41. #Redis数据库索引(默认为0)
  42. spring.redis.database=0
  43. # 连接池配置,springboot2.0中直接使用jedis或者lettuce配置连接池,默认为lettuce连接池
  44. #连接池最大连接数(使用负值表示没有限制)
  45. spring.redis.jedis.pool.max-active=8
  46. #连接池最大阻塞等待时间(使用负值表示没有限制)
  47. spring.redis.jedis.pool.max-wait=-1s
  48. #连接池中的最大空闲连接
  49. spring.redis.jedis.pool.max-idle=8
  50. #接池中的最小空闲连接
  51. spring.redis.jedis.pool.min-idle=0

 说明:Jedis和Lettuce的比较:

     Jedis :

      直连模式,在多个线程间共享一个 Jedis 实例时是线程不安全的,如果想要在多线程环境下使用 Jedis,需要使用连接池,

      每个线程都去拿自己的 Jedis 实例,当连接数量增多时,物理连接成本较高。

     Lettuce:

      连接是基于Netty的,连接实例可以在多个线程间共享,

      所以,一个多线程的应用可以使用同一个连接实例,而不用担心并发线程的数量。当然这个也是可伸缩的设计,一个连接实例不够的情况也可以按需增加连接实例。

      通过异步的方式可以让我们更好的利用系统资源,而不用浪费线程等待网络或磁盘I/O。

/哨兵相关内容//

一、客户端访问哨兵系统

哨兵系统的作用: 监控、自动故障转移、配置提供者、通知。

一、代码示例

在介绍客户端的原理之前,先以Java客户端Jedis为例,演示一下使用方法:下面代码可以连接我们刚刚搭建的哨兵系统,并进行各种读写操作:

  1. public static void testSentinel() throws Exception {
  2. String masterName = "mymaster";
  3. Set<String> sentinels = new HashSet<>();
  4. sentinels.add("192.168.92.128:26379");
  5. sentinels.add("192.168.92.128:26380");
  6. sentinels.add("192.168.92.128:26381");
  7. JedisSentinelPool pool = new JedisSentinelPool(masterName, sentinels);
  8. Jedis jedis = pool.getResource();
  9. jedis.set("key1", "value1");
  10. pool.close();
  11. }

(注:代码中只演示如何连接哨兵,异常处理、资源关闭等未考虑)

二、客户端原理

Jedis客户端对哨兵提供了很好的支持。如上述代码所示,我们只需要向Jedis提供哨兵节点集合和masterName,构造Jedis SentinelPool对象;然后便可以像使用普通Redis连接池一样来使用了:通过pool.getResource()获取连接,执行具体的命令。

在整个过程中,我们的代码不需要显式的指定主节点的地址,就可以连接到主节点;代码中对故障转移没有任何体现,就可以在哨兵完成故障转移后自动的切换主节点。之所以可以做到这一点,是因为在JedisSentinelPool的构造器中,进行了相关的工作,主要包括以下两点:

遍历哨兵节点,获取主节点信息:遍历哨兵节点,通过其中一个哨兵节点+masterName获得主节点的信息;该功能是通过调用哨兵节点的sentinel get-master-addr-by-name命令实现,该命令示例如下:

20190410141755404.png

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM5NjY5MDU4_size_16_color_FFFFFF_t_70

可以看到192.168.184.135的确是master

一旦获得主节点信息,停止遍历(因此一般来说遍历到第一个哨兵节点,循环就停止了)。

增加对哨兵的监听:这样当发生故障转移时,客户端便可以收到哨兵的通知,从而完成主节点的切换。具体做法是:利用Redis提供的发布订阅功能,为每一个哨兵节点开启一个单独的线程,订阅哨兵节点的+switch-master频道,当收到消息时,重新初始化连接池。

三、总结

通过客户端原理的介绍,可以加深对哨兵功能的理解,如下:

配置提供者:客户端可以通过哨兵节点+masterName获取主节点信息,在这里哨兵起到的作用就是配置提供者。

需要注意的是,哨兵只是配置提供者,而不是代理。二者的区别在于:

1、如果是配置提供者,客户端在通过哨兵获得主节点信息后,会直接建立到主节点的连接,后续的请求(如set/get)会直接发向主节点;

2、如果是代理,客户端的每一次请求都会发向哨兵,哨兵再通过主节点处理请求。

举一个例子可以很好的理解哨兵的作用是配置提供者,而不是代理。在前面部署的哨兵系统中,将哨兵节点的配置文件进行如下修改:

  1. sentinel monitor mymaster 192.168.92.128 6379 2
  2. 改为
  3. sentinel monitor mymaster 127.0.0.1 6379 2

然后,将前述客户端代码在局域网的另外一台机器上运行,会发现客户端无法连接主节点;这是因为哨兵作为配置提供者,客户端通过它查询到主节点的地址为127.0.0.1:6379,客户端会向127.0.0.1:6379建立Redis连接,自然无法连接。如果哨兵是代理,这个问题就不会出现了。

通知:哨兵节点在故障转移完成后,会将新的主节点信息发送给客户端,以便客户端及时切换主节点。

二、基本原理

一、哨兵节点支持的命令

哨兵节点作为运行在特殊模式下的Redis节点,其支持的命令与普通的Redis节点不同。在运维中,我们可以通过这些命令查询或修改哨兵系统;不过更重要的是,哨兵系统要实现故障发现、故障转移等各种功能,离不开哨兵节点之间的通信,而通信的很大一部分是通过哨兵节点支持的命令来实现的。下面介绍哨兵节点支持的主要命令:

基础查询:

通过这些命令,可以查询哨兵系统的拓扑结构、节点信息、配置信息等。

1、info sentinel:获取监控的所有主节点的基本信息。

2、sentinel masters:获取监控的所有主节点的详细信息。

3、sentinel master mymaster:获取监控的主节点mymaster的详细信息。

4、sentinel slaves mymaster:获取监控的主节点mymaster的从节点的详细信息。

5、sentinel sentinels mymaster:获取监控的主节点mymaster的哨兵节点的详细信息。

6、sentinel get - master - addr - by- name mymaster:获取监控的主节点mymaster的地址信息,前文已有介绍。

7、sentinel is-master-down-by-addr:哨兵节点之间可以通过该命令询问主节点是否下线,从而对是否客观下线做出判断。

增加/移除对主节点的监控:

sentinel monitor mymaster2 192.168.92.128 16379 2:与部署哨兵节点时配置文件中的sentinel monitor功能完全一样,不再详述。

sentinel remove mymaster2:取消当前哨兵节点对主节点mymaster2的监控。

强制故障转移:

sentinel failover mymaster:该命令可以强制对mymaster执行故障转移,即便当前的主节点运行完好;例如,如果当前主节点所在机器即将报废,便可以提前通过failover命令进行故障转移。

二、基本原理

关于哨兵的原理,关键是了解以下几个概念:

定时任务:每个哨兵节点维护了3个定时任务。定时任务的功能分别如下:通过向主从节点发送info命令获取最新的主从结构;通过发布订阅功能获取其他哨兵节点的信息;通过向其他节点发送ping命令进行心跳检测,判断是否下线。

主观下线:在心跳检测的定时任务中,如果其他节点超过一定时间没有回复,哨兵节点就会将其进行主观下线。顾名思义,主观下线的意思是一个哨兵节点“主观地”判断下线;与主观下线相对应的是客观下线。

客观下线:哨兵节点在对主节点进行主观下线后,会通过sentinel is-master-down-by-addr命令询问其他哨兵节点该主节点的状态;如果判断主节点下线的哨兵数量达到一定数值,则对该主节点进行客观下线。

需要特别注意的是,客观下线是主节点才有的概念;如果从节点和哨兵节点发生故障,被哨兵主观下线后,不会再有后续的客观下线和故障转移操作。

选举领导者哨兵节点:当主节点被判断客观下线以后,各个哨兵节点会进行协商,选举出一个领导者哨兵节点,并由该领导者节点对其进行故障转移操作。

监视该主节点的所有哨兵都有可能被选为领导者,选举使用的算法是Raft算法;Raft算法的基本思路是先到先得:即在一轮选举中,哨兵A向B发送成为领导者的申请,如果B没有同意过其他哨兵,则会同意A成为领导者。选举的具体过程这里不做详细描述,一般来说,哨兵选择的过程很快,谁先完成客观下线,一般就能成为领导者。

故障转移:选举出的领导者哨兵,开始进行故障转移操作,该操作大体可以分为3个步骤:

1、在从节点中选择新的主节点:选择的原则是,首先过滤掉不健康的从节点;然后选择优先级最高的从节点(由slave-priority指定);如果优先级无法区分,则选择复制偏移量最大的从节点;如果仍无法区分,则选择runid最小的从节点。

2、更新主从状态:通过slaveof no one命令,让选出来的从节点成为主节点;并通过slaveof命令让其他节点成为其从节点。

3、将已经下线的主节点(即6379)设置为新的主节点的从节点,当6379重新上线后,它会成为新的主节点的从节点。

通过上述几个关键概念,可以基本了解哨兵的工作原理。为了更形象的说明,下图展示了领导者哨兵节点的日志,包括从节点启动到完成故障转移。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM5NjY5MDU4_size_16_color_FFFFFF_t_70 1

三、配置与实践建议

一、配置

下面介绍与哨兵相关的几个配置。

配置1:sentinel monitor {masterName} {masterIp} {masterPort} {quorum}

sentinel monitor是哨兵最核心的配置,在前文讲述部署哨兵节点时已说明,其中:masterName指定了主节点名称,masterIp和masterPort指定了主节点地址,quorum是判断主节点客观下线的哨兵数量阈值:当判定主节点下线的哨兵数量达到quorum时,对主节点进行客观下线。建议取值为哨兵数量的一半加1。

配置2:sentinel down-after-milliseconds {masterName} {time}

sentinel down-after-milliseconds与主观下线的判断有关:哨兵使用ping命令对其他节点进行心跳检测,如果其他节点超过down-after-milliseconds配置的时间没有回复,哨兵就会将其进行主观下线。该配置对主节点、从节点和哨兵节点的主观下线判定都有效。

down-after-milliseconds的默认值是30000,即30s;可以根据不同的网络环境和应用要求来调整:值越大,对主观下线的判定会越宽松,好处是误判的可能性小,坏处是故障发现和故障转移的时间变长,客户端等待的时间也会变长。例如,如果应用对可用性要求较高,则可以将值适当调小,当故障发生时尽快完成转移;如果网络环境相对较差,可以适当提高该阈值,避免频繁误判。

配置3:sentinel parallel - syncs {masterName} {number}

sentinel parallel-syncs与故障转移之后从节点的复制有关:它规定了每次向新的主节点发起复制操作的从节点个数。例如,假设主节点切换完成之后,有3个从节点要向新的主节点发起复制;如果parallel-syncs=1,则从节点会一个一个开始复制;如果parallel-syncs=3,则3个从节点会一起开始复制。

parallel-syncs取值越大,从节点完成复制的时间越快,但是对主节点的网络负载、硬盘负载造成的压力也越大;应根据实际情况设置。例如,如果主节点的负载较低,而从节点对服务可用的要求较高,可以适量增加parallel-syncs取值。parallel-syncs的默认值是1。

配置4:sentinel failover - timeout {masterName} {time}

sentinel failover-timeout与故障转移超时的判断有关,但是该参数不是用来判断整个故障转移阶段的超时,而是其几个子阶段的超时,例如如果主节点晋升从节点时间超过timeout,或从节点向新的主节点发起复制操作的时间(不包括复制数据的时间)超过timeout,都会导致故障转移超时失败。

failover-timeout的默认值是180000,即180s;如果超时,则下一次该值会变为原来的2倍。

配置5:除上述几个参数外,还有一些其他参数,如安全验证相关的参数,这里不做介绍。

二、实践建议

1、哨兵节点的数量应不止一个。一方面增加哨兵节点的冗余,避免哨兵本身成为高可用的瓶颈;另一方面减少对下线的误判。此外,这些不同的哨兵节点应部署在不同的物理机上。

2、哨兵节点的数量应该是奇数,便于哨兵通过投票做出“决策”:领导者选举的决策、客观下线的决策等。

3、各个哨兵节点的配置应一致,包括硬件、参数等;此外,所有节点都应该使用ntp或类似服务,保证时间准确、一致。

4、哨兵的配置提供者和通知客户端功能,需要客户端的支持才能实现,如前文所说的Jedis;如果开发者使用的库未提供相应支持,则可能需要开发者自己实现。

5、当哨兵系统中的节点在Docker(或其他可能进行端口映射的软件)中部署时,应特别注意端口映射可能会导致哨兵系统无法正常工作,因为哨兵的工作基于与其他节点的通信,而Docker的端口映射可能导致哨兵无法连接到其他节点。例如,哨兵之间互相发现,依赖于它们对外宣称的IP和port,如果某个哨兵A部署在做了端口映射的Docker中,那么其他哨兵使用A宣称的port无法连接到A。

三、总结

在主从复制的基础上,哨兵引入了主节点的自动故障转移,进一步提高了Redis的高可用性;但是哨兵的缺陷同样很明显:哨兵无法对从节点进行自动故障转移,在读写分离场景下,从节点故障会导致读服务不可用,需要我们对从节点做额外的监控、切换操作。

此外,哨兵仍然没有解决写操作无法负载均衡、及存储能力受到单机限制的问题;这些问题的解决需要使用集群。

///

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM5NjY5MDU4_size_16_color_FFFFFF_t_70 2

  1. RedisAutoConfiguration源码

通过源码可以看出,SpringBoot自动帮我们在容器中生成了一个RedisTemplate和一个StringRedisTemplate。但是,这个RedisTemplate的泛型是,写代码不方便,需要写好多类型转换的代码;我们需要一个泛型为形式的RedisTemplate。并且,这个RedisTemplate没有设置数据存在Redis时,key及value的序列化方式。

  1. 看到这个**@ConditionalOnMissingBean**注解后,就知道**如果Spring容器中有了RedisTemplate对象了,这个自动配置的RedisTemplate不会实例化**。因此我们可以直接自己写个配置类,配置RedisTemplate

/

既然自动配置不好用,就重新配置一个RedisTemplate。

  1. package com.bootredis.bootredis.config;
  2. import com.fasterxml.jackson.annotation.JsonAutoDetect;
  3. import com.fasterxml.jackson.annotation.PropertyAccessor;
  4. import com.fasterxml.jackson.databind.ObjectMapper;
  5. import org.springframework.context.annotation.Bean;
  6. import org.springframework.context.annotation.Configuration;
  7. import org.springframework.data.redis.connection.RedisConnectionFactory;
  8. import org.springframework.data.redis.core.RedisTemplate;
  9. import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
  10. import org.springframework.data.redis.serializer.StringRedisSerializer;
  11. //RedisAutoConfiguration类中的默认的RedisTemplate不好用,直接重写一个
  12. @Configuration
  13. public class RedisConfig {
  14. @Bean
  15. public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
  16. RedisTemplate<String, Object> template = new RedisTemplate<>();
  17. template.setConnectionFactory(factory);
  18. // 使用Jackson2JsonRedisSerialize 替换默认的jdkSerializeable序列化
  19. Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
  20. ObjectMapper om = new ObjectMapper();
  21. om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
  22. om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
  23. jackson2JsonRedisSerializer.setObjectMapper(om);
  24. StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
  25. // key采用String的序列化方式
  26. template.setKeySerializer(stringRedisSerializer);
  27. // hash的key也采用String的序列化方式
  28. template.setHashKeySerializer(stringRedisSerializer);
  29. // value序列化方式采用jackson
  30. template.setValueSerializer(jackson2JsonRedisSerializer);
  31. // hash的value序列化方式采用jackson
  32. template.setHashValueSerializer(jackson2JsonRedisSerializer);
  33. template.afterPropertiesSet();
  34. return template;
  35. }
  36. }

/

如果不重新注入到spring容器中一个RedisTemplate对象,在程序中使用泛型必须为,因为默认就这两种。。。。

  1. //spring boot帮我们注入的redisTemplate类,泛型里面只能写 <String, String>、<Object, Object>
  2. @Autowired
  3. private RedisTemplate<Object, Object> redisTemplate;

/

一般使用封装简单的工具类

  1. package com.bootredis.bootredis.util;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.data.redis.core.RedisTemplate;
  4. import org.springframework.stereotype.Component;
  5. import org.springframework.util.CollectionUtils;
  6. import java.util.List;
  7. import java.util.Map;
  8. import java.util.Set;
  9. import java.util.concurrent.TimeUnit;
  10. @Component
  11. public class RedisCacheManager {
  12. @Autowired
  13. private RedisTemplate<String, Object> redisTemplate;
  14. /**
  15. * 指定缓存失效时间
  16. *
  17. * @param key
  18. * 键
  19. * @param time
  20. * 时间(秒)
  21. * @return
  22. */
  23. public boolean expire(String key, long time) {
  24. try {
  25. if (time > 0) {
  26. redisTemplate.expire(key, time, TimeUnit.SECONDS);
  27. }
  28. return true;
  29. } catch (Exception e) {
  30. e.printStackTrace();
  31. return false;
  32. }
  33. }
  34. /**
  35. * 根据key 获取过期时间
  36. *
  37. * @param key
  38. * 键 不能为null
  39. * @return 时间(秒) 返回0代表为永久有效
  40. */
  41. public long getExpire(String key) {
  42. return redisTemplate.getExpire(key, TimeUnit.SECONDS);
  43. }
  44. /**
  45. * 判断key是否存在
  46. *
  47. * @param key
  48. * 键
  49. * @return true 存在 false不存在
  50. */
  51. public boolean hasKey(String key) {
  52. try {
  53. return redisTemplate.hasKey(key);
  54. } catch (Exception e) {
  55. e.printStackTrace();
  56. return false;
  57. }
  58. }
  59. /**
  60. * 删除缓存
  61. *
  62. * @param key
  63. * 可以传一个值 或多个
  64. */
  65. @SuppressWarnings("unchecked")
  66. public void del(String... key) {
  67. if (key != null && key.length > 0) {
  68. if (key.length == 1) {
  69. redisTemplate.delete(key[0]);
  70. } else {
  71. redisTemplate.delete(CollectionUtils.arrayToList(key));
  72. }
  73. }
  74. }
  75. // ============================String=============================
  76. /**
  77. * 普通缓存获取
  78. *
  79. * @param key
  80. * 键
  81. * @return 值
  82. */
  83. public Object get(String key) {
  84. return key == null ? null : redisTemplate.opsForValue().get(key);
  85. }
  86. /**
  87. * 普通缓存放入
  88. *
  89. * @param key
  90. * 键
  91. * @param value
  92. * 值
  93. * @return true成功 false失败
  94. */
  95. public boolean set(String key, Object value) {
  96. try {
  97. redisTemplate.opsForValue().set(key, value);
  98. return true;
  99. } catch (Exception e) {
  100. e.printStackTrace();
  101. return false;
  102. }
  103. }
  104. /**
  105. * 普通缓存放入并设置时间
  106. *
  107. * @param key
  108. * 键
  109. * @param value
  110. * 值
  111. * @param time
  112. * 时间(秒) time要大于0 如果time小于等于0 将设置无限期
  113. * @return true成功 false 失败
  114. */
  115. public boolean set(String key, Object value, long time) {
  116. try {
  117. if (time > 0) {
  118. redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
  119. } else {
  120. set(key, value);
  121. }
  122. return true;
  123. } catch (Exception e) {
  124. e.printStackTrace();
  125. return false;
  126. }
  127. }
  128. /**
  129. * 递增
  130. *
  131. * @param key
  132. * 键
  133. * @param delta
  134. * 要增加几(大于0)
  135. * @return
  136. */
  137. public long incr(String key, long delta) {
  138. if (delta < 0) {
  139. throw new RuntimeException("递增因子必须大于0");
  140. }
  141. return redisTemplate.opsForValue().increment(key, delta);
  142. }
  143. /**
  144. * 递减
  145. *
  146. * @param key
  147. * 键
  148. * @param delta
  149. * 要减少几(小于0)
  150. * @return
  151. */
  152. public long decr(String key, long delta) {
  153. if (delta < 0) {
  154. throw new RuntimeException("递减因子必须大于0");
  155. }
  156. return redisTemplate.opsForValue().increment(key, -delta);
  157. }
  158. // ================================Map=================================
  159. /**
  160. * HashGet
  161. *
  162. * @param key
  163. * 键 不能为null
  164. * @param item
  165. * 项 不能为null
  166. * @return 值
  167. */
  168. public Object hget(String key, String item) {
  169. return redisTemplate.opsForHash().get(key, item);
  170. }
  171. /**
  172. * 获取hashKey对应的所有键值
  173. *
  174. * @param key
  175. * 键
  176. * @return 对应的多个键值
  177. */
  178. public Map<Object, Object> hmget(String key) {
  179. return redisTemplate.opsForHash().entries(key);
  180. }
  181. /**
  182. * HashSet
  183. *
  184. * @param key
  185. * 键
  186. * @param map
  187. * 对应多个键值
  188. * @return true 成功 false 失败
  189. */
  190. public boolean hmset(String key, Map<String, Object> map) {
  191. try {
  192. redisTemplate.opsForHash().putAll(key, map);
  193. return true;
  194. } catch (Exception e) {
  195. e.printStackTrace();
  196. return false;
  197. }
  198. }
  199. /**
  200. * HashSet 并设置时间
  201. *
  202. * @param key
  203. * 键
  204. * @param map
  205. * 对应多个键值
  206. * @param time
  207. * 时间(秒)
  208. * @return true成功 false失败
  209. */
  210. public boolean hmset(String key, Map<String, Object> map, long time) {
  211. try {
  212. redisTemplate.opsForHash().putAll(key, map);
  213. if (time > 0) {
  214. expire(key, time);
  215. }
  216. return true;
  217. } catch (Exception e) {
  218. e.printStackTrace();
  219. return false;
  220. }
  221. }
  222. /**
  223. * 向一张hash表中放入数据,如果不存在将创建
  224. *
  225. * @param key
  226. * 键
  227. * @param item
  228. * 项
  229. * @param value
  230. * 值
  231. * @return true 成功 false失败
  232. */
  233. public boolean hset(String key, String item, Object value) {
  234. try {
  235. redisTemplate.opsForHash().put(key, item, value);
  236. return true;
  237. } catch (Exception e) {
  238. e.printStackTrace();
  239. return false;
  240. }
  241. }
  242. /**
  243. * 向一张hash表中放入数据,如果不存在将创建
  244. *
  245. * @param key
  246. * 键
  247. * @param item
  248. * 项
  249. * @param value
  250. * 值
  251. * @param time
  252. * 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
  253. * @return true 成功 false失败
  254. */
  255. public boolean hset(String key, String item, Object value, long time) {
  256. try {
  257. redisTemplate.opsForHash().put(key, item, value);
  258. if (time > 0) {
  259. expire(key, time);
  260. }
  261. return true;
  262. } catch (Exception e) {
  263. e.printStackTrace();
  264. return false;
  265. }
  266. }
  267. /**
  268. * 删除hash表中的值
  269. *
  270. * @param key
  271. * 键 不能为null
  272. * @param item
  273. * 项 可以使多个 不能为null
  274. */
  275. public void hdel(String key, Object... item) {
  276. redisTemplate.opsForHash().delete(key, item);
  277. }
  278. /**
  279. * 判断hash表中是否有该项的值
  280. *
  281. * @param key
  282. * 键 不能为null
  283. * @param item
  284. * 项 不能为null
  285. * @return true 存在 false不存在
  286. */
  287. public boolean hHasKey(String key, String item) {
  288. return redisTemplate.opsForHash().hasKey(key, item);
  289. }
  290. /**
  291. * hash递增 如果不存在,就会创建一个 并把新增后的值返回
  292. *
  293. * @param key
  294. * 键
  295. * @param item
  296. * 项
  297. * @param by
  298. * 要增加几(大于0)
  299. * @return
  300. */
  301. public double hincr(String key, String item, double by) {
  302. return redisTemplate.opsForHash().increment(key, item, by);
  303. }
  304. /**
  305. * hash递减
  306. *
  307. * @param key
  308. * 键
  309. * @param item
  310. * 项
  311. * @param by
  312. * 要减少记(小于0)
  313. * @return
  314. */
  315. public double hdecr(String key, String item, double by) {
  316. return redisTemplate.opsForHash().increment(key, item, -by);
  317. }
  318. // ============================set=============================
  319. /**
  320. * 根据key获取Set中的所有值
  321. *
  322. * @param key
  323. * 键
  324. * @return
  325. */
  326. public Set<Object> sGet(String key) {
  327. try {
  328. return redisTemplate.opsForSet().members(key);
  329. } catch (Exception e) {
  330. e.printStackTrace();
  331. return null;
  332. }
  333. }
  334. /**
  335. * 根据value从一个set中查询,是否存在
  336. *
  337. * @param key
  338. * 键
  339. * @param value
  340. * 值
  341. * @return true 存在 false不存在
  342. */
  343. public boolean sHasKey(String key, Object value) {
  344. try {
  345. return redisTemplate.opsForSet().isMember(key, value);
  346. } catch (Exception e) {
  347. e.printStackTrace();
  348. return false;
  349. }
  350. }
  351. /**
  352. * 将数据放入set缓存
  353. *
  354. * @param key
  355. * 键
  356. * @param values
  357. * 值 可以是多个
  358. * @return 成功个数
  359. */
  360. public long sSet(String key, Object... values) {
  361. try {
  362. return redisTemplate.opsForSet().add(key, values);
  363. } catch (Exception e) {
  364. e.printStackTrace();
  365. return 0;
  366. }
  367. }
  368. /**
  369. * 将set数据放入缓存
  370. *
  371. * @param key
  372. * 键
  373. * @param time
  374. * 时间(秒)
  375. * @param values
  376. * 值 可以是多个
  377. * @return 成功个数
  378. */
  379. public long sSetAndTime(String key, long time, Object... values) {
  380. try {
  381. Long count = redisTemplate.opsForSet().add(key, values);
  382. if (time > 0)
  383. expire(key, time);
  384. return count;
  385. } catch (Exception e) {
  386. e.printStackTrace();
  387. return 0;
  388. }
  389. }
  390. /**
  391. * 获取set缓存的长度
  392. *
  393. * @param key
  394. * 键
  395. * @return
  396. */
  397. public long sGetSetSize(String key) {
  398. try {
  399. return redisTemplate.opsForSet().size(key);
  400. } catch (Exception e) {
  401. e.printStackTrace();
  402. return 0;
  403. }
  404. }
  405. /**
  406. * 移除值为value的
  407. *
  408. * @param key
  409. * 键
  410. * @param values
  411. * 值 可以是多个
  412. * @return 移除的个数
  413. */
  414. public long setRemove(String key, Object... values) {
  415. try {
  416. Long count = redisTemplate.opsForSet().remove(key, values);
  417. return count;
  418. } catch (Exception e) {
  419. e.printStackTrace();
  420. return 0;
  421. }
  422. }
  423. // ===============================list=================================
  424. /**
  425. * 获取list缓存的内容
  426. *
  427. * @param key
  428. * 键
  429. * @param start
  430. * 开始
  431. * @param end
  432. * 结束 0 到 -1代表所有值
  433. * @return
  434. */
  435. public List<Object> lGet(String key, long start, long end) {
  436. try {
  437. return redisTemplate.opsForList().range(key, start, end);
  438. } catch (Exception e) {
  439. e.printStackTrace();
  440. return null;
  441. }
  442. }
  443. /**
  444. * 获取list缓存的长度
  445. *
  446. * @param key
  447. * 键
  448. * @return
  449. */
  450. public long lGetListSize(String key) {
  451. try {
  452. return redisTemplate.opsForList().size(key);
  453. } catch (Exception e) {
  454. e.printStackTrace();
  455. return 0;
  456. }
  457. }
  458. /**
  459. * 通过索引 获取list中的值
  460. *
  461. * @param key
  462. * 键
  463. * @param index
  464. * 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
  465. * @return
  466. */
  467. public Object lGetIndex(String key, long index) {
  468. try {
  469. return redisTemplate.opsForList().index(key, index);
  470. } catch (Exception e) {
  471. e.printStackTrace();
  472. return null;
  473. }
  474. }
  475. /**
  476. * 将list放入缓存
  477. *
  478. * @param key
  479. * 键
  480. * @param value
  481. * 值
  482. * @return
  483. */
  484. public boolean lSet(String key, Object value) {
  485. try {
  486. redisTemplate.opsForList().rightPush(key, value);
  487. return true;
  488. } catch (Exception e) {
  489. e.printStackTrace();
  490. return false;
  491. }
  492. }
  493. /**
  494. * 将list放入缓存
  495. *
  496. * @param key
  497. * 键
  498. * @param value
  499. * 值
  500. * @param time
  501. * 时间(秒)
  502. * @return
  503. */
  504. public boolean lSet(String key, Object value, long time) {
  505. try {
  506. redisTemplate.opsForList().rightPush(key, value);
  507. if (time > 0)
  508. expire(key, time);
  509. return true;
  510. } catch (Exception e) {
  511. e.printStackTrace();
  512. return false;
  513. }
  514. }
  515. /**
  516. * 将list放入缓存
  517. *
  518. * @param key
  519. * 键
  520. * @param value
  521. * 值
  522. * @return
  523. */
  524. public boolean lSet(String key, List<Object> value) {
  525. try {
  526. redisTemplate.opsForList().rightPushAll(key, value);
  527. return true;
  528. } catch (Exception e) {
  529. e.printStackTrace();
  530. return false;
  531. }
  532. }
  533. /**
  534. * 将list放入缓存
  535. *
  536. * @param key
  537. * 键
  538. * @param value
  539. * 值
  540. * @param time
  541. * 时间(秒)
  542. * @return
  543. */
  544. public boolean lSet(String key, List<Object> value, long time) {
  545. try {
  546. redisTemplate.opsForList().rightPushAll(key, value);
  547. if (time > 0)
  548. expire(key, time);
  549. return true;
  550. } catch (Exception e) {
  551. e.printStackTrace();
  552. return false;
  553. }
  554. }
  555. /**
  556. * 根据索引修改list中的某条数据
  557. *
  558. * @param key
  559. * 键
  560. * @param index
  561. * 索引
  562. * @param value
  563. * 值
  564. * @return
  565. */
  566. public boolean lUpdateIndex(String key, long index, Object value) {
  567. try {
  568. redisTemplate.opsForList().set(key, index, value);
  569. return true;
  570. } catch (Exception e) {
  571. e.printStackTrace();
  572. return false;
  573. }
  574. }
  575. /**
  576. * 移除N个值为value
  577. *
  578. * @param key
  579. * 键
  580. * @param count
  581. * 移除多少个
  582. * @param value
  583. * 值
  584. * @return 移除的个数
  585. */
  586. public long lRemove(String key, long count, Object value) {
  587. try {
  588. Long remove = redisTemplate.opsForList().remove(key, count, value);
  589. return remove;
  590. } catch (Exception e) {
  591. e.printStackTrace();
  592. return 0;
  593. }
  594. }
  595. }

参考:

Redis和springboot 整合redisUtil类

实现故障恢复自动化:详解Redis哨兵技术

Spring整合Redis(spring-data-redis)

Springboot2.0整合Redis(注解开发)

易百Redis教程

发表评论

表情:
评论列表 (有 0 条评论,354人围观)

还没有评论,来说两句吧...

相关阅读