redis中的watch java_redis----java操作redis

添加jar包

redis.clients

jedis

3.0.1

简单操作

public class Myredis {

public static void main(String[] args) {

Jedis jedis = new Jedis("127.0.0.1", 6379);

jedis.set("v5","k5");

//事务

Transaction multi = jedis.multi();

multi.set("k1","k2");

multi.exec();

}

}

首先不使用watch

public class Myredis {

public static Boolean transfer(int transfernum){

Jedis jedis = new Jedis("127.0.0.1", 6379);

Transaction multi = jedis.multi();

multi.decrBy("num",transfernum);

multi.exec();

return true;

}

public static void main(String[] args) {

Myredis.transfer(20);

}

}

测试

1、首先debug代码,停留在了exec()之前,还没有提交事务

f3148ce8aa6f5f72e9bb6affd0e871ae.png

2、查询到redis中的num为80

a367ce98148d18a23f8832cc298dfd7b.png

3、手动修改num为200

06e6a22411cbb27d6cc444ddd73007d0.png

4、放行代码,执行java全部的代码

5、发现num是按照200来有重新计算的

e4cfe2ab37d0eabf7fee6540f5c5f6c2.png

6、总结,当执行exec()时,消息队列中的代码才真正被执行,注意redis不保证原子性,进入队列的代码分开执行,不同队列的代码执行报错对其他队列中的代码不影响。

所以在执行exec()过程中,如果出现num被修改了,就会发生数据不对的问题,所以我们需要使用watch

乐观锁

使用watch来监听key

public class Myredis {

private static int x = 10;

private static Jedis jedis = new Jedis("127.0.0.1", 6379);

public static Boolean transfer(Long transfernum){

jedis.watch("num");

Transaction multi = jedis.multi();

multi.decrBy("num",transfernum);

List exec = multi.exec();

//如果返回值是null,表示事务被终止

System.out.println(exec);

jedis.close();

return true;

}

public static void main(String[] args) {

Myredis.transfer(20L);

}

}

测试结果,如果外部对num进行修改,watch就会监听到,直接终止事务提交.

可以采用循环来重复获取新的值(可以设置可以循环次数,如果在循环次数内没有成功,就退出)

public class Myredis {

private static int x = 10;

private static Jedis jedis = new Jedis("127.0.0.1", 6379);

public static Boolean transfer(Long transfernum){

jedis.watch("num");

List exec = null;

while (exec==null||exec.size()==0){

Transaction multi = jedis.multi();

multi.decrBy("num",transfernum);

exec = multi.exec();

}

return true;

}

public static void main(String[] args) {

Myredis.transfer(20L);

}

}

读写分离

public class Myredis {

public static void main(String[] args) {

Jedis jedis_Master = new Jedis("127.0.0.1", 6379);

Jedis jedis_Slave = new Jedis("127.0.0.1", 6379);

jedis_Slave.slaveof("127.0.0.1",6379);

//主机用来写

jedis_Master.set("k1","v1");

//从机用来读

String k1 = jedis_Slave.get("k1");

}

}

JedisPool

JedisPool的配置参数大部分是由JedisPoolConfig的对应项来赋值的。

maxActive:控制一个pool可分配多少个jedis实例,通过pool.getResource()来获取;如果赋值为-1,则表示不限制;如果pool已经分配了maxActiye个jedis实例,则此时pool的状态为exhausted。

maxIdle:控制一个pool最少有多少个状态为idle(空闲)的jedis实例;whenExhaustedAction:表示当pool中的jedis实例都被allocated完时,pool要采取的操作;默认有三种。

WHEN_EXHAUSTED_FAIL-->表示无jedis实例时,直接抛出NoSuchElementException;WHEN_EXHAUSTED_BLOCK-->则表示阻塞住,或者达到maxWait时抛出JedisConnectionException;WHEN_EXHAUSTED_GROW-->则表示新建一个jedis实例,也就说设置的maxActive无用;

