redis学习-Jedis 使用

Redis实战:Jedis连接池与Pipeline使用详解
本文介绍了Redis客户端Jedis的使用,包括如何通过Resp序列化协议建立TCP连接,详细讲解了在Springboot环境下和非Spring环境下配置Jedis连接池的方法,以及展示了Jedis的Pipline特性和Lua脚本的应用。内容涵盖maven依赖、配置类编写、单例模式创建JedisPool及服务封装。

[toc]

[toc]

本系列文章整理摘抄自

客户端怎么和 Redis 服务器连接?

客户端和 Redis 服务器的通信是 建立在 TCP 连接的基础上的。

并且 Redis 制定了 RESP 序列化协议,是一个简单地通信约定。

Resp序列化协议

*<参数数量>\r\n$<参数1的字节数量>\r\n<参数1>\r\n$<参数2的字节数量>\r\n<参数2>\r\n

来给可视化一下:

1
2
3
4
5
*<参数数量>\r\n
$<参数1的字节数量>\r\n
<参数1>\r\n
$<参数2的字节数量>\r\n
<参数2>\r\n

其他可以参考该书章节。

Jedis 连接池的使用

简单的API 介绍

获取 jedis连接

1
2
3
4
5
6
7
8
9
10
11
12
13
Jedis jedis = new Jedis("127.0.0.1", 6379);

