行业知识
记一次用Arthas排查Redis连接数增加问题(附:查看bean)
���一次生产环境发包后,发现redis连接数变多了,由于改的代码比较多,不确定是哪里出了问题。因此用Arthas来进行了一次排查。
()项目比较多,有用到 jedis、lettuce、redisson 3种客户端。
项目用到的 SpringContextHolder 代码如下:
()package com.aop.util;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.BeansException;import org.springframework.context.ApplicationContext;import org.springframework.context.ApplicationContextAware;import org.springframework.stereotype.Component;import java.util.Map;@Component("springContextHolder")public class SpringContextHolder implements ApplicationContextAware { private static final Logger logger = LoggerFactory.getLogger(SpringContextHolder.class); private static ApplicationContext applicationContext; /** * 实现ApplicationContextAware接口, 注入Context到静态变量中. */ @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { logger.info("ServletContextUtil is init"); SpringContextHolder.applicationContext = applicationContext; } /** * 获取静态变量中的ApplicationContext. */ public static ApplicationContext getApplicationContext() { assertContextInjected(); return applicationContext; } /** * 从静态变量applicationContext中得到Bean, 自动转型为所赋值对象的类型. */ @SuppressWarnings("unchecked") public static T getBean(String name) { assertContextInjected(); return (T) applicationContext.getBean(name); } /** * 从静态变量applicationContext中得到Bean, 自动转型为所赋值对象的类型. */ public static T getBean(Class requiredType) { assertContextInjected(); return applicationContext.getBean(requiredType); } public static Map getBeansOfType(Class requiredType) { assertContextInjected(); return applicationContext.getBeansOfType(requiredType); } /** * 检查ApplicationContext不为空. */SpringContextHolder private static void assertContextInjected() { if (applicationContext == null) { throw new IllegalStateException("Application Context Have Not Injection"); } } /** * 清除SpringContextHolder中的ApplicationContext为Null. */ public static void clearHolder() { applicationContext = null; } /** * 打印对应Class的Bean是否注入到容器 */ public static void printBean(Class requiredType) { assertContextInjected(); T instance = null; try { instance = SpringContextHolder.getBean(requiredType); } catch (Exception ex) { logger.warn("bean:" + requiredType.getSimpleName() + " is null"); } logger.info(requiredType.getSimpleName() + ":{}", instance != null ? instance.toString() : "null"); }} 第一步:看项目中启动类使用到了哪些客户端
启动arthas,怎么启动,网上有资料,可以搜一下。
使用 sc 命令
sc -d com.aop.util.SpringContextHolder
可以看到 classLoaderHash 值 : 33d512c1
[arthas@1]$ sc -d com.aop.util.SpringContextHolde class-info com.aop.util.SpringContextHolde code-source /opt/cloud/tomcat/appstore%23file%23api/WEB-INF/classes/ name com.aop.util.SpringContextHolde isInterface false isAnnotation false isEnum false isAnonymousClass false isArray false isLocalClass false isMemberClass false isPrimitive false isSynthetic false simple-name SpringContextHolde modifier public classLoaderHash 33d512c1
执行 ognl 的 getContext() 测试一下,有输出则成功
ognl -c 33d512c1 '#conetex=@com.aop.util.SpringContextHolde@getContext()'
执行getBeanDefinitionNames()查看有哪些bean
ognl -c 33d512c1 '@com.aop.util.SpringContextHolde@getContext().getBeanDefinitionNames()'
可以看到输出的bean中只有
...org.springframework.boot.autoconfigure.data.redis.JedisConnectionConfiguration...
我们确定,我们项目是引入了 lettuce ,为什么没有有看到相关的Bean,"org.springframework.boot.autoconfigure.data.redis.LettuceConnectionConfiguration" ?
通过查看文档:在 Spring Boot 中整合、使用 Redis - spring 中文网
我们发现
注意如果用到
org.springframework.boot spring-boot-starter-data-redis
默认是使用 lettuce,如果要用 jedis 或者 redisson 务必,排除
org.springframework.boot spring-boot-starter-data-redis io.lettuce lettuce-core
我们项目有人提交代码的时候,把lettuce排除了,导致我们用到了 jedis 客户端。
第二步:查看客户端的连接池、连接串配置
继续看 JedisConnectionConfiguration 的连接池配置
ognl -c 33d512c1 '#context=@com.aop.util.SpringContextHolde @getContext(),#context.getBean("org.springframework.boot.autoconfigure.data.redis.LettuceConnectionConfiguration")' 输出如下:
@JedisConnectionConfiguration[ COMMONS_POOL2_AVAILABLE=@Boolean[true], properties=@RedisProperties[org.springframework.boot.autoconfigure.data.redis.RedisProperties@24d30f55], standaloneConfiguration=null, sentinelConfiguration=null, clusterConfiguration=null,]
继续看,我们用下面命令,查看配置,我们发现
ognl -c 33d512c1 '#context=@com.aop.util.SpringContextHolde@getContext(),#context.getBean("org.springframework.boot.autoconfigure.data.redis.JedisConnectionConfiguration").getProperties().getJedis().getPool()' 输出:
@Pool[ enabled=null, maxIdle=@Integer[100], minIdle=@Integer[100], maxActive=@Integer[200], maxWait=@Duration[PT0.002S], timeBetweenEvictionRuns=null,]
问题就出在这里,我们 minIdle 设置太大,redis 又是集群部署了 6个 节点,因此连接数增加了 600个。之前用的 lettuce 客户端,minIdle=0,maxIdle=8,远远小于 jedis 的配置。
第三步:解决
修改jedis minIdle 配置
其他:用ognl查看连接池信息
// 查看有哪些 bean
ognl -c 33d512c1 '@com.aop.util.SpringContextHolde@getContext()'ognl -c 33d512c1 '#conetex=@com.aop.util.SpringContextHolde@getContext()'ognl -c 33d512c1 '@com.aop.util.SpringContextHolde@getContext().getBeanDefinitionNames()'
// 查看 redisConnectionFactory 连接信息
ognl -c 33d512c1 '#context=@com.aop.util.SpringContextHolde@getContext(),#context.getBean("redisConnectionFactory")' // redison 连接池信息
// 查看配置的模式
ognl -c 33d512c1 '#context=@com.aop.util.SpringContextHolde@getContext(),#context.getBean("redisson").getConfig()'ognl -c 33d512c1 '#context=@com.aop.util.SpringContextHolde@getContext(),#context.getBean("redisson").getConfig().getClusterServersConfig()' // lettuce 连接池信息
ognl -c 33d512c1 '#context=@com.aop.util.SpringContextHolde@getContext(),#context.getBean("org.springframework.boot.autoconfigure.data.redis.LettuceConnectionConfiguration").getProperties().getLettuce().getPool()' // jedis 连接池信息
ognl -c 33d512c1 '#context=@com.aop.util.SpringContextHolde@getContext(),#context.getBean("org.springframework.boot.autoconfigure.data.redis.JedisConnectionConfiguration").getProperties().getJedis().getPool()' // jetcache(默认: jedis) 连接池信息
ognl -c 33d512c1 '#context=@com.aop.util.SpringContextHolde@getContext(),#context.getBean("globalCacheConfig")'ognl -c 33d512c1 '#context=@com.aop.util.SpringContextHolde@getContext(),#context.getBean("globalCacheConfig").getRemoteCacheBuilders().get("default").getConfig'
上一篇:设计怎么说服客户?
下一篇:服务器机柜底线怎么接?