maxwait:表示当borrow一个jedis实例时,最大的等待时间,如果超过等待时间,则直接抛jedisConnectionException;

testOnBorrow:获得一个jedis实例的时候是否检查连接可用性(ping());如果为true,则得到的jedis实例均是可用的;

testonReturn:return 一个jedis实例给pool时,是否检查连接可用性(ping());

简单版

public class Myredis {

public static void main(String[] args) {

JedisPool jedisPoll = MyJedisPoll.getJedisPoll();

Jedis jedis = jedisPoll.getResource();

MyJedisPoll.release(jedis);

}

}

class MyJedisPoll{

private static volatile JedisPool jedisPool=null;

//获取连接池

public static JedisPool getJedisPoll(){

if (jedisPool==null){

synchronized (MyJedisPoll.class){

if (jedisPool==null){

JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();

jedisPoolConfig.setMaxTotal(1000);

jedisPoolConfig.setMaxIdle(4);

jedisPoolConfig.setMaxWaitMillis(100*1000);

jedisPoolConfig.setTestOnBorrow(true);

jedisPool = new JedisPool(jedisPoolConfig, "127.0.0.1", 6379);

}

}

return jedisPool;

}else {

return jedisPool;

}

}

//释放连接

public static void release(Jedis jedis){

//如果连接池已经关闭了,则返回-1,最大活跃数不会超过MAX_ACTIVE,最大空闲数不会超过MAX_OLDE

System.out.println(jedisPool.getNumWaiters()+"链接归还前活跃数:"+jedisPool.getNumActive()+"空闲连接数:"+jedisPool.getNumIdle());

jedis.close();

System.out.println(jedisPool.getNumWaiters()+"链接归还后活跃数:"+jedisPool.getNumActive()+"空闲连接数:"+jedisPool.getNumIdle());

}

}

复杂版本

下面的版本我觉得有一点需要改进getJedis的时候锁加的位置不对!只需要锁住实例化initpoll,注意双重判断(性能问题)

