如果我问你,springboot集成redis,启动项目的过程中是否对服务器集群中的各个节点做连接请求,你会怎样回答。这么细致的问题需要我们仔细研究下整个过程是什么样子的再来回答。如果我说没有连接,但是在配置健康检查之后做了连接,不知道这是你心中的答案否。
今天我们就来一起探索springboot集成redis的具体过程,并查看在没有配置健康检查的前提下客户端服务是否有主动连接redis服务器的情况。同时再加上sprignboot的健康检查,回答此时redis连接信息能用netstat命令看见。
本文包括以下内容
1、springboot-redis启动的具体步骤
2、springboot-redis在健康检查开启时候的连接情况。
springboot集成redis启动的引导类是RedisAutoConfiguration
1.1、RedisAutoConfiguration加载的前提是RedisOperations.class类存在在路径下。也就是说项目必须引入spring-data-redis这个jar包
1.2、RedisAutoConfiguration要加载redis的配置类RedisProperties。具体加载过程如下:
springboot通过JavaBeanBinder#bind绑定以ConfigurationPropertiesde配置的prefix=spring.redis的值。具体来说:
①、从参数里面获取key对应的值。org.springframework.boot.context.properties.bind.Binder#findProperty。
②、将获取到的value注入到对象当中。使用的是
property.setValue(beanSupplier, bound);
1.3、根据配置加载LettuceConnectionConfiguration或者JedisConnectionConfiguration。在我使用的spring-boot-autoconfigure2.1.6.RELEASE版本当中,LettuceConnectionConfiguration已经成为默认配置。本项目使用它来做redis连接池。
LettuceConnectionConfiguration实例化重点是要实例化线程池,并将我们配置的参数设置到线程池实例当中。
同时会实例化LettuceConnectionFactory连接工厂。
1.4、实例化RedisTemplate
①、设置序列化工具,包括key的序列化工具hashKeySerializer和value序列化工具valueSerializer
②、设置1.3的连接工厂RedisConnectionFactory到RedisTemplate实例当中。
1.5、实例化StringRedisTemplate
①、设置序列化工具,包括key的序列化工具hashKeySerializer和value序列化工具valueSerializer。另外,还设置了hashKeySerializer、hashValueSerializer序列化工具。
②、设置1.3的连接工厂RedisConnectionFactory到StringRedisTemplate实例当中。
至此springboot 启动redis就完毕了,我们可以看到启动过程并没有启动任何redis线程,所以redis线程启动属于懒加载的情况。
然而,在我们的springboot项目启动完毕项目之后,用netstate查看6379接口,确实能够看到redis连接远程服务的线程信息。这又是为什么呢?
原来springboot启动redis虽然没有特意启动redis线程,但是springboot的健康检查却做了这个事情,springboot在启动的时候会再加载配置类:RedisHealthIndicatorAutoConfiguration
redis通过以上配置类会注册RedisHealthIndicator实例到spring上下文当中。
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
// ①创建各个主从节点的链接
RedisConnection connection = RedisConnectionUtils.getConnection(this.redisConnectionFactory);
try {
if (connection instanceof RedisClusterConnection) {
// ②任意选一个节点发送请求获取集群信息
ClusterInfo clusterInfo = ((RedisClusterConnection) connection).clusterGetClusterInfo();
//包装结果
builder.up().withDetail("cluster_size", clusterInfo.getClusterSize())
.withDetail("slots_up", clusterInfo.getSlotsOk())
.withDetail("slots_fail", clusterInfo.getSlotsFail());
}
else {
Properties info = connection.info();
builder.up().withDetail(VERSION, info.getProperty(REDIS_VERSION));
}
}
finally {
// ③Lettuce连接并没有释放真正的连接
RedisConnectionUtils.releaseConnection(connection, this.redisConnectionFactory);
}
}
首先健康检查会连接各个redis节点,重点看以下两个地方
第一个就是RedisConnectionUtils.getConnection,它是创建连接的入口。
RedisConnection connection = RedisConnectionUtils.getConnection(this.redisConnectionFactory);
第二个是refresh.loadViews,这个方法最终会连接各个节点。
Map partitions = refresh.loadViews(topologyRefreshSource, useDynamicRefreshSources());
这里有必要交代下这个线程个数的变化
位置①执行可以按照两阶段理解,第一个阶段是创建了所有数据节点的连接,并很快释放掉这些节点连接。
第二个阶段是新创建了两个连接,第一个连接为连通集群前置结点,第二个连接连通了某一个数据节点。
位置②执行完毕新增了两个线程。
位置③,在luttce线程池的情况下并没有实际关掉任何线程。
luttce的pingBeforeActivateConnection
其实,luttce有一个启动就ping远程连接检查连通性的参数,它就是pingBeforeActivateConnection,但是springboot集成lettuce的时候并没有暴露这个参数,所以这个参数默认就是false,无法更改。
mac查某端口的网络信息:netstat –tunlp|grep "6379"
linux查某进程的网络信息:netstat -nap|grep "4026195"|grep ESTABLISHED |awk '{print $5}'|sort -n -r |uniq
springboot启动redis的整个过程中并没有连接远程redis节点。知道redis的健康检查代码被执行之后服务会主动向所有数据节点发送连接请求。并对外挑选某一个节点请求clusterInfo并包装到健康检查显示数据当中。