springboot 整合 redis 主从同步 sentinel哨兵 实现商品抢购秒杀
2017-09-24 22:01
1236 查看
这段时间利用周末学习了一下redis的主从同步,同时配合sentinel哨兵机制,sentinel是redis实现HA的一种方式。为了能学以致用,这里使用springboot 把redis 主从同步及sentinel整合起来实现一个商品抢购的demo,同时在开发过程中遇到的问题也都整理下来了。一方面加深对所学知识的印象,另一方面希望可以为那些刚接触过这些知识的同学提供点实际帮助,springboot 整合redis主从及sentinel哨兵,自己从零开始,也花了一些时间,当然对那些大牛来说,此文可以绕过。
一、redis主从及sentinel环境配置
1、官方网站下载redis https://redis.io/download
2、解压压缩包
tar -xvf redis-4.0.1.tar.gz
3、解压完成后,进入目录 redis-4.0.1
cd redis-4.0.1
4、执行make命令
make
执行make报错,提示cc:未找到命令,原因是虚拟机系统中缺少gcc,安装gcc即可
虚拟机安装gcc命令
安装命令:yum -y install gcc automake autoconf libtool make
安装gcc后,执行make 继续报错:zmalloc.h:50:31: 致命错误:jemalloc/jemalloc.h:没有那个文件或目录
解决方案:
执行命令由make 改成 make MALLOC=libc
5、安装redis服务到指定的目录 /usr/local/redis
make PREFIX=/usr/local/redis install
6、创建配置文件
mkdir /etc/redis
复制配置文件到/etc/redis/ 下面去
cp redis.cnf /etc/redis/
7、启动redis客户端 进入redis 的bin 目录
./redis-server
8、查看redis是否正常启动
ps -ef | grep redis
或者查看redis端口是否被监听
netstat -tunple | grep 6379
9、修改redis 配置文件 后台启动
vi /etc/redis/redis.conf
修改daemonize no 将no改成yes 即可
./redis-server /etc/redis/redis-conf 使用配置文件后台启动
10、关闭linux后台运行的redis服务
进入 bin 目录
使用 pkill 命令
pkill -9 redis-server
11、redis客户端连接
./redis-cli -h 192.168.137.30 -p 6379
set name hello
get name
12、redis 主从同步,slave启动时,会给master发送一个同步命令,然后master以文件的形式同步给slave;
第一次是全量同步,以后会以增量的形式同步,
master同步时数据是非阻塞的,slave同步时时阻塞的(当slave正在同步时,如果应用发送请求过来,必须等slave同步完之后,才能接受请求)
哨兵机制 切换回来之前的主从 一是修改sentinel配置文件,二是关掉sentinel进程,重启redis主从
master 读写并行
slave 只读
./redis-cli -h 192.168.137.30 -p 6379 客户端连接
进入后
192.168.137.30:6379> info 查看配置信息
13、哨兵后台启动
修改配置文件sentinel.conf 增加 daemonize yes
启动 ./redis-sentinel /etc/redis/sentinel.conf
通过以上步骤,redis环境配置基本上就完成了。
开始创建springboot项目,这里使用的是idea,新建project-->Spring Initializr 然后next,定义相关包名 路径即可.
1、application.yml配置如下:
2、pom.xml增加redis依赖
3、RedisConfig.java redis相关配置类,容器启动时会加载。
4、redis服务接口,这里只写了一个下单是否成功方法
5、redis服务接口的实现类
6、测试类TestOrderController
7、测试地址:http://localhost:8080/orderTest/20
控制台输出:
2017-09-24 22:58:57.069 INFO 3856 --- [ Thread-35] c.h.h.miaosha.service.RedisServiceImpl : 已买数量: = 20
2017-09-24 22:58:57.069 INFO 3856 --- [ Thread-35] c.h.h.miaosha.service.RedisServiceImpl : 剩余数量:= 180
2017-09-24 22:59:03.081 INFO 3856 --- [ Thread-37] c.h.h.miaosha.service.RedisServiceImpl : 已买数量: = 40
2017-09-24 22:59:03.081 INFO 3856 --- [ Thread-37] c.h.h.miaosha.service.RedisServiceImpl : 剩余数量:= 160
2017-09-24 22:59:09.096 INFO 3856 --- [ Thread-25] c.h.h.miaosha.service.RedisServiceImpl : 已买数量: = 60
2017-09-24 22:59:09.097 INFO 3856 --- [ Thread-25] c.h.h.miaosha.service.RedisServiceImpl : 剩余数量:= 140
2017-09-24 22:59:15.120 INFO 3856 --- [ Thread-32] c.h.h.miaosha.service.RedisServiceImpl : 已买数量: = 80
2017-09-24 22:59:15.120 INFO 3856 --- [ Thread-32] c.h.h.miaosha.service.RedisServiceImpl : 剩余数量:= 120
2017-09-24 22:59:21.141 INFO 3856 --- [ Thread-29] c.h.h.miaosha.service.RedisServiceImpl : 已买数量: = 100
2017-09-24 22:59:21.141 INFO 3856 --- [ Thread-29] c.h.h.miaosha.service.RedisServiceImpl : 剩余数量:= 100
2017-09-24 22:59:27.154 INFO 3856 --- [ Thread-38] c.h.h.miaosha.service.RedisServiceImpl : 已买数量: = 120
2017-09-24 22:59:27.154 INFO 3856 --- [ Thread-38] c.h.h.miaosha.service.RedisServiceImpl : 剩余数量:= 80
2017-09-24 22:59:33.172 INFO 3856 --- [ Thread-26] c.h.h.miaosha.service.RedisServiceImpl : 已买数量: = 140
2017-09-24 22:59:33.172 INFO 3856 --- [ Thread-26] c.h.h.miaosha.service.RedisServiceImpl : 剩余数量:= 60
2017-09-24 22:59:39.180 INFO 3856 --- [ Thread-31] c.h.h.miaosha.service.RedisServiceImpl : 已买数量: = 160
2017-09-24 22:59:39.180 INFO 3856 --- [ Thread-31] c.h.h.miaosha.service.RedisServiceImpl : 剩余数量:= 40
2017-09-24 22:59:45.190 INFO 3856 --- [ Thread-36] c.h.h.miaosha.service.RedisServiceImpl : 已买数量: = 180
2017-09-24 22:59:45.190 INFO 3856 --- [ Thread-36] c.h.h.miaosha.service.RedisServiceImpl : 剩余数量:= 20
2017-09-24 22:59:51.210 INFO 3856 --- [ Thread-34] c.h.h.miaosha.service.RedisServiceImpl : 已买数量: = 200
2017-09-24 22:59:51.211 INFO 3856 --- [ Thread-34] c.h.h.miaosha.service.RedisServiceImpl : 剩余数量:= 0
2017-09-24 22:59:57.234 INFO 3856 --- [ Thread-27] c.h.h.miaosha.service.RedisServiceImpl : 对不起,商品已售完.
2017-09-24 23:00:03.243 INFO 3856 --- [ Thread-28] c.h.h.miaosha.service.RedisServiceImpl : 对不起,商品已售完.
2017-09-24 23:00:09.254 INFO 3856 --- [ Thread-24] c.h.h.miaosha.service.RedisServiceImpl : 对不起,商品已售完.
2017-09-24 23:00:15.266 INFO 3856 --- [ Thread-30] c.h.h.miaosha.service.RedisServiceImpl : 对不起,商品已售完.
2017-09-24 23:00:21.275 INFO 3856 --- [ Thread-33] c.h.h.miaosha.service.RedisServiceImpl : 对不起,商品已售完.
8、进入sentinel客户端 可以查看:
info
当前master地址: 192.168.137.30 : 6379
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=192.168.137.30:6379,slaves=1,sentinels=1
此时kill掉当前的master
查看sentinel日志:
2934:X 24 Sep 23:03:21.659 # +switch-master mymaster 192.168.137.30 6379 192.168.137.31 6379
2934:X 24 Sep 23:03:21.659 * +slave slave 192.168.137.30:6379 192.168.137.30 6379 @ mymaster 192.168.137.31 6379
2934:X 24 Sep 23:03:51.723 # +sdown slave 192.168.137.30:6379 192.168.137.30 6379 @ mymaster 192.168.137.31 6379
可以看到sentinel已经进行了 switch-master 将之前的slave切换成master,而之前的master则转换成了slave
项目控制台输出:
2017-09-24 23:06:27.336 INFO 3856 --- [8.137.32:26379]] redis.clients.jedis.JedisSentinelPool : Created JedisPool to master at 192.168.137.31:6379
这个时候也可以看到master由之前的192.168.137.30:6379 切换成 192.168.137.31:6379
到此,基本完成了,时间不早了,休息了,明天还要上班。
一、redis主从及sentinel环境配置
1、官方网站下载redis https://redis.io/download
2、解压压缩包
tar -xvf redis-4.0.1.tar.gz
3、解压完成后,进入目录 redis-4.0.1
cd redis-4.0.1
4、执行make命令
make
执行make报错,提示cc:未找到命令,原因是虚拟机系统中缺少gcc,安装gcc即可
虚拟机安装gcc命令
安装命令:yum -y install gcc automake autoconf libtool make
安装gcc后,执行make 继续报错:zmalloc.h:50:31: 致命错误:jemalloc/jemalloc.h:没有那个文件或目录
解决方案:
执行命令由make 改成 make MALLOC=libc
5、安装redis服务到指定的目录 /usr/local/redis
make PREFIX=/usr/local/redis install
6、创建配置文件
mkdir /etc/redis
复制配置文件到/etc/redis/ 下面去
cp redis.cnf /etc/redis/
7、启动redis客户端 进入redis 的bin 目录
./redis-server
8、查看redis是否正常启动
ps -ef | grep redis
或者查看redis端口是否被监听
netstat -tunple | grep 6379
9、修改redis 配置文件 后台启动
vi /etc/redis/redis.conf
修改daemonize no 将no改成yes 即可
./redis-server /etc/redis/redis-conf 使用配置文件后台启动
10、关闭linux后台运行的redis服务
进入 bin 目录
使用 pkill 命令
pkill -9 redis-server
11、redis客户端连接
./redis-cli -h 192.168.137.30 -p 6379
set name hello
get name
12、redis 主从同步,slave启动时,会给master发送一个同步命令,然后master以文件的形式同步给slave;
第一次是全量同步,以后会以增量的形式同步,
master同步时数据是非阻塞的,slave同步时时阻塞的(当slave正在同步时,如果应用发送请求过来,必须等slave同步完之后,才能接受请求)
哨兵机制 切换回来之前的主从 一是修改sentinel配置文件,二是关掉sentinel进程,重启redis主从
master 读写并行
slave 只读
./redis-cli -h 192.168.137.30 -p 6379 客户端连接
进入后
192.168.137.30:6379> info 查看配置信息
13、哨兵后台启动
修改配置文件sentinel.conf 增加 daemonize yes
启动 ./redis-sentinel /etc/redis/sentinel.conf
通过以上步骤,redis环境配置基本上就完成了。
开始创建springboot项目,这里使用的是idea,新建project-->Spring Initializr 然后next,定义相关包名 路径即可.
1、application.yml配置如下:
spring: redis: hostName: 192.168.137.30 port: 6379 password: pool: maxActive: 200 maxWait: -1 maxIdle: 8 minIdle: 0 timeout: 0 database: 0 sentinel: master: mymaster nodes: 192.168.137.32 port: 26379 server: port: 8080
2、pom.xml增加redis依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <version>1.5.6.RELEASE</version> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency>
3、RedisConfig.java redis相关配置类,容器启动时会加载。
@Configuration @EnableAutoConfiguration public class RedisConfig { private static Logger logger = LoggerFactory.getLogger(RedisConfig.class); @Value("${spring.redis.sentinel.master}") private String master; @Value("${spring.redis.sentinel.nodes}") private String sentinelHost; @Value("${spring.redis.sentinel.port}") private Integer sentinelPort; @Bean @ConfigurationProperties(prefix="spring.redis") public JedisPoolConfig getRedisConfig(){ JedisPoolConfig config = new JedisPoolConfig(); return config; } @Bean @ConfigurationProperties(prefix="spring.redis") public JedisConnectionFactory getConnectionFactory(){ JedisPoolConfig config = getRedisConfig(); JedisConnectionFactory factory = new JedisConnectionFactory(getRedisSentinelConfig(), config); factory.setPoolConfig(config); return factory; } @Bean @ConfigurationProperties(prefix = "spring.sentinel") public RedisSentinelConfiguration getRedisSentinelConfig(){ RedisSentinelConfiguration sentinelConfiguration = new RedisSentinelConfiguration(); sentinelConfiguration.setMaster(master); sentinelConfiguration.sentinel(sentinelHost,sentinelPort); return sentinelConfiguration; } @Bean public RedisTemplate<?, ?> getRedisTemplate(){ RedisTemplate<?,?> redisTemplate = new StringRedisTemplate(getConnectionFactory()); return redisTemplate; } }
4、redis服务接口,这里只写了一个下单是否成功方法
public interface IRedisService { public boolean isOrderSuccess(int buyCount,long flashSellEndDate); }
5、redis服务接口的实现类
@Service public class RedisServiceImpl implements IRedisService { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired RedisTemplate redisTemplate; private static final int GOODS_TOTAL_COUNT = 200;//商品总数量 private static final String LOCK_KEY = "checkLock";//线程锁key private static final String SELL_COUNT_KEY = "sellCountKeyNew2";//redis中存放的已卖数量的key private static final int LOCK_EXPIRE = 6 * 1000; //锁占有时长 /** * 检查下单是否成功 * @param buyCount 购买数量 * @param flashSellEndDate 截止时间 * @return */ public boolean isOrderSuccess(int buyCount,long flashSellEndDate) { if(flashSellEndDate <= 0){ logger.info("抢购活动已经结束:" + flashSellEndDate); return false; } boolean resultFlag = false; try { if (redisLock(LOCK_KEY, LOCK_EXPIRE)) { Integer haveSoldCount = (Integer) this.getValueByKey(SELL_COUNT_KEY); Integer totalSoldCount = (haveSoldCount == null ? 0 : haveSoldCount) + buyCount; if (totalSoldCount <= GOODS_TOTAL_COUNT) { this.setKeyValueWithExpire(SELL_COUNT_KEY, totalSoldCount, flashSellEndDate); resultFlag = true; logger.info("已买数量: = " + totalSoldCount); logger.info("剩余数量:= " + (GOODS_TOTAL_COUNT - totalSoldCount)); }else{ if(haveSoldCount < GOODS_TOTAL_COUNT){ logger.info("对不起,您购买的数量已经超出商品库存数量,请重新下单."); }else{ logger.info("对不起,商品已售完."); } } this.removeByKey(LOCK_KEY); } else { Integer soldCount = (Integer) this.getValueByKey(LOCK_KEY); if(soldCount != null && soldCount >= GOODS_TOTAL_COUNT){ //商品已经售完 logger.info("all goods have sold out"); return false; } Thread.sleep(1000);//没有获取到锁 1s后重试 return isOrderSuccess(buyCount,flashSellEndDate); } }catch (Exception e){ e.printStackTrace(); } return resultFlag; } /** * redis 锁 * @param lock 锁的key * @param expire 锁的时长 * @return */ public Boolean redisLock(final String lock, final int expire) { return (Boolean) redisTemplate.execute(new RedisCallback<Boolean>() { @Override public Boolean doInRedis(RedisConnection connection) throws DataAccessException { boolean locked = false; byte[] lockKeyName = redisTemplate.getStringSerializer().serialize(lock); byte[] lockValue = redisTemplate.getValueSerializer().serialize(getDateAferExpire(expire)); locked = connection.setNX(lockKeyName, lockValue); if (locked){ connection.expire(lockKeyName, TimeoutUtils.toSeconds(expire, TimeUnit.MILLISECONDS)); } return locked; } }); } /** * * 判断key是否存在 * @param key */ public boolean existsKey(final String key) { return redisTemplate.hasKey(key); } /** * 获取指定时长后的Date * @param expireTime * @return */ public Date getDateAferExpire(int expireTime){ Calendar calendar = Calendar.getInstance(); calendar.add(Calendar.MILLISECOND, expireTime); return calendar.getTime(); } /** * 根据key 获取对应的value * * @param key */ public Object getValueByKey(final String key) { Object result = null; ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue(); result = operations.get(key); return result; } /** * 删除指定的key * * @param key */ public void removeByKey(final String key) { if (existsKey(key)) { redisTemplate.delete(key); } } /** * 设置带有指定时长的key value * @param key * @param value * @param expireTime * @return */ public boolean setKeyValueWithExpire(final String key, Object value, Long expireTime) { boolean result = false; try { ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue(); operations.set(key, value); if (expireTime != null) { redisTemplate.expire(key, expireTime, TimeUnit.SECONDS); } result = true; } catch (Exception e) { e.printStackTrace(); } return result; } }
6、测试类TestOrderController
@RestController public class TestOrderController { @Autowired IRedisService redisService; private int buyCount = 20; private static final int TEST_NUM = 15; private static final String SELL_END_DATE = "2017-09-24 23:50:00"; private CountDownLatch cdl = new CountDownLatch(TEST_NUM); @RequestMapping("orderTest/{buyCountParam}") public String orderTest(@PathVariable int buyCountParam){ buyCount = buyCountParam; for (int i=0; i<TEST_NUM; i++){ new Thread(new MyThread()).start(); cdl.countDown(); } return "success"; } private class MyThread implements Runnable{ @Override public void run() { try { cdl.await(); } catch (InterruptedException e) { e.printStackTrace(); } Calendar calendar = Calendar.getInstance(); Calendar calendar1 = Calendar.getInstance(); try { calendar1.setTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(SELL_END_DATE)); } catch (ParseException e) { e.printStackTrace(); } redisService.isOrderSuccess(buyCount,(calendar1.getTime().getTime() - calendar.getTime().getTime()) / 1000); } } }
7、测试地址:http://localhost:8080/orderTest/20
控制台输出:
2017-09-24 22:58:57.069 INFO 3856 --- [ Thread-35] c.h.h.miaosha.service.RedisServiceImpl : 已买数量: = 20
2017-09-24 22:58:57.069 INFO 3856 --- [ Thread-35] c.h.h.miaosha.service.RedisServiceImpl : 剩余数量:= 180
2017-09-24 22:59:03.081 INFO 3856 --- [ Thread-37] c.h.h.miaosha.service.RedisServiceImpl : 已买数量: = 40
2017-09-24 22:59:03.081 INFO 3856 --- [ Thread-37] c.h.h.miaosha.service.RedisServiceImpl : 剩余数量:= 160
2017-09-24 22:59:09.096 INFO 3856 --- [ Thread-25] c.h.h.miaosha.service.RedisServiceImpl : 已买数量: = 60
2017-09-24 22:59:09.097 INFO 3856 --- [ Thread-25] c.h.h.miaosha.service.RedisServiceImpl : 剩余数量:= 140
2017-09-24 22:59:15.120 INFO 3856 --- [ Thread-32] c.h.h.miaosha.service.RedisServiceImpl : 已买数量: = 80
2017-09-24 22:59:15.120 INFO 3856 --- [ Thread-32] c.h.h.miaosha.service.RedisServiceImpl : 剩余数量:= 120
2017-09-24 22:59:21.141 INFO 3856 --- [ Thread-29] c.h.h.miaosha.service.RedisServiceImpl : 已买数量: = 100
2017-09-24 22:59:21.141 INFO 3856 --- [ Thread-29] c.h.h.miaosha.service.RedisServiceImpl : 剩余数量:= 100
2017-09-24 22:59:27.154 INFO 3856 --- [ Thread-38] c.h.h.miaosha.service.RedisServiceImpl : 已买数量: = 120
2017-09-24 22:59:27.154 INFO 3856 --- [ Thread-38] c.h.h.miaosha.service.RedisServiceImpl : 剩余数量:= 80
2017-09-24 22:59:33.172 INFO 3856 --- [ Thread-26] c.h.h.miaosha.service.RedisServiceImpl : 已买数量: = 140
2017-09-24 22:59:33.172 INFO 3856 --- [ Thread-26] c.h.h.miaosha.service.RedisServiceImpl : 剩余数量:= 60
2017-09-24 22:59:39.180 INFO 3856 --- [ Thread-31] c.h.h.miaosha.service.RedisServiceImpl : 已买数量: = 160
2017-09-24 22:59:39.180 INFO 3856 --- [ Thread-31] c.h.h.miaosha.service.RedisServiceImpl : 剩余数量:= 40
2017-09-24 22:59:45.190 INFO 3856 --- [ Thread-36] c.h.h.miaosha.service.RedisServiceImpl : 已买数量: = 180
2017-09-24 22:59:45.190 INFO 3856 --- [ Thread-36] c.h.h.miaosha.service.RedisServiceImpl : 剩余数量:= 20
2017-09-24 22:59:51.210 INFO 3856 --- [ Thread-34] c.h.h.miaosha.service.RedisServiceImpl : 已买数量: = 200
2017-09-24 22:59:51.211 INFO 3856 --- [ Thread-34] c.h.h.miaosha.service.RedisServiceImpl : 剩余数量:= 0
2017-09-24 22:59:57.234 INFO 3856 --- [ Thread-27] c.h.h.miaosha.service.RedisServiceImpl : 对不起,商品已售完.
2017-09-24 23:00:03.243 INFO 3856 --- [ Thread-28] c.h.h.miaosha.service.RedisServiceImpl : 对不起,商品已售完.
2017-09-24 23:00:09.254 INFO 3856 --- [ Thread-24] c.h.h.miaosha.service.RedisServiceImpl : 对不起,商品已售完.
2017-09-24 23:00:15.266 INFO 3856 --- [ Thread-30] c.h.h.miaosha.service.RedisServiceImpl : 对不起,商品已售完.
2017-09-24 23:00:21.275 INFO 3856 --- [ Thread-33] c.h.h.miaosha.service.RedisServiceImpl : 对不起,商品已售完.
8、进入sentinel客户端 可以查看:
info
当前master地址: 192.168.137.30 : 6379
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=192.168.137.30:6379,slaves=1,sentinels=1
此时kill掉当前的master
查看sentinel日志:
2934:X 24 Sep 23:03:21.659 # +switch-master mymaster 192.168.137.30 6379 192.168.137.31 6379
2934:X 24 Sep 23:03:21.659 * +slave slave 192.168.137.30:6379 192.168.137.30 6379 @ mymaster 192.168.137.31 6379
2934:X 24 Sep 23:03:51.723 # +sdown slave 192.168.137.30:6379 192.168.137.30 6379 @ mymaster 192.168.137.31 6379
可以看到sentinel已经进行了 switch-master 将之前的slave切换成master,而之前的master则转换成了slave
项目控制台输出:
2017-09-24 23:06:27.336 INFO 3856 --- [8.137.32:26379]] redis.clients.jedis.JedisSentinelPool : Created JedisPool to master at 192.168.137.31:6379
这个时候也可以看到master由之前的192.168.137.30:6379 切换成 192.168.137.31:6379
到此,基本完成了,时间不早了,休息了,明天还要上班。
相关文章推荐
- Spring Boot 整合 Redis 实现缓存操作
- SpringBoot之整合redis实现缓存
- spring boot整合redis,实现shiro的CacheManager
- spring boot整合redis实现shiro的分布式session共享的方法
- Spring Boot 整合 Redis 实现缓存操作
- Spring Boot 整合 Redis 实现缓存操作
- SpringBoot整合Spring-data-redis实现集中式缓存
- SpringBoot整合mybatis、shiro、redis实现基于数据库的细粒度动态权限管理系统实例(转)
- Spring整合Redis-sentinel集群实现03
- Spring Boot 整合 Redis 实现缓存操作
- springboot整合redis,实现session共享
- springboot干货——(十三【二】)整合redis实现消息队列
- SpringBoot整合mybatis、shiro、redis实现基于数据库的细粒度动态权限管理系统实例
- SpringBoot整合mybatis、shiro、redis实现基于数据库的细粒度动态权限管理系统实例
- 整合SpringBoot+Mysql+Redis实现缓存机制的一个Demo
- SpringBoot整合mybatis、shiro、redis实现基于数据库的细粒度动态权限管理系统实例
- SpringBoot整合mybatis、shiro、redis实现基于数据库的细粒度动态权限管理系统实例
- SpringBoot整合redis哨兵主从服务
- Spring Boot学习之整合Redis实现缓存