public class RedisConnectPollUtil{

private static final Log LOG = LogFactory.getLog(RedisConnectPollUtil.class);

//redis获取链接的并发锁

private static ReentrantLock redisPollLock= new ReentrantLock();

//连接redis实例的ip

private static final String REDIS_ADDRESS = "localhost";

//连接redis实例的端口

private static final int PORT = 6379;

//多线程环境中,连接实例的最大数,如果设为-1则无上线,建议设置,否则有可能导致资源耗尽

private static final int MAX_ACTIVE = 8;

//在多线程环境中,连接池中最大空闲连接数,单线程环境没有实际意义

private static final int MAX_OLDE = 4;

//在多线程环境中,连接池中最小空闲连接数

private static final int MIN_OLDE = 1;

//多长时间将空闲线程进行回收,单位毫秒

private static final int METM = 2000;

//对象空闲多久后逐出, 当空闲时间>该值 且 空闲连接>最大空闲数 时直接逐出,不再根据MinEvictableIdleTimeMillis判断  (默认逐出策略)

private static final int SMETM = 2000;

//逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1,只有运行了此线程,MIN_OLDE METM/SMETM才会起作用

private static final int TBERM = 1000;

//当连接池中连接不够用时,等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。如果超过等待时间,则直接抛出JedisConnectionException;

private static final int MAX_WAIT = 1000;

//超时时间,单位毫秒

private static final int TIME_OUT = 5000;

//在借用一个jedis连接实例时,是否提前进行有效性确认操作;如果为true,则得到的jedis实例均是可用的;

private static final boolean TEST_ON_BORROW = false;

//连接池实例

private static JedisPool jedisPool = null;

//初始化连接池,有好多重载的构造函数,根据自己业务实际需要来实例化JedisPoll

private static void initPoll() {

try {

JedisPoolConfig config = new JedisPoolConfig();

config.setMaxTotal(MAX_ACTIVE);

config.setMaxIdle(MAX_OLDE);

config.setMaxWaitMillis(MAX_WAIT);

config.setTestOnBorrow(TEST_ON_BORROW);

config.setMinIdle(MIN_OLDE);

//            config.setMinEvictableIdleTimeMillis(METM);

config.setSoftMinEvictableIdleTimeMillis(SMETM);

config.setTimeBetweenEvictionRunsMillis(TBERM);

jedisPool = new JedisPool(config, REDIS_ADDRESS, PORT, TIME_OUT);

} catch (Exception e) {

LOG.error("initial JedisPoll fail:",e);

}

}

//获取jedis连接实例

public static Jedis getJedis() {

redisPollLock.lock();

if(jedisPool == null) {

initPoll();

}

Jedis jedis = null;

try {

if(jedisPool != null) {

jedis = jedisPool.getResource();

}

} catch (Exception e) {

LOG.error("get jedis fail:",e);

}finally {

redisPollLock.unlock();

}

return jedis;

}

//归还jedis实例,2.9版本后jedisPool.returnResource(jedis);过期,被close替代,源码如下

/*

@Override

public void close() {

if (dataSource != null) {

if (client.isBroken()) {

this.dataSource.returnBrokenResource(this);

} else {

this.dataSource.returnResource(this);

}

} else {

client.close();

}

}

*/

//如果每次获取了jedis连接后不进行归还,redis不会自动回收,那么获取的最多连接数量为MAX_ACTIVE

//超出数量则会抛出异常redis.clients.jedis.exceptions.JedisException: Could not get a resource from the pool

public static void returnSource(Jedis jedis) {

if(jedis != null) {

//如果连接池已经关闭了,则返回-1,最大活跃数不会超过MAX_ACTIVE,最大空闲数不会超过MAX_OLDE

System.out.println(jedisPool.getNumWaiters()+"链接归还前活跃数:"+jedisPool.getNumActive()+"空闲连接数:"+jedisPool.getNumIdle());

jedis.close();

System.out.println(jedisPool.getNumWaiters()+"链接归还后活跃数:"+jedisPool.getNumActive()+"空闲连接数:"+jedisPool.getNumIdle());

}

}

private static int k = 0;

public static void main(String[] args) {

for(int i=0;i<20;i++) {

new Thread(new Runnable() {

@Override

public void run() {

Jedis jedis = getJedis();

System.out.println("第"+(k++)+"次"+jedis.lpop("pageList"));

returnSource(jedis);

//判断此连接是否还有效,有效返回true,否则返回false

//连接归还后,将不可用,会抛出redis.clients.jedis.exceptions.JedisConnectionException: Unexpected end of stream.

if(!jedis.isConnected()) {

jedis.lpop("pageList");

}

//                    jedisPool.close(); jedisPoll关闭后将导致池不可用

//                    System.out.println("jedispoll是否关闭了?"+jedisPool.isClosed());

}

}).start();

}

try {

//主线程等待一定时间,否则会发生线程执行时效错乱问题

Thread.currentThread().sleep(15000);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

System.out.println(jedisPool.getNumWaiters()+"最终链接归还后活跃数:"+jedisPool.getNumActive()+"空闲连接数:"+jedisPool.getNumIdle());

destroy();

}

//在运用正常运行时,通常是不会手动调用jedisPool.close();池内将保持最大空闲数的连接,如果设置了逐出策略

//那么池内就会保留最小空闲连接,如果应用突然关闭,我们需要在bean销毁时将连接池销毁.

public static void destroy(){

if(jedisPool != null) {

try {

jedisPool.destroy();

} catch (Exception e) {

LOG.error("jedisPool destroy fail ",e);

}

}

}

}

最后任务

之后有时间需要看源码了解watch是如何实现监控的,和exec如果实现执行队列中的代码的......

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值