遇到的问题:
一开始做项目一直用的是jedis对象连接,今天发现自己上线的项目抛出了异常:JedisConnectionException: java.Net.SocketTimeoutException: Read timed和和java.lang.ClassCastException: [B cannot be cast to java.util.List。
异常解释:
jedis的默认读取时间是2s,由于我用的是jedis对象连接因此当多个线程操作redis的时候,redis服务器采用的机制是FIFO(先入先出)机制,因此会使得线程等待时间增长,因此会造成redis读取超时,看了这个文章(https://blog.csdn.net/shuaiOKshuai/article/details/23266091)后,所以我在创建jedis对象时候把时间改成了100s,之后没有再抛出TimedoutException不过还是抛出了ClassCastException异常,后来查询网上资料(https://hellojimmy.iteye.com/blog/1197543)发现还是因为redis读取超时导致返回值是一个特别长的序列导致和我想接收的数据类型不一致导致抛出了该异常。
解决办法:
后来还是想到只能用jedisPool连接池来管理jedis对象。
使用jedisPool的好处:
- 不适用连接池:当有很多任务需要采用线程执行的时候,而且有时可能会创建很多线程的时候,最好使用下线程池。
不使用线程池的话,所创建的线程数无法控制,比如一下子创建了几百几千个线程,电脑一下子就崩溃了,因为创建销毁线程,消耗资源较多。。 - 使用连接池:提高效率 创建好一定数量的线程放在池中,等需要使用的时候就从池中拿一个,这要比需要的时候创建一个线程对象要快的多。
- 方便管理 可以编写线程池管理代码对池中的线程统一进行管理,比如说系统启动时由该程序创建100个线程,每当有请求的时候,就分配一个线程去工作, 如果刚好并发有101个请求,那多出的这一个请求可以排队等候,避免因无休止的创建线程导致系统崩溃2:方便管理 可以编写线程池管理代码对池中的线程统一进行管理,比如说系统启动时由该程序创建100个线程,每当有请求的时候,就分配一个线程去工作, 如果刚好并发有101个请求,那多出的这一个请求可以排队等候,避免因无休止的创建线程导致系统崩溃
所需jar包:
jedis-2.9.0.jar+commons-pool2-2.4.2.jar(注意jar包版本不同会导致出现JedisPoolConfig中没有SetMaxTotal(),setMaxIdle()等方法)
下载地址:https://www.mvnjar.com/org.apache.commons/commons-pool2/2.4.1/detail.html
简洁代码:
package com.yj.test.javaBases.testJedis;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCommands;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;public class TestJedis {public static final Logger logger = LoggerFactory.getLogger(TestJedis.class);// JedispoolJedisCommands jedisCommands;JedisPool jedisPool;JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();String ip = "192.168.x.x";int port = 6379;int timeout = 2000;public TestJedis() {// 初始化jedis// 设置配置jedisPoolConfig.setMaxTotal(1024);jedisPoolConfig.setMaxIdle(100);jedisPoolConfig.setMaxWaitMillis(100);jedisPoolConfig.setTestOnBorrow(false);//jedis 第一次启动时,会报错jedisPoolConfig.setTestOnReturn(true);// 初始化JedisPooljedisPool = new JedisPool(jedisPoolConfig, ip, port, timeout);// Jedis jedis = jedisPool.getResource();jedisCommands = jedis;}public void setValue(String key, String value) {this.jedisCommands.set(key, value);}public String getValue(String key) {return this.jedisCommands.get(key);}public static void main(String[] args) {TestJedis testJedis = new TestJedis();testJedis.setValue("testJedisKey", "testJedisValue");logger.info("get value from redis:{}",testJedis.getValue("testJedisKey"));}}
详细配置解释代码:
package com.test;import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;public class RedisUtil {//Redis服务器IPprivate static String ADDR = "192.168.0.41";//Redis的端口号private static int PORT = 6379;//访问密码private static String AUTH = "admin";//可用连接实例的最大数目,默认值为8;//如果赋值为-1,则表示不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted(耗尽)。private static int MAX_TOTAL = 8;//最小空闲连接数, 默认0private static int MIN_IDLE=0;//控制一个pool最多有多少个状态为idle(空闲的)的jedis实例,默认值也是8。//最大空闲连接数, 默认8个private static int MAX_IDLE = 8;//获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间, 默认-1//等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。如果超过等待时间,则直接抛出JedisConnectionException;private static int MAX_WAIT = -1;private static int TIMEOUT = 10000;//连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认trueprivate static boolean BLOCK_WHEN_EXHAUSTED = false;//设置的逐出策略类名, 默认DefaultEvictionPolicy(当连接超过最大空闲时间,或连接数超过最大空闲连接数)private static String EVICTION_POLICY_CLASSNAME="org.apache.commons.pool2.impl.DefaultEvictionPolicy";//是否启用pool的jmx管理功能, 默认trueprivate static boolean JMX_ENABLED=true;//MBean ObjectName = new ObjectName("org.apache.commons.pool2:type=GenericObjectPool,name=" + "pool" + i); 默认为"pool", JMX不熟,具体不知道是干啥的...默认就好.private static String JMX_NAME_PREFIX="pool";//是否启用后进先出, 默认trueprivate static boolean LIFO=true;//逐出连接的最小空闲时间 默认1800000毫秒(30分钟)private static long MIN_EVICTABLE_IDLE_TIME_MILLIS=1800000L;//对象空闲多久后逐出, 当空闲时间>该值 且 空闲连接>最大空闲数 时直接逐出,不再根据MinEvictableIdleTimeMillis判断 (默认逐出策略) private static long SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS=1800000L;//每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3private static int NUM_TESTS_PER_EVICYION_RUN=3;//在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的;//在获取连接的时候检查有效性, 默认falseprivate static boolean TEST_ON_BORROW = false;//在空闲时检查有效性, 默认falseprivate static boolean TEST_WHILEIDLE=false;//逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1private static long TIME_BERWEEN_EVICTION_RUNS_MILLIS=-1;private static JedisPool jedisPool = null;/*** 初始化Redis连接池*/static {try {JedisPoolConfig config = new JedisPoolConfig();config.setBlockWhenExhausted(BLOCK_WHEN_EXHAUSTED);config.setEvictionPolicyClassName(EVICTION_POLICY_CLASSNAME); config.setJmxEnabled(JMX_ENABLED);config.setJmxNamePrefix(JMX_NAME_PREFIX);config.setLifo(LIFO);config.setMaxIdle(MAX_IDLE);config.setMaxTotal(MAX_TOTAL);config.setMaxWaitMillis(MAX_WAIT);config.setMinEvictableIdleTimeMillis(MIN_EVICTABLE_IDLE_TIME_MILLIS);config.setMinIdle(MIN_IDLE);config.setNumTestsPerEvictionRun(NUM_TESTS_PER_EVICYION_RUN);config.setSoftMinEvictableIdleTimeMillis(SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS);config.setTestOnBorrow(TEST_ON_BORROW);config.setTestWhileIdle(TEST_WHILEIDLE);config.setTimeBetweenEvictionRunsMillis(TIME_BERWEEN_EVICTION_RUNS_MILLIS);jedisPool = new JedisPool(config, ADDR, PORT, TIMEOUT, AUTH);} catch (Exception e) {e.printStackTrace();}}/*** 获取Jedis实例* @return*/public synchronized static Jedis getJedis() {try {if (jedisPool != null) {Jedis resource = jedisPool.getResource();return resource;} else {return null;}} catch (Exception e) {e.printStackTrace();return null;}}/*** 释放jedis资源* @param jedis*/public static void close(final Jedis jedis) {if (jedis != null) {jedis.close();}}
}
参考博客:
https://blog.csdn.net/shuaiOKshuai/article/details/23266091
https://hellojimmy.iteye.com/blog/1197543
https://blog.csdn.net/baidu_18607183/article/details/49330083
https://www.jianshu.com/p/8334151cbc10
https://www.cnblogs.com/zimublog/p/8404476.html