Jedis jedis = null;
try {
jedis = new Jedis("127.0.0.1", 6379);

} catch (Exception e) {
logger.error(e.getMessage(),e);
} finally {
if (jedis != null) {
jedis.close();
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//  String
jedis.set("key", "value");
jedis.get("key")


// hset -字典
jedis.set("hash", "key1", "value1");
jedis.set("hash", "key2", "value2");
jedis.get("key1")

// list -列表
jedis.rpush("mylist", "1");


// set -集合
jedis.sadd("set", "aaa");

....

springboot 环境下的使用

1. maven 依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.1.0</version>
</dependency>


<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.4.2</version>
</dependency>

<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
</dependency>

2. 代码实现

配置类 -> 从 application.preperties 读取配置项,并且配置

1
2
3
4
5
6
7
8
@Component
@ConfigurationProperties(prefix = "redis")
public class RedisConfig {
// 私有属性

// 配置项
get... set...
}

poolFactory 工厂类创建 pool

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Component
public class RedisPoolFactory {

// 注入配置项
@Autowired
RedisConfig redisConfig;

// Bean注解 : 根据方法创建对象,类型是JedisPool
@Bean
public JedisPool JedisPoolFactory(){

JedisPoolConfig poolConfig = new JedisPoolConfig();

// 各种配置
poolConfig.setMaxIdle(redisConfig.getPoolMaxIdle());
poolConfig.setMaxTotal(redisConfig.getPoolMaxTotal());
poolConfig.setMaxWaitMillis(redisConfig.getPoolMaxTotal()*1000);

JedisPool jedisPool = new JedisPool(poolConfig, redisConfig.getHost(), redisConfig.getPort(), redisConfig.getTimeout()*1000, redisConfig.getPassword(), 0);

return jedisPool;
}

Redis 服务类 开始封装各种服务

当然,也要为服务模块化,比如 RedisUserService, RedisMiaoshaService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Service
public class RedisService {
// 注入,@Bean 产生的jedisPool
@Autowired
JedisPool jedisPool;

public <T> boolean set(IProfixForKey prefix, String key, T value){

Jedis resource = null;
try{
// 拿到连接
resource = jedisPool.getResource();

// 封装一下key, 加上特点的头信息,例如: dbName:tableName:id
String strValue = beanToString(value);

// 生成key
String realKey = prefix.getPrefix() + key;

if (strValue == null || strValue.length() <= 0){
return false;
}

// 设置
resource.set(realKey, strValue);

return true;
}finally {
returnToPool(resource);
}

}
}

非 spring 环境下的使用

举个例子,不知道合不合适。

单例模式 来创建 JedisPool

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public class JedisFactory {

private volatile static JedisPool jedisPool;

private volatile static JedisPoolConfig poolConfig;
private volatile static String ip;
private volatile static int port;
private volatile static int timeout;
private volatile static String password;
private volatile static int database;

private JedisFactory() {

//JedisPoolConfig poolConfig, String ip, int port, int timeout, String password, int database
/* apache common-pool 工具
*
* JedisPoolConfig
* */
this.jedisPool = new JedisPool(poolConfig, ip, port, timeout , password, 0);
}


public static JedisPool getJedisPool(){

if (jedisPool == null){

synchronized (JedisFactory.class){

if (jedisPool == null){
jedisPool = new JedisPool(JedisFactory.poolConfig, JedisFactory.ip, JedisFactory.port,
JedisFactory.timeout, JedisFactory.password, JedisFactory.database);
}
}
}

return jedisPool;
}

public static void setJedisPoolConfig(JedisPoolConfig poolConfig, String ip, int port, int timeout, String password, int database){

System.out.println(" 配置jedis 参数...");
JedisFactory.poolConfig = poolConfig;
JedisFactory.ip = ip;
JedisFactory.port = port;
JedisFactory.timeout = timeout * 1000;
JedisFactory.password = password;
JedisFactory.database = database;

}
}

JedisService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

package top.elgong.jedis;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

/*
*
* 解决redis 用到的
* */
public class JedisService {

/**
*
* @param key
* @param value
*/
public void set(String key, String value){
Jedis resource = null;
try{
JedisPool jedisPool = JedisFactory.getJedisPool();

resource = jedisPool.getResource();

resource.set(key, value);

}catch (Exception e){
e.printStackTrace();
}finally {

// 送回连接池中
if (resource != null){
resource.close(); // close 就是送回池子
}
}
}

/*
*
* */
public String get(String key){
Jedis resource = null;
String ret = null;
try{
JedisPool jedisPool = JedisFactory.getJedisPool();
resource = jedisPool.getResource();

ret = resource.get(key);


}catch (Exception e){
e.printStackTrace();
}finally {
// 送回连接池中
if (resource != null){
resource.close(); // close 就是送回池子
}
}

return ret;
}

}

test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package top.elgong.jedis;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPoolConfig;

import java.time.LocalDateTime;

public class Test {

public static JedisService jedisService = new JedisService();
public static void main(String[] args) {

JedisPoolConfig poolConfig = new JedisPoolConfig();
String ip = "121.41.111.45";
int port = 6379;
int timeout = 300;
String password = "Gelqq666%";
int database = 0;

JedisFactory.setJedisPoolConfig(poolConfig, ip, port, timeout, password, database);

jedisService.set("leetcode-java:jedis:test:key1", "haha-" + LocalDateTime.now().toString());

String s = jedisService.get("leetcode-java:jedis:test:key1");

System.out.println(s);
}
}

Jedis Pipline 的使用

1
2
3
4
5
6
7
8
9
10
11
12
Jedis jedis = new Jedis("127.0.0.1");

// 1)生成pipeline对象
Pipeline pipeline = jedis.pipelined();

// 2)pipeline执行命令, 注意此时命令并未真正执行
for (String key : keys) {
pipeline.del(key);
}

// 3)执行命令
pipeline.sync();

Jedis Lua 脚本

待补充。。

Jedis使用总结 前段时间细节的了解了Jedis使用Jedisredis的java版本的客户端实现。 本文做个总结,主要分享如下内容: 【pipeline】【分布式的id生成器】【分布式锁【watch】【multi】】【redis分布式】 好了,一个一个来。 一、 Pipeline 官方的说明是:starts a pipeline,which is a very efficient way to send lots of command and read all the responses when you finish sending them。简单点说pipeline适用于批处理。当有大量的操作需要一次性执行的时候,可以用管道。 示例: Jedis jedis = new Jedis(String, int); Pipeline p = jedis.pipelined(); p.set(key,value);//每个操作都发送请求给redis-server p.get(key,value); p.sync();//这段代码获取所有的response 这里我进行了20w次连续操作(10w读,10w写),不用pipeline耗时:187242ms,用pipeline耗时:1188ms,可见使用管道后的性能上了一个台阶。看了代码了解到,管道通过一次性写入请求,然后一次性读取响应。也就是说jedis是:request response,request response,...;pipeline则是:request request... response response的方式。这样无需每次请求都等待server端的响应。 二、 跨jvm的id生成器 谈到这个话题,首先要知道redis-server端是单线程来处理client端的请求的。 这样来实现一个id生成器就非常简单了,只要简单的调用jdeis.incr(key);就搞定了。 你或许会问,incr是原子操作吗,能保证不会出现并发问题吗,不是说了吗,server端是单线程处理请求的。 三、 【跨jvm的锁实现【watch】【multi】】 首先说下这个问题的使用场景,有些时候我们业务逻辑是在不同的jvm进程甚至是不同的物理机上的jvm处理的。这样如何来实现不同jvm上的同步问题呢,其实我们可以基于redis来实现一个锁。 具体事务和监听请参考文章:redis学习笔记之事务 暂时找到三种实现方式: 1. 通过jedis.setnx(key,value)实现 import java.util.Random; import org.apache.commons.pool.impl.GenericObjectPool.Config; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.Transaction; /** * @author Teaey */ public class RedisLock { //加锁标志 public static final String LOCKED = "TRUE"; public static final long ONE_MILLI_NANOS = 1000000L; //默认超时时间(毫秒) public static final long DEFAULT_TIME_OUT = 3000; public static JedisPool pool; public static final Random r = new Random(); //锁的超时时间(秒),过期删除 public static final int EXPIRE = 5 * 60; static { pool = new JedisPool(new Config(), "host", 6379); } private Jedis jedis; private String key; //锁状态标志 private boolean locked = false; public RedisLock(String key) { this.key = key; this.jedis = pool.getResource(); } public boolean lock(long timeout) { long nano = System.nanoTime(); timeout *= ONE_MILLI_NANOS; try { while ((System.nanoTime() - nano) < timeout) { if (jedis.setnx(key, LOCKED) == 1) { jedis.expire(key, EXPIRE); locked = true; return locked; } // 短暂休眠,nano避免出现活锁 Thread.sleep(3, r.nextInt(500)); } } catch (Exception e) { } return false; } public boolean lock() { return lock(DEFAULT_TIME_OUT); } // 无论是否加锁成功,必须调用 public void unlock() { try { if (locked) jedis.del(key); } finally { pool.returnResource(jedis); } } } 2. 通过事务(multi)实现 由于采纳第一张方法,第二种跟第三种实现只贴了关键代码,望谅解。^_^ public boolean lock_2(long timeout) { long nano = System.nanoTime(); timeout *= ONE_MILLI_NANOS; try { while ((System.nanoTime() - nano) < timeout) { Transaction t = jedis.multi(); // 开启事务,当server端收到multi指令 // 会将该client的命令放入一个队列,然后依次执行,知道收到exec指令 t.getSet(key, LOCKED); t.expire(key, EXPIRE); String ret = (String) t.exec().get(0); if (ret == null || ret.equals("UNLOCK")) { return true; } // 短暂休眠,nano避免出现活锁 Thread.sleep(3, r.nextInt(500)); } } catch (Exception e) { } return false; } 3. 通过事务+监听实现 public boolean lock_3(long timeout) { long nano = System.nanoTime(); timeout *= ONE_MILLI_NANOS; try { while ((System.nanoTime() - nano) < timeout) { jedis.watch(key); // 开启watch之后,如果key的值被修改,则事务失败,exec方法返回null String value = jedis.get(key); if (value == null || value.equals("UNLOCK")) { Transaction t = jedis.multi(); t.setex(key, EXPIRE, LOCKED); if (t.exec() != null) { return true; } } jedis.unwatch(); // 短暂休眠,nano避免出现活锁 Thread.sleep(3, r.nextInt(500)); } } catch (Exception e) { } return false; } 最终采用第一种实现,因为加锁只需发送一个请求,效率最高。 四、 【redis分布式】 最后一个话题,jedis的分布式。在jedis的源码里发现了两种hash算法(MD5,MURMUR Hash(默认)),也可以自己实现redis.clients.util.Hashing接口扩展。 List hosts = new ArrayList(); //server1 JedisShardInfo host1 = new JedisShardInfo("", 6380, 2000); //server2 JedisShardInfo host2 = new JedisShardInfo("", 6381, 2000); hosts.add(host1); hosts.add(host2); ShardedJedis jedis = new ShardedJedis(hosts); jedis.set("key", "");
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值