翻译一篇关于jedis的文章
翻译 自 http://www.baeldung.com/jedis-java-redis-client-library
Intro to Jedis – the Java Redis Client Library
1. Overview
2. Why Jedis?
3. Maven Dependencies
1
2
3
4
5
|
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.8.1</version>
</dependency>
|
4. Redis Installation
1
|
Jedis jedis = new Jedis();
|
5. Redis Data Structures
5.1. Strings
1
2
|
jedis.set("events/city/rome", "32,15,223,828");
String cachedResponse = jedis.get("events/city/rome");
|
5.2. Lists
1
2
3
4
|
jedis.lpush("queue#tasks", "firstTask");
jedis.lpush("queue#tasks", "secondTask");
String task = jedis.rpop("queue#tasks");
|
5.3. Sets
1
2
3
4
5
6
|
jedis.sadd("nicknames", "nickname#1");
jedis.sadd("nicknames", "nickname#2");
jedis.sadd("nicknames", "nickname#1");
Set<String> nicknames = jedis.smembers("nicknames");
boolean exists = jedis.sismember("nicknames", "nickname#1");
|
5.4. Hashes
1
2
3
4
5
6
7
|
jedis.hset("user#1", "name", "Peter");
jedis.hset("user#1", "job", "politician");
String name = jedis.hget("user#1", "name");
Map<String, String> fields = jedis.hgetAll("user#1");
String job = fields.get("job");
|
5.5. Sorted Sets
1
|
|
1
2
3
4
5
6
7
8
9
10
11
12
|
Map<String, Double> scores = new HashMap<>();
scores.put("PlayerOne", 3000.0);
scores.put("PlayerTwo", 1500.0);
scores.put("PlayerThree", 8200.0);
scores.keySet().forEach(player -> {
jedis.zadd("ranking", scores.get(player), player);
});
String player = jedis.zrevrange("ranking", 0, 1).iterator().next();
long rank = jedis.zrevrank("ranking", "PlayerOne");
|
6. Transactions
1
2
3
4
5
6
7
8
|
String friendsPrefix = "friends#";
String userOneId = "4352523";
String userTwoId = "5552321";
Transaction t = jedis.multi();
t.sadd(friendsPrefix + userOneId, userTwoId);
t.sadd(friendsPrefix + userTwoId, userOneId);
t.exec();
|
1
|
jedis.watch("friends#deleted#" + userOneId);
|
7. Pipelining
1
2
3
4
5
6
7
8
9
10
11
12
13
|
String userOneId = "4352523";
String userTwoId = "4849888";
Pipeline p = jedis.pipelined();
p.sadd("searched#" + userOneId, "paris");
p.zadd("ranking", 126, userOneId);
p.zadd("ranking", 325, userTwoId);
Response<Boolean> pipeExists = p.sismember("searched#" + userOneId, "paris");
Response<Set<String>> pipeRanking = p.zrange("ranking", 0, -1);
p.sync();
String exists = pipeExists.get();
Set<String> ranking = pipeRanking.get();
|
8. Publish/Subscribe
8.1. Subscriber
1
2
3
4
5
6
7
|
Jedis jSubscriber = new Jedis();
jSubscriber.subscribe(new JedisPubSub() {
@Override
public void onMessage(String channel, String message) {
// handle message
}
}, "channel");
|
8.2. Publisher
1
2
|
Jedis jPublisher = new Jedis();
jPublisher.publish("channel", "test message");
|
9. Connection Pooling
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
final JedisPoolConfig poolConfig = buildPoolConfig();
JedisPool jedisPool = new JedisPool(poolConfig, "localhost");
private JedisPoolConfig buildPoolConfig() {
final JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(128);
poolConfig.setMaxIdle(128);
poolConfig.setMinIdle(16);
poolConfig.setTestOnBorrow(true);
poolConfig.setTestOnReturn(true);
poolConfig.setTestWhileIdle(true);
poolConfig.setMinEvictableIdleTimeMillis(Duration.ofSeconds(60).toMillis());
poolConfig.setTimeBetweenEvictionRunsMillis(Duration.ofSeconds(30).toMillis());
poolConfig.setNumTestsPerEvictionRun(3);
poolConfig.setBlockWhenExhausted(true);
return poolConfig;
}
|
1
2
3
|
try (Jedis jedis = jedisPool.getResource()) {
// do operations with jedis resource
}
|
10. Redis Cluster
1
2
3
|
try (JedisCluster jedisCluster = new JedisCluster(new HostAndPort("localhost", 6379))) {
// use the jedisCluster resource as if it was a normal Jedis resource
} catch (IOException e) {}
|
11. Conclusion
1 概述这是一遍介绍接jedis的文章,一个redis客户端库操作
redis,这是目前最受欢迎的内存数据存储结构同时也
可以存储在磁盘中。它的驱动是根据键值存储数据结构
而且在使用的时候就想数据库,缓存,信息代理等
首先我们将要描述多种关于jedis的使用情况
在后来的章节,我们尽力在多种数据结构上进行解释 传输,
管道,和发布订阅一些特色。我们包括连接池和redis 集群
2 为什么使用jedisjedis是目前最流行的redis客户端库在他们的官方主页上这里有多种替代选择jedis,但是仅仅只有两个目前值得推荐使用这两种分别是lettuce和Redissson这两种客户端有独特之处例如线程安全,透明重连接处理和一些异步的api,所有的这些都是jedis所缺少的
3 使用Maven Dependencies
让我们开始声明一个独一无二的的dependency ,我们需要在pom.xml中
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.8.1</version>
</dependency>
如果你想查找最先版的redis库,请点击这里
4 Redis 的安装
你应该需要安装和启动一个目前最新版的redis,我们目前运行的是最先稳定版3.2.1在目前时间范围内。但是任何发布的3.X版本都是可以的点击这里获取更多关于redis Linux版和mac的信息。他们都是一些非常相似基础的安装步骤。Windows不是官方的支持,但是这个网站是可以的获取到安装windows版本的在这之后我们直接使用客户端驱动直接连接通过我们的Java代码
Jedis jedis = new Jedis();
这是一个默认的构造方法将会正常启动,除非你将要开始一个服务
在一个非默认端口的在一个远程机器,在这种情况下,
您可以将正确的值作为参数传递给构造函数,
从而正确地配置它。
e.
5 Redis 数据结构
绝大数本地操作命令是支持的,足够的遍历。通常情况先
它们都是共享相同的方法名
5
stringshi最基本的类型在Redis的值中,常用于持久化单一的键值数据类型
jedis.set("events/city/rome", "32,15,223,828");
String cachedResponse = jedis.get("events/city/rome");
这个变量cachedResponse 将会获得这个值32,15,223,828
于此相关的还有过期支持机制我们稍后再谈论。它可以工作很轻量级很快很方便的
去使用缓存层为HTTP请求获取在你的网络应用层和其他的缓存需求
5.2Lists
redis Lists 是一个简单字符串链表,根据插入的顺序使他更好的运行在一些方面
例如消息队列
jedis.lpush("queue#tasks", "firstTask");
jedis.lpush("queue#tasks", "secondTask");
String task = jedis.rpop("queue#tasks");
这个变量测试将会获取的fistTask的值。记住你可以序列化任何对象
和持久化这个对象作为一个字符串。所以信息在一个队列中
可以携带更复杂的数据当获取时
5.3Sets
Redis Sets是一个无序的String类型集合,当你使用它你可以排除重复对象
jedis.sadd("nicknames", "nickname#1");
jedis.sadd("nicknames", "nickname#2");
jedis.sadd("nicknames", "nickname#1");
Set<String> nicknames = jedis.smembers("nicknames");
boolean exists = jedis.sismember("nicknames", "nickname#1");
这个java Set nicknames 的长度将会是2 在第二次加入
nickname#1时被忽略掉了,所以这个exists变量的值将会是true
这个方法sismember使你能够快速的检查某个变量会员是否存在
5.4 Hashes
redis Hashes 是一个映射关系,在String键和String值类型
jedis.hset("user#1", "name", "Peter");
jedis.hset("user#1", "job", "politician");
String name = jedis.hget("user#1", "name");
Map<String, String> fields = jedis.hgetAll("user#1");
String job = fields.get("job");
正如你所看到一样,hashes 是一个 数据类型当它想通过一个对象的单独属性
而不需要遍历整个对象
5.5 Sorted Set
Sorted Sets是像Set一样但是他的每个元素hi向关联有序的。
通常在有序的时候使用它们
Map<String, Double> scores = new HashMap<>();
scores.put("PlayerOne", 3000.0);
scores.put("PlayerTwo", 1500.0);
scores.put("PlayerThree", 8200.0);
scores.keySet().forEach(player -> {
jedis.zadd("ranking", scores.get(player), player);
});
String player = jedis.zrevrange("ranking", 0, 1).iterator().next();
long rank = jedis.zrevrank("ranking", "PlayerOne");
变量player 将持有值player3,因为我们检索的是前1名player ,
他是得分最高的player 。rank变量的值为1,
因为PlayerOne是排名的第二名,排名为零。
6 Transactions
事务保证了原子性和线程的安全操作。这意味着来自其他客户端的请求
在redis的事务里请求绝不会同时请求操作。
String friendsPrefix = "friends#";
String userOneId = "4352523";
String userTwoId = "5552321";
Transaction t = jedis.multi();
t.sadd(friendsPrefix + userOneId, userTwoId);
t.sadd(friendsPrefix + userTwoId, userOneId);
t.exec();
你也可以使事务成功取决于一个特定的键
通过“watching” 它在实例化你的事务之前
jedis.watch("friends#deleted#" + userOneId);
如果这个键改变在事务执行之前,那么那么这个事务将完全的失败
7 Pinelining(管道)
当我们需要去发送多个命令。我们可以将它们打包在一起通过一个request
节约了连接费用通过使用pipeline。这是对于网络优化是很有必要的
当这些操作相互独立,我们可以从中获取有利是,使用这个技术
String userOneId = "4352523";
String userTwoId = "4849888";
Pipeline p = jedis.pipelined();
p.sadd("searched#" + userOneId, "paris");
p.zadd("ranking", 126, userOneId);
p.zadd("ranking", 325, userTwoId);
Response<Boolean> pipeExists = p.sismember("searched#" + userOneId, "paris");
Response<Set<String>> pipeRanking = p.zrange("ranking", 0, -1);
p.sync();
String exists = pipeExists.get();
Set<String> ranking = pipeRanking.get();
注意 我们无法直接访问命令响应 在管道被同步之后,
取而代之的是 我们得到了一个响应实例
我们可以请求底层响应。
8.1 Subscriber
订阅者收听信息来自于一个管道
Jedis jSubscriber = new Jedis();
jSubscriber.subscribe(new JedisPubSub() {
@Override
public void onMessage(String channel, String message) {
// handle message
}
}, "channel");
订阅是一个阻塞方法,你要显示的取消订阅来自JedisPubSub的
我们已经重载了onMessage犯法,但是更多的方法需要去覆盖
8.2. Publisher
Then simply send messages to that same channel from the publisher’s thread:
Jedis jPublisher = new Jedis();
jPublisher.publish("channel", "test message");
8.2 Publisher
就是仅仅是发送一个信息在同一个管道中通过发布者线程
Jedis jPublisher = new Jedis();
jPublisher.publish("channel", "test message");
9 Connnectiong Pooling 连接池
我们都知道使用刚才的方法实例jedishis幼稚的。
在一个真实的方案中,你不想使用一个单一的实例在一个多线程环境下面
因为单一实例在多线程是线程不安全的
幸运的是我们能足够的简单创造一个连接池能使我们重用redis
线程池是线程安全的和足够信赖,只要你返回资源到池中当你运行完它
让我们开始创建一个JedisPool
final JedisPoolConfig poolConfig = buildPoolConfig();
JedisPool jedisPool = new JedisPool(poolConfig, "localhost");
private JedisPoolConfig buildPoolConfig() {
final JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(128);
poolConfig.setMaxIdle(128);
poolConfig.setMinIdle(16);
poolConfig.setTestOnBorrow(true);
poolConfig.setTestOnReturn(true);
poolConfig.setTestWhileIdle(true);
poolConfig.setMinEvictableIdleTimeMillis(Duration.ofSeconds(60).toMillis());
poolConfig.setTimeBetweenEvictionRunsMillis(Duration.ofSeconds(30).toMillis());
poolConfig.setNumTestsPerEvictionRun(3);
poolConfig.setBlockWhenExhausted(true);
return poolConfig;
}
当池实例化是线程安全的,你可以存储它在任何静态的地方。
但是你需要注意的是销毁一个连接池避免泄露,当一个程序关闭
现在我们可以确保我们的池,当应用程序在任何地方需要连接的时候
try (Jedis jedis = jedisPool.getResource()) {
// do operations with jedis resource
}
我们使用Java的try with resource 声明避免去手动的关闭jedis资源
但是当你不能使用这样声明的时候,你也可以关闭资源通过手动的方式在finally语句中
确保我们使用池像我们使用described 在我们的应用程序中当我们
不想去面对令人不愉快的多线程问题。你可以显示的配置
池配置文件,参数去适应你的最新的设置在你的系统中
10 Redis Cluster
这是一个实现提供一个简单可伸缩和高可用性。如果你对它不熟悉,我们鼓励去
去阅读这篇官方文章。我们不会说关于Redis cluster的配置因为这仅仅是那篇文章的很小一部分。
当你阅读完那篇文章,你应该不会出现什么问题在做这些事情的时候
一旦我们将下面的准备好,我们将开始使用它在我们的应用程序中
try (JedisCluster jedisCluster = new JedisCluster(new HostAndPort("localhost", 6379))) {
// use the jedisCluster resource as if it was a normal Jedis resource
} catch (IOException e) {
}
我们需要提供一个ip和一个端口信息给我们一个主节点实例之一。
它会自己自动的发现剩余的实例在我们的集群中
这是一个很重要的功能,但是它不是一个银弹。当使用Redis Cluster
你不能使用事务,管道这两个在Redis中很重要的功能。这两个公共在其他许多的应用程序中
确保数据的一致性
事务是残废的,因为在集群中一个键值将会持久化在多个实例中
操作的原子性和线程的安全性不能保证操作在多个命令的执行在不同的实例机器上
在一些先进的key的创建策略将会确保数据是有效的
当你想持久化到同一个机器上或从同一个机器上获取持久化的内容。
理论上说能够使帮你用事务的成功通过使用底层jedis的实例关于Redis cluster的