Redis客户端连接池
使用场景
对于一些大对象,或者初始化过程较长的可复用的对象,我们如果每次都new对象出来,那么意味着会耗费大量的时间。
我们可以将这些对象缓存起来,当接口调用完毕后,不是销毁对象,当下次使用的时候,直接从对象池中拿出来即可。
下面以redis客户端举例,说明下连接池的基础实现。
commons-pool2,是常用的对象池工具包,实现了对象池中对象的整个生命周期的管理,同时还可以手动指定对象生命周期的调度阀值。
Jedis是java的redis客户端的实现,能够实现对redis单机以及切片集群的链接。使用起来还很方便。
下面使用Jedis和commons-pool实现客户端连接池的管理。
首先定义生成Jedis链接的工厂
public class JedisPooledFactory extends BasePooledObjectFactory<Jedis> { //jedis server url
private String url = null; //redis server port
private int port = 6379; /**
* @param url
* @param port
*/
public JedisPooledFactory(String url, int port) {
super();
this.url = url;
this.port = port;
} /**
* @see org.apache.commons.pool2.BasePooledObjectFactory#create()
*/
@Override
public Jedis create() throws Exception {
Assert.notNull(url);
return new Jedis(url, port);
} @Override
public boolean validateObject(PooledObject<Jedis> p) {
//if closed,validate error
if(!p.getObject().isConnected()){
return false;
}
return super.validateObject(p);
} @Override
public void destroyObject(PooledObject<Jedis> p) throws Exception {
// close the connection
p.getObject().close();
super.destroyObject(p);
} /**
* @see org.apache.commons.pool2.BasePooledObjectFactory#wrap(java.lang.Object)
*/
@Override
public PooledObject<Jedis> wrap(Jedis obj) {
return new DefaultPooledObject<Jedis>(obj);
}
}
jedis连接工厂
我们可以看到,这个工厂主要是实现了对Jedis连接对象的生命周期的管理,结合Jedis来说明定义的行为:1.怎么创建Jedis连接(比如连接池中jedis连接不够用的时候)。2.怎么销毁对象(比如连接池中大量空闲连接)。3.每次borrow/return Jedis连接的时候,判断jedis连接的有效性。,如果无效就将该对象销毁,然后重新borrow。4.wrap,将任意对象池化的时候,需要让对象支持一些对象池中的特定的一些特性,比如在对象池中,如果空闲对象超过了阀值并且超过了一定的时间,borrow的时候就清除掉对象,这个意味着池中的对象需要支持池化后的一些特性,主要是与生命状态相关的特性。那么这个wrap就是对象的包装类,有个默认的实现:
DefaultPooledObject
我们现在要开始使用Jedis连接工厂了
public class RedisClientImpl implements InitializingBean, RedisClient { private final static Logger logger = LoggerFactory.getLogger(RedisClientImpl.class); /** redis url */
private String url = null;
private int port = 6379; /**
* The Max wait time.
*/
private int maxWaitTime = 1000; /** jedis池化 */
private GenericObjectPool<Jedis> jedisPool = null; /**
* Instantiates a new Redis client.
*/
public RedisClientImpl() {
} /**
* Instantiates a new Redis client.
*
* @param url the url
* @param port the port
*/
public RedisClientImpl(String url, int port){
setPort(port);
setUrl(url);
} /**
* 不带异常的put数据
* @param key
* @param value
*/
public void putobjWithExp(String key, Object value) {
Jedis jedis = null;
try {
jedis = getJedis();
jedis.set(key, JSON.toJSONString(value));
} catch (Exception e) {
logger.error("获取Jedis异常", e);
} finally {
if (jedis != null) {
returnJedis(jedis);
}
}
} /**
* 获取jedis
* @return 从池中获取jedis
* @throws Exception
*/
private Jedis getJedis() throws Exception {
LoggerUtils.info(logger, "borrow jedis,borrowwed=", jedisPool.getNumActive(), ",maxTotal=",
jedisPool.getMaxTotal());
return jedisPool.borrowObject(maxWaitTime);
} /**
* 归还jedis
* @param jedis
*/
private void returnJedis(Jedis jedis) {
LoggerUtils.info(logger, "return jedis,borrowwed=", jedisPool.getNumActive(), ",maxTotal=",
jedisPool.getMaxTotal());
jedisPool.returnObject(jedis);
} /**
* Setter method for property <tt>port</tt>.
*
* @param port value to be assigned to property port
*/
public void setPort(int port) {
this.port = port;
} /**
* Setter method for property <tt>url</tt>.
*
* @param url value to be assigned to property url
*/
public void setUrl(String url) {
this.url = url;
} @Override
public void afterPropertiesSet() throws Exception {
LoggerUtils.info(logger, "开始初始化jedis池,url=", url, ",port=", port);
Assert.notNull(url);
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
poolConfig.setBlockWhenExhausted(true);
poolConfig.setMaxWaitMillis(100);
poolConfig.setLifo(false);
poolConfig.setMaxIdle(50);//// 最大空闲连接数
poolConfig.setMinIdle(20);// 最小空闲连接数
poolConfig.setMaxTotal(500);// 整个池的最大值,最大连接数
poolConfig.setTestOnBorrow(true);
poolConfig.setTestOnCreate(true);
poolConfig.setTestOnReturn(true);
poolConfig.setTestWhileIdle(false);
jedisPool = new GenericObjectPool<>(new JedisPooledFactory(url, port), poolConfig);
}
redis带连接池的客户端
这里jedis采用单机的形式。
首先是afterPropertiesSet方法,这里对jedis连接池做了一些配置,比如池的大小,borrow jedis连接的时候等待时间(borrow的时候采用的乐观锁),池中空闲对象超过多少的时候,return连接直接就销毁。。。等等
然后是putobjWithExp方法,这里首先是从池中borrow一个链接,如果池中没有的话,commons-pool会自动创建。然后获取到连接了以后,调用下jedis的set方法,将数据保存。
这里采用的是fastJson来做序列化的,保存的内容也是String格式的。 而tair是支持自定义序列化工具的,而且它的序列化是
最后,将jedis连接归还到pool去就好啦。
除了对象复用以外,其实还没有提到一个很重要的使用对象池的原因:
对于连接池的场景而言,连接是有限的资源,不采用池化,那么无法对资源的分配进行管理。
打个比方,不采用连接池,每个请求进来生成一个连接,那么如果突然某个业务请求量递增,直接导致连接数都被该系统占用了。但是采用了连接池,不仅仅可以对象复用,同时还能做资源的管控。
测试:
测试环境:10个线程,同时采用jedispool和非jedispool的方式向redis put数据,put的数据一样,一共put 50000次,以下是测试结果。
带jedis连接池的--->任务执行完毕,执行时间5055ms,共有50000个任务,执行异常0次
不带连接池的--->任务执行完毕,执行时间14654ms,共有50000个任务,执行异常0次
效果还是很明显的。后续还可以增加对连接池的定时任务监控等~~~
Redis客户端连接池的更多相关文章
- 由浅入深了解Thrift之客户端连接池化续
前文<由浅入深了解Thrift之客户端连接池化>中我们已经实现了服务调用端 连接的池化,实现的过于简陋,离实际的项目运用还很遥远.本文将在进一步改造,主要是两方面:1.服务端如何注册多个服 ...
- Redis客户端连接以及持久化数据(三)
0.Redis目录结构 1)Redis介绍及部署在CentOS7上(一) 2)Redis指令与数据结构(二) 3)Redis客户端连接以及持久化数据(三) 4)Redis高可用之主从复制实践(四) 5 ...
- 使用redis客户端连接windows和linux下的redis并解决无法连接redis的问题
搭建环境:linux是centos7.4(请注意centos7以下版本的防火墙跟centos7以上的不同,使用redis客户端连接redis时会有区别,建议使用centos7以上版本) 一.下载red ...
- C#两大知名Redis客户端连接哨兵集群的姿势
前言 前面利用<Docker-Compose搭建Redis高可用哨兵集群>, 我们的思路是将Redis.Sentinel.Redis Client App链接到同一个网桥网络,这个网桥内的 ...
- redis客户端连接异常
本文参考:http://mdba.cn/2015/04/02/redistwemproxy-%e5%ae%a2%e6%88%b7%e7%ab%af%e8%bf%9e%e6%8e%a5%e5%bc%82 ...
- Redis 客户端连接
Redis 通过监听一个 TCP 端口或者 Unix socket 的方式来接收来自客户端的连接,当一个连接建立后,Redis 内部会进行以下一些操作: 首先,客户端 socket 会被设置为非阻 ...
- Redis客户端连接
Redis接受上配置监听TCP端口和Unix套接字客户端的连接,如果启用.当一个新的客户端连接被接受,如有以下操作进行: 客户端套接字置于非阻塞状态,因为Redis的使用复用和非阻塞I/O操作. TC ...
- redis客户端连接到服务器的步骤
和大多数客户端连接到服务器一样,redis-cli连接到服务器也主要分为两个阶段,请求连接阶段和数据传送阶段.具体来讲redis-cli做的事情有: 1.以socket方式建立连接: 2,选择相应的数 ...
- Swoole2.0协程客户端连接池的实现
Swoole2.0官方默认的实例是短连接的,在请求处理完毕后就会切断redis或mysql的连接.实际项目可以使用连接池实现复用. 实现原理也很简单,使用SplQueue,在请求到来时判断资源队列中是 ...
随机推荐
- 使用数据泵+dblink迁移数据库,适用于本地空间不足的情况
col name for a40 select name,locks,pins from v$db_object_cache where locks > 0 and pins > 0 an ...
- eclipse护眼颜色和字体大小设置
♣eclipse护眼颜色和关键字颜色设置 ♣eclipse字体大小设置(包括jsp , .xml ,.java) 1.Eclipse字体大小调整: 窗口(Window)-首选项(Preferences ...
- php套件 wampserver 常见问题
安装问题 dll 丢失 今天手贱升级了win10,重新安装了一下php的套件,提示有几个组件找不到,其中有一个msvcr100.dll丢失. google了一下: youtube视频:Windows ...
- GCD死锁 多线程
NSLog("); dispatch_sync(dispatch_get_main_queue(), ^{ // sync同步 main串行 // 同步,异步--线程 同步-主线程 // m ...
- List与Json的相互转换
List 与 Json 的相互转换,需要使用到6个jar包,如果不引用这些jar包,需要写的代码量比较多,这里暂时记录的是使用jar包来进行转换的情况. 下面图片是测试demo的结构,仅供参考,需要注 ...
- [转]搬瓦工教程之九:通过Net-Speeder为搬瓦工提升网速
搬瓦工教程之九:通过Net-Speeder为搬瓦工提升网速 有的同学反映自己的搬瓦工速度慢,丢包率高.这其实和你的网络服务提供商有关.据我所知一部分上海电信的同学就有这种问题.那么碰到了坑爹的网络服务 ...
- Quartz2D 编程指南(三)渐变、透明层 、数据管理
概览 图形上下文 路径 颜色与颜色空间 变换 图案 阴影 渐变 透明层 Quartz 2D 中的数据管理 位图与图像遮罩 CoreGraphics 绘制 Layer 渐变 简介 渐变是从一个颜色到另外 ...
- Eexcel批量设定单元格条件格式
最近在整理excel项目记录,觉得有些技巧还是值得拥有,记录备份下,以防将来使用: [工具/原料] excel(office或wps) [方法/步骤] 1.建立一个追踪表如下: 2.选择状态列,建立数 ...
- sqlite3 命令
然后使用下列操作打开并进入数据库 1 2 3 $./adb shell $cd sdcard/path/subdir $sqlite3 dsxniubility.db 终端内进入数据库一般操作也就是 ...
- vim - char code and charset
In normal mode, type ga to display the decimal and hex values for the character under the cursor, or ...