记录服务器上,不定时出现io.lettuce.core.RedisCommandTimeoutException: Command timed out after 12 millisecond(s)
-
日志
-
org.springframework.data.redis.RedisConnectionFailureException: Unable to connect to Redis; nested exception is org.springframework.data.redis.connection.PoolException: Could not get a resource from the pool; nested exception is io.lettuce.core.RedisConnectionException: Unable to connect to 192.168.10.143:6350 at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$ExceptionTranslatingConnectionProvider.translateException(LettuceConnectionFactory.java:1534) ~[spring-data-redis-2.3.5.RELEASE.jar:2.3.5.RELEASE] at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$ExceptionTranslatingConnectionProvider.getConnection(LettuceConnectionFactory.java:1442) ~[spring-data-redis-2.3.5.RELEASE.jar:2.3.5.RELEASE] at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$SharedConnection.getNativeConnection(LettuceConnectionFactory.java:1228) ~[spring-data-redis-2.3.5.RELEASE.jar:2.3.5.RELEASE] at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$SharedConnection.getConnection(LettuceConnectionFactory.java:1211) ~[spring-data-redis-2.3.5.RELEASE.jar:2.3.5.RELEASE] at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory.getSharedReactiveConnection(LettuceConnectionFactory.java:985) ~[spring-data-redis-2.3.5.RELEASE.jar:2.3.5.RELEASE] at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory.getReactiveConnection(LettuceConnectionFactory.java:446) ~[spring-data-redis-2.3.5.RELEASE.jar:2.3.5.RELEASE] at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory.getReactiveConnection(LettuceConnectionFactory.java:99) ~[spring-data-redis-2.3.5.RELEASE.jar:2.3.5.RELEASE] at reactor.core.publisher.MonoSupplier.call(MonoSupplier.java:85) ~[reactor-core-3.3.11.RELEASE.jar:3.3.11.RELEASE] at reactor.core.publisher.FluxSubscribeOnCallable$CallableSubscribeOnSubscription.run(FluxSubscribeOnCallable.java:225) ~[reactor-core-3.3.11.RELEASE.jar:3.3.11.RELEASE] at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:68) ~[reactor-core-3.3.11.RELEASE.jar:3.3.11.RELEASE] at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:28) ~[reactor-core-3.3.11.RELEASE.jar:3.3.11.RELEASE] at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266) ~[na:1.8.0_251] at java.util.concurrent.FutureTask.run(FutureTask.java) ~[na:1.8.0_251] at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) ~[na:1.8.0_251] at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) ~[na:1.8.0_251] at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) ~[na:1.8.0_251] at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) ~[na:1.8.0_251] at java.lang.Thread.run(Thread.java:748) ~[na:1.8.0_251] Caused by: org.springframework.data.redis.connection.PoolException: Could not get a resource from the pool; nested exception is io.lettuce.core.RedisConnectionException: Unable to connect to 192.168.10.143:6350 at org.springframework.data.redis.connection.lettuce.LettucePoolingConnectionProvider.getConnection(LettucePoolingConnectionProvider.java:109) ~[spring-data-redis-2.3.5.RELEASE.jar:2.3.5.RELEASE] at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$ExceptionTranslatingConnectionProvider.getConnection(LettuceConnectionFactory.java:1440) ~[spring-data-redis-2.3.5.RELEASE.jar:2.3.5.RELEASE] ... 16 common frames omitted Caused by: io.lettuce.core.RedisConnectionException: Unable to connect to 192.168.10.143:6350 at io.lettuce.core.RedisConnectionException.create(RedisConnectionException.java:78) ~[lettuce-core-5.3.5.RELEASE.jar:5.3.5.RELEASE] at io.lettuce.core.RedisConnectionException.create(RedisConnectionException.java:56) ~[lettuce-core-5.3.5.RELEASE.jar:5.3.5.RELEASE] at io.lettuce.core.AbstractRedisClient.getConnection(AbstractRedisClient.java:242) ~[lettuce-core-5.3.5.RELEASE.jar:5.3.5.RELEASE] at io.lettuce.core.RedisClient.connect(RedisClient.java:206) ~[lettuce-core-5.3.5.RELEASE.jar:5.3.5.RELEASE] at org.springframework.data.redis.connection.lettuce.StandaloneConnectionProvider.lambda$getConnection$1(StandaloneConnectionProvider.java:115) ~[spring-data-redis-2.3.5.RELEASE.jar:2.3.5.RELEASE] at java.util.Optional.orElseGet(Optional.java:267) ~[na:1.8.0_251] at org.springframework.data.redis.connection.lettuce.StandaloneConnectionProvider.getConnection(StandaloneConnectionProvider.java:115) ~[spring-data-redis-2.3.5.RELEASE.jar:2.3.5.RELEASE] at org.springframework.data.redis.connection.lettuce.LettucePoolingConnectionProvider.lambda$null$0(LettucePoolingConnectionProvider.java:97) ~[spring-data-redis-2.3.5.RELEASE.jar:2.3.5.RELEASE] at io.lettuce.core.support.ConnectionPoolSupport$RedisPooledObjectFactory.create(ConnectionPoolSupport.java:211) ~[lettuce-core-5.3.5.RELEASE.jar:5.3.5.RELEASE] at io.lettuce.core.support.ConnectionPoolSupport$RedisPooledObjectFactory.create(ConnectionPoolSupport.java:201) ~[lettuce-core-5.3.5.RELEASE.jar:5.3.5.RELEASE] at org.apache.commons.pool2.BasePooledObjectFactory.makeObject(BasePooledObjectFactory.java:58) ~[commons-pool2-2.8.1.jar:2.8.1] at org.apache.commons.pool2.impl.GenericObjectPool.create(GenericObjectPool.java:899) ~[commons-pool2-2.8.1.jar:2.8.1] at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:429) ~[commons-pool2-2.8.1.jar:2.8.1] at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:354) ~[commons-pool2-2.8.1.jar:2.8.1] at io.lettuce.core.support.ConnectionPoolSupport$1.borrowObject(ConnectionPoolSupport.java:122) ~[lettuce-core-5.3.5.RELEASE.jar:5.3.5.RELEASE] at io.lettuce.core.support.ConnectionPoolSupport$1.borrowObject(ConnectionPoolSupport.java:117) ~[lettuce-core-5.3.5.RELEASE.jar:5.3.5.RELEASE] at org.springframework.data.redis.connection.lettuce.LettucePoolingConnectionProvider.getConnection(LettucePoolingConnectionProvider.java:103) ~[spring-data-redis-2.3.5.RELEASE.jar:2.3.5.RELEASE] ... 17 common frames omitted Caused by: io.lettuce.core.RedisCommandTimeoutException: Command timed out after 12 millisecond(s) at io.lettuce.core.ExceptionFactory.createTimeoutException(ExceptionFactory.java:51) ~[lettuce-core-5.3.5.RELEASE.jar:5.3.5.RELEASE] at io.lettuce.core.protocol.CommandExpiryWriter.lambda$potentiallyExpire$0(CommandExpiryWriter.java:172) ~[lettuce-core-5.3.5.RELEASE.jar:5.3.5.RELEASE] at io.netty.util.concurrent.PromiseTask.runTask(PromiseTask.java:98) ~[netty-common-4.1.53.Final.jar:4.1.53.Final] at io.netty.util.concurrent.ScheduledFutureTask.run(ScheduledFutureTask.java:170) ~[netty-common-4.1.53.Final.jar:4.1.53.Final] at io.netty.util.concurrent.DefaultEventExecutor.run(DefaultEventExecutor.java:66) ~[netty-common-4.1.53.Final.jar:4.1.53.Final] at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989) ~[netty-common-4.1.53.Final.jar:4.1.53.Final] at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.53.Final.jar:4.1.53.Final] at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.53.Final.jar:4.1.53.Final] ... 1 common frames omitted
-
原因分析
-
由于lettuce没有心跳检测,直接能判断为查询redis数据超时了。
-
网上百度了一波,发现有结论说是替换lettuce 为 jedis 就能解决问题**【此方案无效】**
-
修改pom文件
-
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <exclusions> <exclusion> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </exclusion> <exclusion> <groupId>io.lettuce</groupId> <artifactId>lettuce-core</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency>
-
-
-
替换后,使用jedis链接池运行一段时间,又报出了JedisConnectionException: java.net.SocketTimeoutException: Read timed out异常,接下去继续分析
记录服务器上,不定时出现JedisConnectionException: java.net.SocketTimeoutException: Read timed out的问题
错误日志
-
org.springframework.data.redis.RedisConnectionFailureException: Cannot get Jedis connection; nested exception is redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.fetchJedisConnector(JedisConnectionFactory.java:282) at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.getConnection(JedisConnectionFactory.java:476) at org.springframework.data.redis.core.RedisConnectionUtils.doGetConnection(RedisConnectionUtils.java:134) at org.springframework.data.redis.core.RedisConnectionUtils.getConnection(RedisConnectionUtils.java:97) at org.springframework.data.redis.core.RedisConnectionUtils.getConnection(RedisConnectionUtils.java:84) at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:215) at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:188) at org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:96) at org.springframework.data.redis.core.DefaultSetOperations.size(DefaultSetOperations.java:330) at com.rivtower.rivus.product.service.impl.ProductDefServiceImpl.handlePublishingQty(ProductDefServiceImpl.java:279) at com.rivtower.rivus.product.service.impl.ProductDefServiceImpl$$FastClassBySpringCGLIB$$e459cf51.invoke(<generated>) at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:687) at com.rivtower.rivus.product.service.impl.ProductDefServiceImpl$$EnhancerBySpringCGLIB$$85b0914f.handlePublishingQty(<generated>) at com.rivtower.rivus.product.controller.ProductDefController.queryProductDefByRegisterId(ProductDefController.java:85) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:878) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:792) at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) at javax.servlet.http.HttpServlet.service(HttpServlet.java:626) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) at javax.servlet.http.HttpServlet.service(HttpServlet.java:733) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:320) at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:126) at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:90) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:118) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:158) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter.doFilterInternal(BearerTokenAuthenticationFilter.java:130) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:92) at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:77) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215) at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178) at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:358) at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:93) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) at org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:747) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374) at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run(Thread.java:748) Caused by: redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out at redis.clients.jedis.util.RedisInputStream.ensureFill(RedisInputStream.java:205) at redis.clients.jedis.util.RedisInputStream.readByte(RedisInputStream.java:43) at redis.clients.jedis.Protocol.process(Protocol.java:155) at redis.clients.jedis.Protocol.read(Protocol.java:220) at redis.clients.jedis.Connection.readProtocolWithCheckingBroken(Connection.java:278) at redis.clients.jedis.Connection.getStatusCodeReply(Connection.java:196) at redis.clients.jedis.BinaryClient.connect(BinaryClient.java:119) at redis.clients.jedis.BinaryJedis.connect(BinaryJedis.java:1904) at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.fetchJedisConnector(JedisConnectionFactory.java:277) ... 103 common frames omitted Caused by: java.net.SocketTimeoutException: Read timed out at java.net.SocketInputStream.socketRead0(Native Method) at java.net.SocketInputStream.socketRead(SocketInputStream.java:116) at java.net.SocketInputStream.read(SocketInputStream.java:171) at java.net.SocketInputStream.read(SocketInputStream.java:141) at java.net.SocketInputStream.read(SocketInputStream.java:127) at redis.clients.jedis.util.RedisInputStream.ensureFill(RedisInputStream.java:199) ... 111 common frames omitted
问题分析
- 前面将redis的连接池从lettuce换成了jedis,但是依然出现了redis超时异常,由此可见应该从配置问题找起。不过首先先定位下报错位置,从错误日志最底部的方法栈排查java.net.SocketInputStream.socketRead0为本地方法,可以从java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
问题排查
-
由于排查SocketInputStream这个IO类没有识别性,最终定位错误日志最终在redis.clients.jedis.util.RedisInputStream.ensureFill(RedisInputStream.java:199),
-
Caused by: java.net.SocketTimeoutException: Read timed out at java.net.SocketInputStream.socketRead0(Native Method) at java.net.SocketInputStream.socketRead(SocketInputStream.java:116) at java.net.SocketInputStream.read(SocketInputStream.java:171) at java.net.SocketInputStream.read(SocketInputStream.java:141) at java.net.SocketInputStream.read(SocketInputStream.java:127) at redis.clients.jedis.util.RedisInputStream.ensureFill(RedisInputStream.java:199) ... 111 common frames omitted
-
-
定位代码报错位置
-
下断点后排查,发现超时时间是SocketInputStream的一个属性,那么就找下这个属性的初始化赋值。
反向推理
-
方向查询SocketInputStream的初始化创建过程
-
SocketInputStream <- RedisInputStream(InputStream in, int size) <- redis.clients.jedis.Connection.connect() <- new RedisInputStream(socket.getInputStream()) <- jedisSocketFactory.createSocket() <- DefaultJedisSocketFactory.createSocket()
-
-
查看createSocket()源码,发现超时时间是 通过getSoTimeout()获取,getSoTimeout()方法获取的是DefaultJedisSocketFactory的属性值soTimeout,那么接下去追溯DefaultJedisSocketFactory创建过程
-
@Override public Socket createSocket() throws IOException { Socket socket = null; try { socket = new Socket(); // ->@wjw_add socket.setReuseAddress(true); socket.setKeepAlive(true); // Will monitor the TCP connection is // valid socket.setTcpNoDelay(true); // Socket buffer Whetherclosed, to // ensure timely delivery of data socket.setSoLinger(true, 0); // Control calls close () method, // the underlying socket is closed // immediately // <-@wjw_add socket.connect(new InetSocketAddress(getHost(), getPort()), getConnectionTimeout()); //超时时间 socket.setSoTimeout(getSoTimeout()); if (ssl) { if (null == sslSocketFactory) { sslSocketFactory = (SSLSocketFactory) SSLSocketFactory.getDefault(); } socket = sslSocketFactory.createSocket(socket, getHost(), getPort(), true); if (null != sslParameters) { ((SSLSocket) socket).setSSLParameters(sslParameters); } if ((null != hostnameVerifier) && (!hostnameVerifier.verify(getHost(), ((SSLSocket) socket).getSession()))) { String message = String.format( "The connection to '%s' failed ssl/tls hostname verification.", getHost()); throw new JedisConnectionException(message); } } return socket; } catch (Exception ex) { if (socket != null) { socket.close(); } throw ex; } }
-
-
DefaultJedisSocketFactory创建过程
-
DefaultJedisSocketFactory(String host, int port, int connectionTimeout, int soTimeout, boolean ssl, SSLSocketFactory sslSocketFactory, SSLParameters sslParameters, HostnameVerifier hostnameVerifier) <- setSoTimeout() <- BinaryJedis(final String host, final int port, final int connectionTimeout, final int soTimeout) <- Jedis(final String host, final int port, final int connectionTimeout, final int soTimeout) <- JedisConnectionFactory.getActiveSentinel()
-
-
在JedisConnectionFactory.getActiveSentinel方法中找到如下逻辑
-
public class JedisConnectionFactory implements InitializingBean, DisposableBean, RedisConnectionFactory { private final JedisClientConfiguration clientConfiguration; private Jedis getActiveSentinel() { Assert.isTrue(RedisConfiguration.isSentinelConfiguration(configuration), "SentinelConfig must not be null!"); for (RedisNode node : ((SentinelConfiguration) configuration).getSentinels()) { //创建Jedis Jedis jedis = new Jedis(node.getHost(), node.getPort(), getConnectTimeout(), getReadTimeout()); try { if (jedis.ping().equalsIgnoreCase("pong")) { potentiallySetClientName(jedis); return jedis; } } catch (Exception ex) { log.warn(String.format("Ping failed for sentinel host:%s", node.getHost()), ex); } } throw new InvalidDataAccessResourceUsageException("No Sentinel found"); } private int getReadTimeout() { return Math.toIntExact(clientConfiguration.getReadTimeout().toMillis()); } }
-
可以看到,readTimeOut最终是获取了JedisClientConfiguration clientConfiguration。接下去还是一样,查询JedisClientConfiguration的初始化赋值过程
-
-
下断点排查是通过public JedisConnectionFactory(RedisStandaloneConfiguration standaloneConfig, JedisClientConfiguration clientConfig) 构造方法初始化的 JedisConnectionFactory。而在此构造方法中传入了 JedisClientConfiguration clientConfig,这个类的具体实现类是 DefaultJedisClientConfiguration,这里可以发现已经是排查到spring-data-redis模块了,接下去可以提前设想下,这个Configuration类 读取的配置 是读取 yml文件或者config文件的配置。
-
-
package org.springframework.data.redis.connection.jedis; class DefaultJedisClientConfiguration implements JedisClientConfiguration { DefaultJedisClientConfiguration(boolean useSsl, @Nullable SSLSocketFactory sslSocketFactory, @Nullable SSLParameters sslParameters, @Nullable HostnameVerifier hostnameVerifier, boolean usePooling, @Nullable GenericObjectPoolConfig poolConfig, @Nullable String clientName, Duration readTimeout, Duration connectTimeout) { this.useSsl = useSsl; this.sslSocketFactory = Optional.ofNullable(sslSocketFactory); this.sslParameters = Optional.ofNullable(sslParameters); this.hostnameVerifier = Optional.ofNullable(hostnameVerifier); this.usePooling = usePooling; this.poolConfig = Optional.ofNullable(poolConfig); this.clientName = Optional.ofNullable(clientName); this.readTimeout = readTimeout; this.connectTimeout = connectTimeout; } }
-
-
继续追溯org.springframework.data.redis.connection.jedis.DefaultJedisClientConfiguration 初始化过程
-
public interface JedisClientConfiguration { private Duration readTimeout = Duration.ofMillis(Protocol.DEFAULT_TIMEOUT); @Override public JedisClientConfigurationBuilder readTimeout(Duration readTimeout) { Assert.notNull(readTimeout, "Duration must not be null!"); this.readTimeout = readTimeout; return this; } @Override public JedisClientConfiguration build() { return new DefaultJedisClientConfiguration(useSsl, sslSocketFactory, sslParameters, hostnameVerifier, usePooling, poolConfig, clientName, readTimeout, connectTimeout); } }
-
-
最终找到了 spring-data-redis-starter的自动装配的类
-
package org.springframework.boot.autoconfigure.data.redis; import java.net.UnknownHostException; import java.time.Duration; import org.apache.commons.pool2.impl.GenericObjectPool; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPoolConfig; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisClusterConfiguration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisSentinelConfiguration; import org.springframework.data.redis.connection.jedis.JedisClientConfiguration; import org.springframework.data.redis.connection.jedis.JedisClientConfiguration.JedisClientConfigurationBuilder; import org.springframework.data.redis.connection.jedis.JedisConnection; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.util.StringUtils; /** * Redis connection configuration using Jedis. * * @author Mark Paluch * @author Stephane Nicoll */ @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ GenericObjectPool.class, JedisConnection.class, Jedis.class }) class JedisConnectionConfiguration extends RedisConnectionConfiguration { JedisConnectionConfiguration(RedisProperties properties, ObjectProvider<RedisSentinelConfiguration> sentinelConfiguration, ObjectProvider<RedisClusterConfiguration> clusterConfiguration) { super(properties, sentinelConfiguration, clusterConfiguration); } @Bean @ConditionalOnMissingBean(RedisConnectionFactory.class) JedisConnectionFactory redisConnectionFactory( ObjectProvider<JedisClientConfigurationBuilderCustomizer> builderCustomizers) throws UnknownHostException { return createJedisConnectionFactory(builderCustomizers); } private JedisConnectionFactory createJedisConnectionFactory( ObjectProvider<JedisClientConfigurationBuilderCustomizer> builderCustomizers) { JedisClientConfiguration clientConfiguration = getJedisClientConfiguration(builderCustomizers); if (getSentinelConfig() != null) { return new JedisConnectionFactory(getSentinelConfig(), clientConfiguration); } if (getClusterConfiguration() != null) { return new JedisConnectionFactory(getClusterConfiguration(), clientConfiguration); } return new JedisConnectionFactory(getStandaloneConfig(), clientConfiguration); } private JedisClientConfiguration getJedisClientConfiguration( ObjectProvider<JedisClientConfigurationBuilderCustomizer> builderCustomizers) { JedisClientConfigurationBuilder builder = applyProperties(JedisClientConfiguration.builder()); RedisProperties.Pool pool = getProperties().getJedis().getPool(); if (pool != null) { applyPooling(pool, builder); } if (StringUtils.hasText(getProperties().getUrl())) { customizeConfigurationFromUrl(builder); } builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); return builder.build(); } private JedisClientConfigurationBuilder applyProperties(JedisClientConfigurationBuilder builder) { if (getProperties().isSsl()) { builder.useSsl(); } if (getProperties().getTimeout() != null) { Duration timeout = getProperties().getTimeout(); builder.readTimeout(timeout).connectTimeout(timeout); } if (StringUtils.hasText(getProperties().getClientName())) { builder.clientName(getProperties().getClientName()); } return builder; } private void applyPooling(RedisProperties.Pool pool, JedisClientConfiguration.JedisClientConfigurationBuilder builder) { builder.usePooling().poolConfig(jedisPoolConfig(pool)); } private JedisPoolConfig jedisPoolConfig(RedisProperties.Pool pool) { JedisPoolConfig config = new JedisPoolConfig(); config.setMaxTotal(pool.getMaxActive()); config.setMaxIdle(pool.getMaxIdle()); config.setMinIdle(pool.getMinIdle()); if (pool.getTimeBetweenEvictionRuns() != null) { config.setTimeBetweenEvictionRunsMillis(pool.getTimeBetweenEvictionRuns().toMillis()); } if (pool.getMaxWait() != null) { config.setMaxWaitMillis(pool.getMaxWait().toMillis()); } return config; } private void customizeConfigurationFromUrl(JedisClientConfiguration.JedisClientConfigurationBuilder builder) { ConnectionInfo connectionInfo = parseUrl(getProperties().getUrl()); if (connectionInfo.isUseSsl()) { builder.useSsl(); } } }
-
@ConfigurationProperties(prefix = "spring.redis") public class RedisProperties { /** * Database index used by the connection factory. */ private int database = 0; /** * Connection URL. Overrides host, port, and password. User is ignored. Example: * redis://user:password@example.com:6379 */ private String url; /** * Redis server host. */ private String host = "localhost"; /** * Login password of the redis server. */ private String password; /** * Redis server port. */ private int port = 6379; /** * Whether to enable SSL support. */ private boolean ssl; /** * Connection timeout. */ private Duration timeout; /** * Client name to be set on connections with CLIENT SETNAME. */ private String clientName; private Sentinel sentinel; private Cluster cluster; private final Jedis jedis = new Jedis(); private final Lettuce lettuce = new Lettuce(); public int getDatabase() { return this.database; } public void setDatabase(int database) { this.database = database; } public String getUrl() { return this.url; } public void setUrl(String url) { this.url = url; } public String getHost() { return this.host; } public void setHost(String host) { this.host = host; } public String getPassword() { return this.password; } public void setPassword(String password) { this.password = password; } public int getPort() { return this.port; } public void setPort(int port) { this.port = port; } public boolean isSsl() { return this.ssl; } public void setSsl(boolean ssl) { this.ssl = ssl; } public void setTimeout(Duration timeout) { this.timeout = timeout; } public Duration getTimeout() { return this.timeout; } public String getClientName() { return this.clientName; } public void setClientName(String clientName) { this.clientName = clientName; } public Sentinel getSentinel() { return this.sentinel; } public void setSentinel(Sentinel sentinel) { this.sentinel = sentinel; } public Cluster getCluster() { return this.cluster; } public void setCluster(Cluster cluster) { this.cluster = cluster; } public Jedis getJedis() { return this.jedis; } public Lettuce getLettuce() { return this.lettuce; } /** * Pool properties. */ public static class Pool { /** * Maximum number of "idle" connections in the pool. Use a negative value to * indicate an unlimited number of idle connections. */ private int maxIdle = 8; /** * Target for the minimum number of idle connections to maintain in the pool. This * setting only has an effect if both it and time between eviction runs are * positive. */ private int minIdle = 0; /** * Maximum number of connections that can be allocated by the pool at a given * time. Use a negative value for no limit. */ private int maxActive = 8; /** * Maximum amount of time a connection allocation should block before throwing an * exception when the pool is exhausted. Use a negative value to block * indefinitely. */ private Duration maxWait = Duration.ofMillis(-1); /** * Time between runs of the idle object evictor thread. When positive, the idle * object evictor thread starts, otherwise no idle object eviction is performed. */ private Duration timeBetweenEvictionRuns; public int getMaxIdle() { return this.maxIdle; } public void setMaxIdle(int maxIdle) { this.maxIdle = maxIdle; } public int getMinIdle() { return this.minIdle; } public void setMinIdle(int minIdle) { this.minIdle = minIdle; } public int getMaxActive() { return this.maxActive; } public void setMaxActive(int maxActive) { this.maxActive = maxActive; } public Duration getMaxWait() { return this.maxWait; } public void setMaxWait(Duration maxWait) { this.maxWait = maxWait; } public Duration getTimeBetweenEvictionRuns() { return this.timeBetweenEvictionRuns; } public void setTimeBetweenEvictionRuns(Duration timeBetweenEvictionRuns) { this.timeBetweenEvictionRuns = timeBetweenEvictionRuns; } } /** * Cluster properties. */ public static class Cluster { /** * Comma-separated list of "host:port" pairs to bootstrap from. This represents an * "initial" list of cluster nodes and is required to have at least one entry. */ private List<String> nodes; /** * Maximum number of redirects to follow when executing commands across the * cluster. */ private Integer maxRedirects; public List<String> getNodes() { return this.nodes; } public void setNodes(List<String> nodes) { this.nodes = nodes; } public Integer getMaxRedirects() { return this.maxRedirects; } public void setMaxRedirects(Integer maxRedirects) { this.maxRedirects = maxRedirects; } } /** * Redis sentinel properties. */ public static class Sentinel { /** * Name of the Redis server. */ private String master; /** * Comma-separated list of "host:port" pairs. */ private List<String> nodes; /** * Password for authenticating with sentinel(s). */ private String password; public String getMaster() { return this.master; } public void setMaster(String master) { this.master = master; } public List<String> getNodes() { return this.nodes; } public void setNodes(List<String> nodes) { this.nodes = nodes; } public String getPassword() { return this.password; } public void setPassword(String password) { this.password = password; } } /** * Jedis client properties. */ public static class Jedis { /** * Jedis pool configuration. */ private Pool pool; public Pool getPool() { return this.pool; } public void setPool(Pool pool) { this.pool = pool; } } /** * Lettuce client properties. */ public static class Lettuce { /** * Shutdown timeout. */ private Duration shutdownTimeout = Duration.ofMillis(100); /** * Lettuce pool configuration. */ private Pool pool; private final Cluster cluster = new Cluster(); public Duration getShutdownTimeout() { return this.shutdownTimeout; } public void setShutdownTimeout(Duration shutdownTimeout) { this.shutdownTimeout = shutdownTimeout; } public Pool getPool() { return this.pool; } public void setPool(Pool pool) { this.pool = pool; } public Cluster getCluster() { return this.cluster; } public static class Cluster { private final Refresh refresh = new Refresh(); public Refresh getRefresh() { return this.refresh; } public static class Refresh { /** * Cluster topology refresh period. */ private Duration period; /** * Whether adaptive topology refreshing using all available refresh * triggers should be used. */ private boolean adaptive; public Duration getPeriod() { return this.period; } public void setPeriod(Duration period) { this.period = period; } public boolean isAdaptive() { return this.adaptive; } public void setAdaptive(boolean adaptive) { this.adaptive = adaptive; } } } } }
-
-
总结最后发现RedisInputStream的readTimeout的值,是从RedisProperties 配置类中获取的,获取的配置项是spring.redis.timeout
正向总结-逻辑
-
项目类依赖于RedisTemplate
-
//1.存在类存在属性依赖RedisTemplate,这就开始了Redis相关类bean的初始化 public class Test{ @Autowired private RedisTemplate<String,String> redisTemplate; }
-
-
RedisTemplate由项目中配置类创建Bean,此处决定RedisTemplate的初始化创建依赖于RedisConnectionFactory factory
-
@Configuration @EnableCaching //开启注解 public class RedisConfig extends CachingConfigurerSupport { @Resource(name = "customObjectMapper") private ObjectMapper om; /** * redisTemplate相关配置 * @param factory * @return */ @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); // 配置连接工厂 template.setConnectionFactory(factory); //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式) Jackson2JsonRedisSerializer<Object> jacksonSerial = new Jackson2JsonRedisSerializer<>(Object.class); jacksonSerial.setObjectMapper(om); // 值采用json序列化 template.setValueSerializer(jacksonSerial); //使用StringRedisSerializer来序列化和反序列化redis的key值 template.setKeySerializer(new StringRedisSerializer()); // 设置hash key 和value序列化模式 template.setHashKeySerializer(new StringRedisSerializer()); template.setHashValueSerializer(jacksonSerial); template.afterPropertiesSet(); return template; } }
-
-
RedisConnectionFactory 的创建是由org.springframework.boot.autoconfigure.data.redis.JedisConnectionConfiguration创建的。这里需要记住JedisConnectionConfiguration下的两个属性,因为JedisConnectionConfiguration是继承RedisConnectionConfiguration,在RedisConnectionConfiguration中存在属性 private final RedisProperties properties;后续会用到
-
package org.springframework.boot.autoconfigure.data.redis; /** * Redis connection configuration using Jedis. * * @author Mark Paluch * @author Stephane Nicoll */ @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ GenericObjectPool.class, JedisConnection.class, Jedis.class }) class JedisConnectionConfiguration extends RedisConnectionConfiguration { JedisConnectionConfiguration(RedisProperties properties, ObjectProvider<RedisSentinelConfiguration> sentinelConfiguration, ObjectProvider<RedisClusterConfiguration> clusterConfiguration) { super(properties, sentinelConfiguration, clusterConfiguration); } //当项目中不存在RedisConnectionFactory,则创建RedisConnectionFactory @Bean @ConditionalOnMissingBean(RedisConnectionFactory.class) JedisConnectionFactory redisConnectionFactory( ObjectProvider<JedisClientConfigurationBuilderCustomizer> builderCustomizers) throws UnknownHostException { //调用私有方法createJedisConnectionFactory(ObjectProvider<JedisClientConfigurationBuilderCustomizer> builderCustomizers) return createJedisConnectionFactory(builderCustomizers); } private JedisConnectionFactory createJedisConnectionFactory( ObjectProvider<JedisClientConfigurationBuilderCustomizer> builderCustomizers) { JedisClientConfiguration clientConfiguration = getJedisClientConfiguration(builderCustomizers); if (getSentinelConfig() != null) { return new JedisConnectionFactory(getSentinelConfig(), clientConfiguration); } if (getClusterConfiguration() != null) { return new JedisConnectionFactory(getClusterConfiguration(), clientConfiguration); } return new JedisConnectionFactory(getStandaloneConfig(), clientConfiguration); } private JedisClientConfiguration getJedisClientConfiguration( ObjectProvider<JedisClientConfigurationBuilderCustomizer> builderCustomizers) { JedisClientConfigurationBuilder builder = applyProperties(JedisClientConfiguration.builder()); RedisProperties.Pool pool = getProperties().getJedis().getPool(); if (pool != null) { applyPooling(pool, builder); } if (StringUtils.hasText(getProperties().getUrl())) { customizeConfigurationFromUrl(builder); } builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); return builder.build(); } private JedisClientConfigurationBuilder applyProperties(JedisClientConfigurationBuilder builder) { if (getProperties().isSsl()) { builder.useSsl(); } if (getProperties().getTimeout() != null) { Duration timeout = getProperties().getTimeout(); builder.readTimeout(timeout).connectTimeout(timeout); } if (StringUtils.hasText(getProperties().getClientName())) { builder.clientName(getProperties().getClientName()); } return builder; } private void applyPooling(RedisProperties.Pool pool, JedisClientConfiguration.JedisClientConfigurationBuilder builder) { builder.usePooling().poolConfig(jedisPoolConfig(pool)); } private JedisPoolConfig jedisPoolConfig(RedisProperties.Pool pool) { JedisPoolConfig config = new JedisPoolConfig(); config.setMaxTotal(pool.getMaxActive()); config.setMaxIdle(pool.getMaxIdle()); config.setMinIdle(pool.getMinIdle()); if (pool.getTimeBetweenEvictionRuns() != null) { config.setTimeBetweenEvictionRunsMillis(pool.getTimeBetweenEvictionRuns().toMillis()); } if (pool.getMaxWait() != null) { config.setMaxWaitMillis(pool.getMaxWait().toMillis()); } return config; } private void customizeConfigurationFromUrl(JedisClientConfiguration.JedisClientConfigurationBuilder builder) { ConnectionInfo connectionInfo = parseUrl(getProperties().getUrl()); if (connectionInfo.isUseSsl()) { builder.useSsl(); } } }
-
abstract class RedisConnectionConfiguration { private final RedisProperties properties; }
-
-
而从JedisConnectionConfiguration(RedisProperties properties,
ObjectProvider sentinelConfiguration,
ObjectProvider clusterConfiguration)可以看到,JedisConnectionConfiguration依赖RedisProperties配置类-
@ConfigurationProperties(prefix = "spring.redis") public class RedisProperties { /** * Database index used by the connection factory. */ private int database = 0; /** * Connection URL. Overrides host, port, and password. User is ignored. Example: * redis://user:password@example.com:6379 */ private String url; /** * Redis server host. */ private String host = "localhost"; /** * Login password of the redis server. */ private String password; /** * Redis server port. */ private int port = 6379; /** * Whether to enable SSL support. */ private boolean ssl; /** * Connection timeout. */ private Duration timeout; /** * Client name to be set on connections with CLIENT SETNAME. */ private String clientName; private Sentinel sentinel; private Cluster cluster; private final Jedis jedis = new Jedis(); private final Lettuce lettuce = new Lettuce(); public int getDatabase() { return this.database; } public void setDatabase(int database) { this.database = database; } public String getUrl() { return this.url; } public void setUrl(String url) { this.url = url; } public String getHost() { return this.host; } public void setHost(String host) { this.host = host; } public String getPassword() { return this.password; } public void setPassword(String password) { this.password = password; } public int getPort() { return this.port; } public void setPort(int port) { this.port = port; } public boolean isSsl() { return this.ssl; } public void setSsl(boolean ssl) { this.ssl = ssl; } public void setTimeout(Duration timeout) { this.timeout = timeout; } public Duration getTimeout() { return this.timeout; } public String getClientName() { return this.clientName; } public void setClientName(String clientName) { this.clientName = clientName; } public Sentinel getSentinel() { return this.sentinel; } public void setSentinel(Sentinel sentinel) { this.sentinel = sentinel; } public Cluster getCluster() { return this.cluster; } public void setCluster(Cluster cluster) { this.cluster = cluster; } public Jedis getJedis() { return this.jedis; } public Lettuce getLettuce() { return this.lettuce; } /** * Pool properties. */ public static class Pool { /** * Maximum number of "idle" connections in the pool. Use a negative value to * indicate an unlimited number of idle connections. */ private int maxIdle = 8; /** * Target for the minimum number of idle connections to maintain in the pool. This * setting only has an effect if both it and time between eviction runs are * positive. */ private int minIdle = 0; /** * Maximum number of connections that can be allocated by the pool at a given * time. Use a negative value for no limit. */ private int maxActive = 8; /** * Maximum amount of time a connection allocation should block before throwing an * exception when the pool is exhausted. Use a negative value to block * indefinitely. */ private Duration maxWait = Duration.ofMillis(-1); /** * Time between runs of the idle object evictor thread. When positive, the idle * object evictor thread starts, otherwise no idle object eviction is performed. */ private Duration timeBetweenEvictionRuns; public int getMaxIdle() { return this.maxIdle; } public void setMaxIdle(int maxIdle) { this.maxIdle = maxIdle; } public int getMinIdle() { return this.minIdle; } public void setMinIdle(int minIdle) { this.minIdle = minIdle; } public int getMaxActive() { return this.maxActive; } public void setMaxActive(int maxActive) { this.maxActive = maxActive; } public Duration getMaxWait() { return this.maxWait; } public void setMaxWait(Duration maxWait) { this.maxWait = maxWait; } public Duration getTimeBetweenEvictionRuns() { return this.timeBetweenEvictionRuns; } public void setTimeBetweenEvictionRuns(Duration timeBetweenEvictionRuns) { this.timeBetweenEvictionRuns = timeBetweenEvictionRuns; } } /** * Cluster properties. */ public static class Cluster { /** * Comma-separated list of "host:port" pairs to bootstrap from. This represents an * "initial" list of cluster nodes and is required to have at least one entry. */ private List<String> nodes; /** * Maximum number of redirects to follow when executing commands across the * cluster. */ private Integer maxRedirects; public List<String> getNodes() { return this.nodes; } public void setNodes(List<String> nodes) { this.nodes = nodes; } public Integer getMaxRedirects() { return this.maxRedirects; } public void setMaxRedirects(Integer maxRedirects) { this.maxRedirects = maxRedirects; } } /** * Redis sentinel properties. */ public static class Sentinel { /** * Name of the Redis server. */ private String master; /** * Comma-separated list of "host:port" pairs. */ private List<String> nodes; /** * Password for authenticating with sentinel(s). */ private String password; public String getMaster() { return this.master; } public void setMaster(String master) { this.master = master; } public List<String> getNodes() { return this.nodes; } public void setNodes(List<String> nodes) { this.nodes = nodes; } public String getPassword() { return this.password; } public void setPassword(String password) { this.password = password; } } /** * Jedis client properties. */ public static class Jedis { /** * Jedis pool configuration. */ private Pool pool; public Pool getPool() { return this.pool; } public void setPool(Pool pool) { this.pool = pool; } } /** * Lettuce client properties. */ public static class Lettuce { /** * Shutdown timeout. */ private Duration shutdownTimeout = Duration.ofMillis(100); /** * Lettuce pool configuration. */ private Pool pool; private final Cluster cluster = new Cluster(); public Duration getShutdownTimeout() { return this.shutdownTimeout; } public void setShutdownTimeout(Duration shutdownTimeout) { this.shutdownTimeout = shutdownTimeout; } public Pool getPool() { return this.pool; } public void setPool(Pool pool) { this.pool = pool; } public Cluster getCluster() { return this.cluster; } public static class Cluster { private final Refresh refresh = new Refresh(); public Refresh getRefresh() { return this.refresh; } public static class Refresh { /** * Cluster topology refresh period. */ private Duration period; /** * Whether adaptive topology refreshing using all available refresh * triggers should be used. */ private boolean adaptive; public Duration getPeriod() { return this.period; } public void setPeriod(Duration period) { this.period = period; } public boolean isAdaptive() { return this.adaptive; } public void setAdaptive(boolean adaptive) { this.adaptive = adaptive; } } } } }
-
-
RedisProperties配置类获取的是从配置类中获取spring.redis 下的相关配置项
-
redis: database: 4 host: 192.168.10.143 port: 6350 password: lettuce: pool: max-idle: 8 max-active: 8 min-idle: 0 max-wait: -1 timeout: 12
-
-
上一步为初始化JedisConnectionConfiguration及其相关的依赖配置类,接下去的步骤是初始化JedisConnectionFactory类 的bean,通过JedisConnectionConfiguration.createJedisConnectionFactory() 方法
-
@Bean @ConditionalOnMissingBean(RedisConnectionFactory.class) JedisConnectionFactory redisConnectionFactory( ObjectProvider<JedisClientConfigurationBuilderCustomizer> builderCustomizers) throws UnknownHostException { return createJedisConnectionFactory(builderCustomizers); } //创建JedisConnectionFactory private JedisConnectionFactory createJedisConnectionFactory( ObjectProvider<JedisClientConfigurationBuilderCustomizer> builderCustomizers) { //获取JedisClientConfigurationBuilder JedisClientConfiguration clientConfiguration = getJedisClientConfiguration(builderCustomizers); if (getSentinelConfig() != null) { return new JedisConnectionFactory(getSentinelConfig(), clientConfiguration); } if (getClusterConfiguration() != null) { return new JedisConnectionFactory(getClusterConfiguration(), clientConfiguration); } return new JedisConnectionFactory(getStandaloneConfig(), clientConfiguration); } //获取JedisClientConfigurationBuilder private JedisClientConfiguration getJedisClientConfiguration( ObjectProvider<JedisClientConfigurationBuilderCustomizer> builderCustomizers) { // org.springframework.data.redis.connection.jedis.JedisClientConfiguration.DefaultJedisClientConfigurationBuilder //关键步骤调用applyProperties 将redisProperties的timeout属性值 赋值给builder的readTimeout字段 JedisClientConfigurationBuilder builder = applyProperties(JedisClientConfiguration.builder()); RedisProperties.Pool pool = getProperties().getJedis().getPool(); if (pool != null) { applyPooling(pool, builder); } if (StringUtils.hasText(getProperties().getUrl())) { customizeConfigurationFromUrl(builder); } builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); //Build the {@link JedisClientConfiguration} with the configuration applied from this builder. 这里要注意的是readTimeout的赋值 return builder.build(); } private JedisClientConfigurationBuilder applyProperties(JedisClientConfigurationBuilder builder) { if (getProperties().isSsl()) { builder.useSsl(); } //此处设置了redis readTimeout if (getProperties().getTimeout() != null) { //获取的是RedisProperties的Timeout属性 Duration timeout = getProperties().getTimeout(); builder.readTimeout(timeout).connectTimeout(timeout); } if (StringUtils.hasText(getProperties().getClientName())) { builder.clientName(getProperties().getClientName()); } return builder; } class DefaultJedisClientConfigurationBuilder implements JedisClientConfigurationBuilder, JedisPoolingClientConfigurationBuilder, JedisSslClientConfigurationBuilder { //初始化默认为Protocol.DEFAULT_TIMEOUT public static final int DEFAULT_TIMEOUT = 2000; private Duration readTimeout = Duration.ofMillis(Protocol.DEFAULT_TIMEOUT); /* * (non-Javadoc) * @see org.springframework.data.redis.connection.jedis.JedisClientConfiguration.JedisClientConfigurationBuilder#build() */ @Override public JedisClientConfiguration build() { return new DefaultJedisClientConfiguration(useSsl, sslSocketFactory, sslParameters, hostnameVerifier, usePooling, poolConfig, clientName, readTimeout, connectTimeout); } /* * (non-Javadoc) * @see org.springframework.data.redis.connection.jedis.JedisClientConfiguration.JedisClientConfigurationBuilder#readTimeout(java.time.Duration) */ @Override public JedisClientConfigurationBuilder readTimeout(Duration readTimeout) { Assert.notNull(readTimeout, "Duration must not be null!"); this.readTimeout = readTimeout; return this; } }
-
-
此处可以获得到逻辑
- 初始化JedisConnectionFactory类 的bean
- 调用getJedisClientConfiguration()方法生成JedisClientConfigurationBuilder对象
- 生成JedisClientConfigurationBuilder对象后,调用applyProperties(JedisClientConfigurationBuilder builder)方法,将redisProperties的timeout属性值,赋值给JedisClientConfigurationBuilder 的ReadTimeout属性
- 总结:由此框架就定义了redis的readTimeout时间,至此在createJedisConnectionFactory()方法中的getJedisClientConfiguration()逻辑结束了。
-
将生成的JedisClientConfiguration赋值给JedisConnectionFactory,最后返回JedisConnectionFactory
-
private JedisConnectionFactory createJedisConnectionFactory( ObjectProvider<JedisClientConfigurationBuilderCustomizer> builderCustomizers) { JedisClientConfiguration clientConfiguration = getJedisClientConfiguration(builderCustomizers); if (getSentinelConfig() != null) { return new JedisConnectionFactory(getSentinelConfig(), clientConfiguration); } if (getClusterConfiguration() != null) { return new JedisConnectionFactory(getClusterConfiguration(), clientConfiguration); } return new JedisConnectionFactory(getStandaloneConfig(), clientConfiguration); }
-
-
创建完JedisConnectionFactory类的Bean对象后,回到redisTemplate类的Bean创建方法中,通过setConnectionFactory() 给redisTemplate赋值redis工厂。最后,完成redisTemplate 类的Bean创建。
-
@Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); // 配置连接工厂 template.setConnectionFactory(factory); //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式) Jackson2JsonRedisSerializer<Object> jacksonSerial = new Jackson2JsonRedisSerializer<>(Object.class); jacksonSerial.setObjectMapper(om); // 值采用json序列化 template.setValueSerializer(jacksonSerial); //使用StringRedisSerializer来序列化和反序列化redis的key值 template.setKeySerializer(new StringRedisSerializer()); // 设置hash key 和value序列化模式 template.setHashKeySerializer(new StringRedisSerializer()); template.setHashValueSerializer(jacksonSerial); template.afterPropertiesSet(); return template; }
-
-
接下去是调用流程
-
redisTemplate.opsForValue().get(key);
-
public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperations<K, V>, BeanClassLoaderAware { AbstractOperations(RedisTemplate<K, V> template) { this.template = template; } DefaultValueOperations(RedisTemplate<K, V> template) { super(template); } private final ValueOperations<K, V> valueOps = new DefaultValueOperations<>(this); /* * (non-Javadoc) * @see org.springframework.data.redis.core.RedisOperations#opsForValue() */ @Override public ValueOperations<K, V> opsForValue() { return valueOps; } @Nullable public <T> T execute(RedisCallback<T> action, boolean exposeConnection) { return execute(action, exposeConnection, false); } @Nullable public <T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline) { Assert.isTrue(initialized, "template not initialized; call afterPropertiesSet() before using it"); Assert.notNull(action, "Callback object must not be null"); RedisConnectionFactory factory = getRequiredConnectionFactory(); RedisConnection conn = null; try { if (enableTransactionSupport) { // only bind resources in case of potential transaction synchronization conn = RedisConnectionUtils.bindConnection(factory, enableTransactionSupport); } else { conn = RedisConnectionUtils.getConnection(factory); } boolean existingConnection = TransactionSynchronizationManager.hasResource(factory); RedisConnection connToUse = preProcessConnection(conn, existingConnection); boolean pipelineStatus = connToUse.isPipelined(); if (pipeline && !pipelineStatus) { connToUse.openPipeline(); } RedisConnection connToExpose = (exposeConnection ? connToUse : createRedisConnectionProxy(connToUse)); T result = action.doInRedis(connToExpose); // close pipeline if (pipeline && !pipelineStatus) { connToUse.closePipeline(); } // TODO: any other connection processing? return postProcessResult(result, connToUse, existingConnection); } finally { RedisConnectionUtils.releaseConnection(conn, factory, enableTransactionSupport); } } }
-
class DefaultValueOperations<K, V> extends AbstractOperations<K, V> implements ValueOperations<K, V> { DefaultValueOperations(RedisTemplate<K, V> template) { super(template); } @Nullable <T> T execute(RedisCallback<T> callback, boolean exposeConnection) { return template.execute(callback, exposeConnection); } /* * redis get 命令 */ @Override public V get(Object key) { return execute(new ValueDeserializingRedisCallback(key) { @Override protected byte[] inRedis(byte[] rawKey, RedisConnection connection) { return connection.get(rawKey); } }, true); } }
-
public abstract class RedisConnectionUtils { public static RedisConnection getConnection(RedisConnectionFactory factory) { return getConnection(factory, false); } public static RedisConnection getConnection(RedisConnectionFactory factory, boolean transactionSupport) { return doGetConnection(factory, true, false, transactionSupport); } public static RedisConnection doGetConnection(RedisConnectionFactory factory, boolean allowCreate, boolean bind, boolean transactionSupport) { Assert.notNull(factory, "No RedisConnectionFactory specified"); RedisConnectionHolder connHolder = (RedisConnectionHolder) TransactionSynchronizationManager.getResource(factory); if (connHolder != null) { if (transactionSupport) { potentiallyRegisterTransactionSynchronisation(connHolder, factory); } return connHolder.getConnection(); } if (!allowCreate) { throw new IllegalArgumentException("No connection found and allowCreate = false"); } if (log.isDebugEnabled()) { log.debug("Opening RedisConnection"); } //获取 RedisConnection RedisConnection conn = factory.getConnection(); if (bind) { RedisConnection connectionToBind = conn; if (transactionSupport && isActualNonReadonlyTransactionActive()) { connectionToBind = createConnectionProxy(conn, factory); } connHolder = new RedisConnectionHolder(connectionToBind); TransactionSynchronizationManager.bindResource(factory, connHolder); if (transactionSupport) { potentiallyRegisterTransactionSynchronisation(connHolder, factory); } return connHolder.getConnection(); } return conn; } }
-
public class JedisConnectionFactory implements InitializingBean, DisposableBean, RedisConnectionFactory { /* * (non-Javadoc) * @see org.springframework.data.redis.connection.RedisConnectionFactory#getConnection() */ public RedisConnection getConnection() { if (isRedisClusterAware()) { return getClusterConnection(); } //获取redis 连接 Jedis jedis = fetchJedisConnector(); JedisConnection connection = (getUsePool() ? new JedisConnection(jedis, pool, getDatabase(), getClientName()) : new JedisConnection(jedis, null, getDatabase(), getClientName())); connection.setConvertPipelineAndTxResults(convertPipelineAndTxResults); return postProcessConnection(connection); } protected Jedis fetchJedisConnector() { try { if (getUsePool() && pool != null) { return pool.getResource(); } Jedis jedis = createJedis(); //强制初始化,这里会获取连接 // force initialization (see Jedis issue #82) jedis.connect(); potentiallySetClientName(jedis); return jedis; } catch (Exception ex) { throw new RedisConnectionFailureException("Cannot get Jedis connection", ex); } } }
-
public class BinaryJedis implements BasicCommands, BinaryJedisCommands, MultiKeyBinaryCommands, AdvancedBinaryJedisCommands, BinaryScriptingCommands, Closeable { public void connect() { client.connect(); } public class BinaryClient extends Connection { @Override public void connect() { if (!isConnected()) { super.connect(); if (user != null) { auth(user, password); getStatusCodeReply(); } else if (password != null) { auth(password); getStatusCodeReply(); } if (db > 0) { select(db); getStatusCodeReply(); } } } } }
-
public class Connection implements Closeable { public String getStatusCodeReply() { flush(); final byte[] resp = (byte[]) readProtocolWithCheckingBroken(); if (null == resp) { return null; } else { return SafeEncoder.encode(resp); } } protected Object readProtocolWithCheckingBroken() { if (broken) { throw new JedisConnectionException("Attempting to read from a broken connection"); } try { return Protocol.read(inputStream); } catch (JedisConnectionException exc) { broken = true; throw exc; } } }
-
public final class Protocol { public static Object read(final RedisInputStream is) { return process(is); } private static Object process(final RedisInputStream is) { final byte b = is.readByte(); switch (b) { case PLUS_BYTE: return processStatusCodeReply(is); case DOLLAR_BYTE: return processBulkReply(is); case ASTERISK_BYTE: return processMultiBulkReply(is); case COLON_BYTE: return processInteger(is); case MINUS_BYTE: processError(is); return null; default: throw new JedisConnectionException("Unknown reply: " + (char) b); } } } public class RedisInputStream extends FilterInputStream { public byte readByte() throws JedisConnectionException { ensureFill(); return buf[count++]; } private void ensureFill() throws JedisConnectionException { if (count >= limit) { try { limit = in.read(buf); count = 0; if (limit == -1) { throw new JedisConnectionException("Unexpected end of stream."); } } catch (IOException e) { throw new JedisConnectionException(e); } } } }
-