Redis连接池
Redis 是单进程单线程的,它利用队列技术将并发访问变为串行访问,消除了传统数据库串行控制的开销。
Redis 是基于内存的数据库,使用之前需要建立连接,建立断开连接需要消耗大量的时间。
再假设 Redis 服务器与客户端分处在异地,虽然基于内存的 Redis 数据库有着超高的性能,但是底层的网络通信却占用了一次数据请求的大量时间,因为每次数据交互都需要先建立连接,假设一次数据交互总共用时 30ms,超高性能的 Redis 数据库处理数据所花的时间可能不到 1ms,也即是说前期的连接占用了 29ms,连接池则可以实现在客户端建立多个连接并且不释放,当需要使用连接的时候通过一定的算法获取已经建立的连接,使用完了以后则还给连接池,这就免去了数据库连接所占用的时间。
Jedis resource = jedisPool.getResource();
注意,这行代码。我们从 JedisPool 中获取的仅仅是一个连接。至于多个连接到达单进程单线程的 Redis 之后怎么处理,就与线程池无关了。
实际上,Redis 在收到多个连接后,采用的是非阻塞 IO,基于 epoll 的多路 IO 复用。
然后采用队列模式将并发访问变为串行访问,对于串行访问,本身操作内存就很快,Redis 采用一个线程来处理就再正常不过了。
Redis连接池的使用与配置
在 Java 中使用Redis连接池需要两个 jar 包 commons-pool2-2.4.2.jar 跟 jedis-2.9.0.jar
配置连接池
因为连接池中会有很多jedis实例,RedisPool对象会很大,所以我们需要把他写成单例模式,如果是交由Spring管理就不用了,因为Spring管理的Bean默认是单例的。
// 连接信息
private static final String HOST = "132.232.6.208";
private static final int PORT = 6381;
JedisPoolConfig poolConfig = new JedisPoolConfig();
// 基本配置
poolConfig.setMaxTotal(1000); // 最大连接数
poolConfig.setMaxIdle(32); // 最大空闲连接数
poolConfig.setMaxWaitMillis(100*1000); // 最大等待时间
poolConfig.setTestOnBorrow(true); // 检查连接可用性, 确保获取的redis实例可用
JedisPool jedisPool = new JedisPool(poolConfig, HOST, PORT);
获取连接池连接
Jedis jedis = jedisPool.getResource();
将连接归还连接池
jedisPool.returnResourceObject(jedis); // 已废弃,推荐使用jedis.close()方法
jedis.close()
完整代码
package com.project.uitl;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
/**
* Redis 连接池工具包
* @author wqj24
*
*/
public class JedisPoolUtil {
private static final String HOST = "132.232.6.208";
private static final int PORT = 6381;
private static volatile JedisPool jedisPool = null;
private JedisPoolUtil() {}
/**
* 获取RedisPool实例(单例)
* @return RedisPool实例
*/
public static JedisPool getJedisPoolInstance() {
if (jedisPool == null) {
synchronized (JedisPoolUtil.class) {
if (jedisPool == null) {
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(1000); // 最大连接数
poolConfig.setMaxIdle(32); // 最大空闲连接数
poolConfig.setMaxWaitMillis(100*1000); // 最大等待时间
poolConfig.setTestOnBorrow(true); // 检查连接可用性, 确保获取的redis实例可用
jedisPool = new JedisPool(poolConfig, HOST, PORT);
}
}
}
return jedisPool;
}
/**
* 从连接池中获取一个 Jedis 实例(连接)
* @return Jedis 实例
*/
public static Jedis getJedisInstance() {
return getJedisPoolInstance().getResource();
}
/**
* 将Jedis对象(连接)归还连接池
* @param jedisPool 连接池
* @param jedis 连接对象
*/
public static void release(JedisPool jedisPool, Jedis jedis) {
if (jedis != null) {
jedisPool.returnResourceObject(jedis); // 已废弃,推荐使用jedis.close()方法
}
}
}
测试代码
package com.project.test;
import org.junit.Test;
import com.project.uitl.JedisPoolUtil;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
public class RedisPoolTest {
JedisPool jedisPool = JedisPoolUtil.getJedisPoolInstance();
// 测试单例
@Test
public void test01() {
JedisPool A = JedisPoolUtil.getJedisPoolInstance();
JedisPool B = JedisPoolUtil.getJedisPoolInstance();
System.out.println(A == B);
}
@Test
public void test02() {
Jedis jedis = null;
try {
jedis = jedisPool.getResource(); // 获取Redus连接
// 业务
jedis.set("k1", "v111");
System.out.println(jedis.get("k1"));
} finally {
jedis.close(); // 关闭redis连接
}
}
}
Redis连接池配置及初始化
加入db选择后的Redis连接池配置代码
public class RedisPoolConfigure {
//Redis服务器IP
private String ADDR ;
//Redis的端口号
private int PORT ;
//可用连接实例的最大数目
private int MAX_ACTIVE ;
//pool中的idle jedis实例数
private int MAX_IDLE ;
//等待可用连接的最大时间,单位毫秒
private int MAX_WAIT ;
//超时时间,单位毫秒
private int TIME_OUT ;
//设置的逐出策略类名, 默认DefaultEvictionPolicy(当连接超过最大空闲时间,或连接数超过最大空闲连接数)
private String EVICTION_POLICY_CLASS_NAME ;
//连接耗尽时是否阻塞, false报异常,ture阻塞直到超时
private boolean BLOCK_WHEN_EXHAUSTED;
//是否启用pool的jmx管理功能, 默认true
private boolean JMX_ENABLED;
//在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的;
private boolean TEST_ON_BORROW ;
//服务器密码
private String REDIS_PASS;
//redis选择数据库DB
private int REDIS_DB;
private String LUASHA;
private Map<String, String> configure = null;
/**
* 根据配置文件,将RedisPool连接配置初始化
*/
public RedisPoolConfigure(){
try {
configure = new ConfigureReader().readProperties("redis.properties");
} catch (IOException e) {
e.printStackTrace();
}
this.ADDR = configure.get("REDIS.ADDR");
this.LUASHA = configure.get("REDIS.LUA_HASH");
this.EVICTION_POLICY_CLASS_NAME = configure.get("REDIS.EVICTION_POLICY_CLASS_NAME");
this.BLOCK_WHEN_EXHAUSTED = Boolean.parseBoolean(configure.get("REDIS.BLOCK_WHEN_EXHAUSTED"));
this.JMX_ENABLED = Boolean.parseBoolean(configure.get("REDIS.JMX_ENABLED"));
this.TEST_ON_BORROW = Boolean.parseBoolean(configure.get("REDIS.TEST_ON_BORROW"));
this.REDIS_PASS=configure.get("REDIS.PASS");
if(typeCheck()){
this.PORT = new Integer(configure.get("REDIS.PORT"));
this.MAX_ACTIVE = new Integer(configure.get("REDIS.MAX_ACTIVE"));
this.MAX_IDLE = new Integer(configure.get("REDIS.MAX_IDLE"));
this.MAX_WAIT = new Integer(configure.get("REDIS.MAX_WAIT"));
this.REDIS_DB=new Integer(configure.get("REDIS.DB"));
}else{
System.out.println("error");
}
}
/**
* 辅助工具,检查map中数据的类型
* @return
*/
private boolean typeCheck() {
if (isNumeric(configure.get("REDIS.PORT"))
&& isNumeric(configure.get("REDIS.MAX_ACTIVE"))
&& isNumeric(configure.get("REDIS.MAX_IDLE"))
&& isNumeric(configure.get("REDIS.MAX_WAIT"))
&& isNumeric(configure.get("REDIS.DB"))) {
return true;
}
return false;
}
public String getADDR() {
return ADDR;
}
public int getPORT() {
return PORT;
}
public int getMAX_ACTIVE() {
return MAX_ACTIVE;
}
public int getMAX_IDLE() {
return MAX_IDLE;
}
public int getMAX_WAIT() {
return MAX_WAIT;
}
public int getTIME_OUT() {
return TIME_OUT;
}
public boolean isTEST_ON_BORROW() {
return TEST_ON_BORROW;
}
public String getEVICTION_POLICY_CLASS_NAME() {
return EVICTION_POLICY_CLASS_NAME;
}
public boolean isBLOCK_WHEN_EXHAUSTED() {
return BLOCK_WHEN_EXHAUSTED;
}
public boolean isJMX_ENABLED() {
return JMX_ENABLED;
}
/**
* 判断传入的数据是否为纯数字构成
* @param str
* @return
*/
public boolean isNumeric(String str) {
if(str==null || "".equals(str)){
return false;
}
for (int i = 0; i < str.length(); i++) {
if (!Character.isDigit(str.charAt(i))) {
return false;
}
}
return true;
}
public String getLUASHA() {
return LUASHA;
}
public void setLUASHA(String lUASHA) {
LUASHA = lUASHA;
}
public String getREDIS_PASS() {
return REDIS_PASS;
}
public void setREDIS_PASS(String rEDIS_PASS) {
REDIS_PASS = rEDIS_PASS;
}
public int getREDIS_DB() {
return REDIS_DB;
}
public void setREDIS_DB(int rEDIS_DB) {
REDIS_DB = rEDIS_DB;
}
}
Redis连接池初始化、获取Jedis实例和释放Jedis实例
/**
* jedis的连接池,返回未封装的jedis对象
* 一般只有在RedisCache类提供的操作粒度不足使用时才使用此类提供的原生jedis方法
* @author Hector
*
*/
public class RedisPool {
private static JedisPool jedisPool = null;
/**
* 初始化Redis连接池
*/
static {
try {
RedisPoolConfigure configure = new RedisPoolConfigure();
JedisPoolConfig config = new JedisPoolConfig();
config.setBlockWhenExhausted(configure.isBLOCK_WHEN_EXHAUSTED());
config.setEvictionPolicyClassName(configure.getEVICTION_POLICY_CLASS_NAME());
config.setJmxEnabled(configure.isJMX_ENABLED());
config.setMaxIdle(configure.getMAX_IDLE());
config.setMaxTotal(configure.getMAX_ACTIVE());
config.setMaxWaitMillis(configure.getMAX_WAIT());
config.setTestOnBorrow(configure.isTEST_ON_BORROW());
jedisPool = new JedisPool(config, configure.getADDR(), configure.getPORT(), configure.getTIME_OUT(),configure.getREDIS_PASS(),configure.getREDIS_DB());
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取Jedis实例
* @return
*/
public synchronized static Jedis getJedis() {
Jedis resource=null;
try {
if (jedisPool != null) {
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();
}
}
public static JedisPool getJedisPool() {
return jedisPool;
}
}
参考文章:
https://www.xttblog.com/?p=3823
https://www.jianshu.com/p/f7cb5235a892
https://blog.csdn.net/weixin_38994249/article/details/82774326