在《 Docker下redis与springboot三部曲之二:安装redis主从和哨兵》一文中,我们在docker下搭建了redis主从和哨兵,本章我们开发一个基于springboot的web工程,体验springboot下如何使用redis高可用服务;
原文地址:http://blog.csdn.net/boling_cavalry/article/details/79041129
实战环境
- 本次实战的环境是Ubuntu16,安装的Docker版本是17.03.2-ce;
- 本次开发web工程的ide推荐使用IntelliJ IDEA;
- 本次实战用到的redis服务,在上一次实战中已经搭建完成,详情请参照在《 Docker下redis与springboot三部曲之二:安装redis主从和哨兵》;
- 哨兵的数量为三个,由于之前已经配置了QUORUM=2,如果哨兵只有一个,在master出了问题重新选举master的时候,会因为投票数不够一半导致选举失败,无法将slave切换为master;
- 容器运行情况如下:
root@rabbitmq:/usr/local/work/blog# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
39369dd41b45 bolingcavalry/redis3sentinel:0.0.1 "sentinel-entrypoi..." About a minute ago Up About a minute 6379/tcp, 26379/tcp blog_sentinel_2
2c319d8cd473 bolingcavalry/redis3sentinel:0.0.1 "sentinel-entrypoi..." About a minute ago Up About a minute 6379/tcp, 26379/tcp blog_sentinel_3
9c2ddac0d1fb redis:3 "docker-entrypoint..." About a minute ago Up About a minute 6379/tcp blog_slave_2
d6fbcf0db3fd bolingcavalry/springbootrun:0.0.1 "/bin/bash" 2 minutes ago Up 2 minutes 0.0.0.0:8080->8080/tcp blog_java_1
5f889da820a1 bolingcavalry/redis3sentinel:0.0.1 "sentinel-entrypoi..." 2 minutes ago Up 2 minutes 6379/tcp, 26379/tcp blog_sentinel_1
b54d6373a65e redis:3 "docker-entrypoint..." 2 minutes ago Up 2 minutes 6379/tcp blog_slave_1
2b7b3b80af56 redis:3 "docker-entrypoint..." 2 minutes ago Up 2 minutes 6379/tcp blog_master_1
73b4044c420d redis:3 "docker-entrypoint..." 2 minutes ago Up 2 minutes 6379/tcp blog_client_1
redis环境
我们先回顾一下docker-compose.yml的内容,看看已经准备好哪些服务了:
代码语言:javascript复制master:
image: redis:3
ports:
- "6379:6379"
slave:
image: redis:3
command: redis-server --slaveof redis-master 6379
links:
- master:redis-master
sentinel:
image: bolingcavalry/redis3sentinel:0.0.1
environment:
- SENTINEL_DOWN_AFTER=5000
- SENTINEL_FAILOVER=5000
links:
- master:redis-master
- slave
ports:
- "26379:26379"
java:
image: bolingcavalry/springbootrun:0.0.1
links:
- sentinel:redis-sentinel
- master:redis-master
volumes:
- /usr/local/work/share:/usr/Downloads
ports:
- "8080:8080"
tty:true
client:
image: redis:3
上面的java服务有以下几点请注意:
- 这个服务已经配置了两个link参数,由此在这个容器中redis-sentinel就代表了哨兵的地址,redis-master就代表了master的地址(/etc/hosts的配置效果);
- 使用的镜像是bolingcavalry/springbootrun:0.0.1,这是我做的镜像,功能非常简单:装了JDK,暴露了8080端口,详情请看《Docker下运行springboot》;
- 容器的/usr/local/work/share与当前电脑的/usr/Downloads目录已建立映射关系,把文件放到/usr/Downloads目录,就相当于把文件放入了容器的/usr/local/work/share目录;
- 容器的8080端口与当前电脑的8080端口绑定;
- 使用tty参数分配终端,否则容器启动后会立即退出;
web工程源码
web工程源码我已上传到github,地址是:git@github.com:zq2599/blog_demos.git,或者用浏览器访问:https://github.com/zq2599/blog_demos,这里面有多个工程,本次实战的工程是redissentineldemo,如下图红框所示:
pom.xml
本次实战的web工程是基于maven构建的,pom.xml的内容与通常的spring boot工程一样,只有依赖库新增以下内容:
代码语言:javascript复制<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
如上所示,增加了spring-boot-starter-data-redis;
application.properties(连接master)
配置文件application.properties的内容如下:
代码语言:javascript复制spring.redis.database=0
spring.redis.host=redis-master
spring.redis.port=6379
spring.redis.pool.max-active=2500
spring.redis.pool.max-wait=6000
spring.redis.pool.max-idle=500
spring.redis.pool.min-idle=100
spring.redis.pool.testOnBorrow=true
spring.redis.pool.blockWhenExhausted=true
spring.redis.pool.numTestsPerEvictionRun=3
spring.redis.pool.timeBetweenEvictionRunsMillis=-1
spring.redis.timeout=100
spring.redis.host和spring.redis.port表明这个配置文件是直接连接master库的;
源码
在springboot下操作redis的步骤很简单,这里做了一个controller来验证写redis,代码如下:
代码语言:javascript复制@RestController
public class MockController {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@RequestMapping(value = "/testredis/{key}/{value}", method = RequestMethod.GET)
@ResponseBody
public String testRedis(@PathVariable("key") final String key, @PathVariable("value") final String value) {
try{
stringRedisTemplate.opsForValue().set(key, value);
}catch(Exception e){
e.printStackTrace();
}
return "1. success";
}
}
如上所示,spring环境会根据配置信息来初始化StringRedisTemplate对象,我们只要通过@Autowire注解配置就能直接使用了;
验证
- 将上述工程打包成jar,放入blog_java_1容器(之前提到过文件夹映射的),再执行命令java -jar redissentineldemo-0.0.1-SNAPSHOT.jar启动服务;
- 我当前电脑的IP是192.168.119.155,所以在浏览器访问:http://192.168.119.155:8080/testredis/name/tom111112333666777,浏览器会显示"“1. success”,此时controller会在redis中写入key为"name",value为"tom111112333666777"的一条记录;
- 执行命令docker exec -it blog_slave_1 /bin/bash进入slave的容器,查看"name"对应的值,如下所示:
root@362d09f7fee9:/data# redis-cli
127.0.0.1:6379> get name
"tom111112333666777"
127.0.0.1:6379>
可见从库已经同步到了主库写入的数据;
web工程连接到哨兵
前面的实战我们是直接连接到redis的master,并未体验到哨兵带来的高可用能力,现在我们来试试连接到哨兵,这样当master有问题时,如果slave成了新的master,应用程序不需要任何改动,通过哨兵就能自动连接到新的master;
application.properties(连接哨兵)
从连接到master改为连接到哨兵的改动很简单,只需要修改配置文件即可,修改后的application.properties的内容如下:
代码语言:javascript复制spring.redis.database=0
#spring.redis.host=redis-master
#spring.redis.port=6379
spring.redis.pool.max-active=2500
spring.redis.pool.max-wait=6000
spring.redis.pool.max-idle=500
spring.redis.pool.min-idle=100
spring.redis.pool.testOnBorrow=true
spring.redis.pool.blockWhenExhausted=true
spring.redis.pool.numTestsPerEvictionRun=3
spring.redis.pool.timeBetweenEvictionRunsMillis=-1
spring.redis.sentinel.master=mymaster
spring.redis.sentinel.nodes=redis-sentinel:26379
spring.redis.timeout=1000
可见spring.redis.host和spring.redis.port这两个配置被注释掉,然后新增了spring.redis.sentinel.master和spring.redis.sentinel.nodes,“redis-sentinel”是link参数,可以连接到哨兵的机器;
以上就是所有改动,重新打包复制到blog_java_1容器,启动后在浏览器发起一次请求,然后去redis服务器上检查,发现数据成功更新;
验证高可用
- 执行命令令docker stop blog_master_1停止master容器,再docker logs -f blog_sentinel_1命令查看哨兵日志,可以发现slave已经切换成了master,如下:
root@rabbitmq:~# docker stop blog_master_1
blog_master_1
root@rabbitmq:/usr/local/work/blog# docker logs -f blog_sentinel_1
_._
_.-``__ ''-._
_.-`` `. `_. ''-._ Redis 3.2.11 (00000000/0) 64 bit
.-`` .-```. ```/ _.,_ ''-._
( ' , .-` | `, ) Running in sentinel mode
|`-._`-...-` __...-.``-._|'` _.-'| Port: 26379
| `-._ `._ / _.-' | PID: 1
`-._ `-._ `-./ _.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' | http://redis.io
`-._ `-._`-.__.-'_.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' |
`-._ `-._`-.__.-'_.-' _.-'
`-._ `-.__.-' _.-'
`-._ _.-'
`-.__.-'
1:X 12 Jan 12:31:46.811 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
1:X 12 Jan 12:31:46.812 # Sentinel ID is 43a5934ce2c884b268e40cfbb3d3e14bb3d7bac3
1:X 12 Jan 12:31:46.812 # monitor master mymaster 172.17.0.3 6379 quorum 2
1:X 12 Jan 12:31:46.812 * slave slave 172.17.0.4:6379 172.17.0.4 6379 @ mymaster 172.17.0.3 6379
1:X 12 Jan 12:31:56.923 * fix-slave-config slave 172.17.0.4:6379 172.17.0.4 6379 @ mymaster 172.17.0.3 6379
1:X 12 Jan 12:32:13.463 * sentinel sentinel 6f85d8262413edc32ce2a996cbbaf6d8bfc27563 172.17.0.8 26379 @ mymaster 172.17.0.3 6379
1:X 12 Jan 12:32:13.469 * sentinel sentinel ba786641ce23a03f0d6cff8885d6315edf025d43 172.17.0.7 26379 @ mymaster 172.17.0.3 6379
1:X 12 Jan 12:32:16.886 * slave slave 172.17.0.9:6379 172.17.0.9 6379 @ mymaster 172.17.0.3 6379
1:X 12 Jan 12:32:26.997 * fix-slave-config slave 172.17.0.9:6379 172.17.0.9 6379 @ mymaster 172.17.0.3 6379
1:X 12 Jan 12:35:28.735 # sdown master mymaster 172.17.0.3 6379
1:X 12 Jan 12:35:28.805 # new-epoch 1
1:X 12 Jan 12:35:28.805 # vote-for-leader 6f85d8262413edc32ce2a996cbbaf6d8bfc27563 1
1:X 12 Jan 12:35:29.812 # odown master mymaster 172.17.0.3 6379 #quorum 3/2
1:X 12 Jan 12:35:29.812 # Next failover delay: I will not start a failover before Fri Jan 12 12:35:39 2018
1:X 12 Jan 12:35:30.004 # config-update-from sentinel 6f85d8262413edc32ce2a996cbbaf6d8bfc27563 172.17.0.8 26379 @ mymaster 172.17.0.3 6379
1:X 12 Jan 12:35:30.004 # switch-master mymaster 172.17.0.3 6379 172.17.0.4 6379
1:X 12 Jan 12:35:30.004 * slave slave 172.17.0.9:6379 172.17.0.9 6379 @ mymaster 172.17.0.4 6379
1:X 12 Jan 12:35:30.004 * slave slave 172.17.0.3:6379 172.17.0.3 6379 @ mymaster 172.17.0.4 6379
1:X 12 Jan 12:35:35.025 # sdown slave 172.17.0.3:6379 172.17.0.3 6379 @ mymaster 172.17.0.4 6379
如上日志所示,“ new-epoch 1”表示发起了新的选举,“ odown master mymaster 172.17.0.3 6379 #quorum 3/2”表示投票超过了一半,选举成功,slave被选为master;
这里要注意的是,quorum参数在conf中被我们配置成了2,所以这里要有三个哨兵才能投票通过,如果您没有执行命令docker-compose scale sentinel=3将哨兵数量扩展为3,slave是不会切换成master的!
- 像前面的验证方式一样,在浏览器发起一次请求(http://192.168.119.155:8080/testredis/name/aaabbbccc),然后去slave服务器上执行redis-cli进入控制台检查key为"name"的值,发现数据成功更新为"aaabbbccc";
至此,《Docker下redis与springboot三部曲》系列实战就全部完成了,希望能帮助你熟悉和了解redis的一些特性,以及基本的java调用redis服务的操作。