1. 概述
目标读者
本文档专供需要Redis客户端API调用的开发人员,适用于具备Java开发经验的开发人员。
简介
Redis客户端通过API调用,向Redis发起命令调用,向Redis或写入或读出数据。
基本概念
● Redis服务端
提供服务的一方。
● Redis单机版服务端
服务端是单个Redis实例提供服务。
● Redis集群版服务端
服务端由多个Redis主实例和若干从实例组成,共同提供服务。
● Redis客户端
发起读写命令。
● Redis 单机版客户端
用来访问Redis单机版服务端的客户端API。
● Redis 单机版客户端(管道)
Redis单机版客户端通过管道的方式向Redis服务端一次发送多条命令,并一次取得返回结果,在其使用范围内能够较大提高单个客户端的性能。
● Redis集群版客户端
用来访问Redis集群版服务端的客户端API。
● Redis集群版客户端(管道)
同单机版客户端的管道模式类似,集群版客户端也可以使用管道模式一次向集群写入多条命令,提高客户端性能。
2. 开发环境准备
操作场景
本开发指南提供了Redis组件的样例代码和常用接口,便于开发者快速熟悉Redis。为了运行Redis组件的样例代码,需要完成下面的操作。
操作步骤
步骤1 确保Redis服务已安装,并正常运行。
步骤2 客户端机器安装Eclipse和JDK程序,安装要求如下:
l Eclipse使用3.0及以上版本(也可使用NetBeans或者IntelliJ等IDE)。
l JDK 1.6及以上。
步骤3 如果需要使用Redis集群,需要保证集群状态为“Good”,集群内各Redis实例运行正常。
图 Redis集群状态
步骤4 在FusionInsight Manager界面,点击“Services > Redis > Download Client”,下载Redis客户端。
在弹出的“File Type”信息提示框中的“Save type”勾选“All Client Files”,单击“OK”开始下载客户端软件包,等待下载完成。
图 下载Redis客户端
步骤5 解压下载的客户端,解压目录中“Redis\redis.client.demo”文件夹即为本文档对应的样例工程。“Redis\redis.client.demo\lib”目录内为工程依赖的jar包。
步骤6 导入样例工程到Eclipse开发环境(如果使用其他IDE请注意转化)。
1. 选择“File > Import > General > Existing Projects into Workspace > Next > Browse”。
显示“浏览文件夹”对话框。
2. 选择需要使用的样例工程文件夹,单击“Finish”。
步骤7 设置Eclipse的文本文件编码格式,解决乱码显示问题。
1. 在Eclipse的菜单栏中,选择“Window > Preferences”。
2. 弹出“Preferences”窗口。
3. 在左边导航上选择“General > Workspace”,在“Text file encoding”区域,选中“Other”,并设置参数值为“UTF-8”,单击“Apply”后,单击“OK”。
3. 开发指引
开发流程
1. 确定使用单机版服务端还是集群版服务端。
2. 确定入口IP地址和端口。
3. 导入样例工程,或新建工程并导入样例工程中的jar包文件。
4. 参照代码样例,客户端接口介绍,以及客户端接口文档进行开发。
说明
Redis角色Role_1对应的端口是22400, Role_2对应的端口是22401,以此类推。
通过FusionInsight Manager,用户可以查看单机版服务端或者集群版服务端的运行状态,实时监控Redis实例或者集群的吞吐量。
图 查看Redis实例状态
图 查看Redis集群状态
4. 安全模式
概述
在安装Redis服务时,可以选择是否安装为安全模式。若没有特别的配置,Redis的安全模式与Manager的安全模式一致,且一旦安装后,无法更改。
本节介绍安全模式下的开发。
操作步骤
安全模式下,Redis客户端需要登录认证并授权以后才能访问或操作Redis Server。
步骤1 登录FusionInsight Manager,点击“System > Role Management”,进入角色管理界面。
步骤2 单击“Add Role”,然后“Role name”和“Description”输入角色名字与描述。
步骤3 单击“Rights > Redis Access Manage”,添加集群或单实例的访问权限。
步骤4 在FusionInsight Manager首页,单击“System > User Management > Add User”,添加一个机机(Machine-Machine)用户,并赋予上面步骤中创建的角色权限。
步骤5 用户创建成功后,在用户列表中点击该用户名称后的,下载相关安全文件。
步骤6 按照样例工程的配置,将下载的安全文件放置到“classes/config”子目录下。
也可以将auth.conf放在classpath路径下,并修改auth.conf中的username(第四步中添加的用户)、realmsName(当前域名)、keyTabFile(keytab文件路径)、krbConfPath(krb5.conf文件路径)。如果keytab文件和krb5.conf是放在当前路径下,keyTabFile和krbConfPath可以只写文件名,否则在auth.conf中配置成绝对路径。
步骤7 Redis Server刷新权限信息需要5-10分钟,按照样例工程配置好安全的信息后,就可以和非安全模式一样,使用客户端了。
5. 代码样例
5.1 单机版客户端使用
功能介绍
使用String类型的IP地址,int类型的PORT端口来初始化一个Jedis,然后就可以调用其方法:set get mset mget 等方法了,每个方法都会返回一个String类型的返回值用来标示操作结果。如果出现网络异常或者Redis服务端异常则会抛出异常。
代码样例
public void testJedis () {
Jedis jedis = null;
try {
//使用IP和端口号初始化
jedis = new Jedis ("192.168.0.1", 22400);
//方法的最后一个参数为 bool : 是否使用管道模式
String result = jedis.set("key111", "value111");
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if(jedis!= null){
jedis.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
5.2 集群版客户端使用
功能介绍
集群版使用JedisCluster,初始化参数为HashSet<HostAndPort>为集群中的实例列表,一般只需要填写集群中的某一个实例的IP和PORT即可,也可以填写多个。
代码样例
public void testClusterAdpter() {
//HostAndPort为集群中任意可以联通的成员。JedisCluster将通过这个成员获取整个集群的信息
//参数1 : 实例ip地址参数2 :端口
HostAndPort host1 = new HostAndPort("192.168.0.1", 22400);
HostAndPort host2 = new HostAndPort("192.168.0.1", 22401);
//Set中放置一个HostAndPort即可
HashSet<HostAndPort> set = new HashSet<HostAndPort>();
set.add(host1);
set.add(host2);
JedisCluster adapter = null;
try {
adapter = new JedisCluster(set);
String result = adapter.set("key111", "value111");
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if(adapter != null){
adapter.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
5.3 集群版客户端管道使用
不需要返回
功能介绍
对于不需要返回值的命令,直接调用JedisCluster 的sync()方法即可。
代码样例
public void testSync() {
HostAndPort host1 = new HostAndPort("192.168.0.1", 22400);
HostAndPort host2 = new HostAndPort("192.168.0.1", 22401);
HashSet<HostAndPort> set = new HashSet<HostAndPort>();
set.add(host1);
set.add(host2);
JedisCluster cluster = new JedisCluster(set);
ClusterBatch pipeline = cluster.getPipeline();
try {
pipeline.set("key111", "value111");
pipeline.set("key222", "value333");
pipeline.set("key222", "value444");
pipeline.sync();// 使用不需要返回值情况
} catch (Exception e) {
e.printStackTrace();
} finally {
cluster.close();
if (pipeline != null) {
pipeline.close();
}
}
}
需要全部返回
功能介绍
JedisCluster 的syncAndReturnAll则会返回所有命令的结果,返回类型为: List<Object>。
代码样例
public void testClusterAdpter() {
HostAndPort host1 = new HostAndPort("192.168.0.1", 22400);
HostAndPort host2 = new HostAndPort("192.168.0.1", 22401);
HashSet<HostAndPort> set = new HashSet<HostAndPort>();
set.add(host1);
set.add(host2);
JedisCluster adapter = new JedisCluster(set);
ClusterBatch pipeline = adapter.getPipeline();
try {
pipeline.set("key111", "value111");
pipeline.set("key222", "value222");
pipeline.set("key333", "value333");
List<Object> syncAndReturnAll = pipeline.syncAndReturnAll();
System.out.println("is equal:"+"OK".equals(syncAndReturnAll.get(0)));
System.out.println("is equal:"+"OK".equals(syncAndReturnAll.get(2)));
} catch (Exception e) {
e.printStackTrace();
} finally {
adapter.close();
}
}
需要部分返回
功能介绍
ClusterAdapter的syncAndReturn(String[])方法则是返回部分需要返回值的接口。其参数为需要返回值的命令的key数组,其返回值为List<Object>。
代码样例
public void testSyncAndReturn() {
HostAndPort host1 = new HostAndPort("192.168.0.1", 22400);
HostAndPort host2 = new HostAndPort("192.168.0.1", 22401);
HashSet<HostAndPort> set = new HashSet<HostAndPort>();
set.add(host1);
set.add(host2);
JedisCluster adapter =new JedisCluster(set);
ClusterBatch pipeline = adapter.getPipeline();
try {
pipeline.set("key111", "value1");
pipeline.set("key222", "value2");
pipeline.set("key333", "value3");
List<Object> syncAndReturn =
pipeline.syncAndReturn(new String[] { "key222", "key333" });
System.out.println("is equal:"+"OK".equals(syncAndReturn.get(0)));
System.out.println("is equal:"+"OK".equals(syncAndReturn.get(1)));
} catch (Exception e) {
e.printStackTrace();
} finally {
adapter.close();
}
}
6. 客户端接口介绍
6.1 简介
Redis接口与社区保持一致,新增的安全模式也不需要修改接口调用,只需要增加相应的配置文件和授权文件即可。
用户可参照社区接口使用:
Redis社区:
http://redis.io/commands
jedis社区:
https://github.com/xetorthio/jedis
6.2 Jedis的使用
可参考样例工程。
import java.util.Arrays;
import java.util.List;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisShardInfo;
import redis.clients.jedis.Pipeline;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPipeline;
import redis.clients.jedis.ShardedJedisPool;
import redis.clients.jedis.Transaction;
public class JedisDemo {
private static Jedis jedis;
private static ShardedJedis sharding;
private static ShardedJedisPool pool;
@BeforeClass
public static void setUpBeforeClass() throws Exception {
List<JedisShardInfo> shards = Arrays.asList(new JedisShardInfo("localhost", 22400), new JedisShardInfo(
"localhost", 22400)); // 使用相同的ip:port,仅作测试
jedis = new Jedis("localhost");
sharding = new ShardedJedis(shards);
pool = new ShardedJedisPool(new JedisPoolConfig(), shards);
}
@AfterClass
public static void tearDownAfterClass() throws Exception {
jedis.disconnect();
sharding.disconnect();
pool.destroy();
}
/**
* 普通同步方式
*/
@Test
public void test1Normal() {
long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
String result = jedis.set("n" + i, "n" + i);
}
long end = System.currentTimeMillis();
System.out.println("Simple SET: " + ((end - start) / 1000.0) + " seconds");
}
/**
* 事务方式
*/
@Test
public void test2Trans() {
long start = System.currentTimeMillis();
Transaction tx = jedis.multi();
for (int i = 0; i < 100000; i++) {
tx.set("t" + i, "t" + i);
}
// System.out.println(tx.get("t1000").get());
List<Object> results = tx.exec();
long end = System.currentTimeMillis();
System.out.println("Transaction SET: " + ((end - start) / 1000.0) + " seconds");
}
/**
* 管道
*/
@Test
public void test3Pipelined() {
Pipeline pipeline = jedis.pipelined();
long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
pipeline.set("p" + i, "p" + i);
}
// System.out.println(pipeline.get("p1000").get());
List<Object> results = pipeline.syncAndReturnAll();
long end = System.currentTimeMillis();
System.out.println("Pipelined SET: " + ((end - start) / 1000.0) + " seconds");
}
/**
* 管道中调用事务
*/
@Test
public void test4combPipelineTrans() {
long start = System.currentTimeMillis();
Pipeline pipeline = jedis.pipelined();
pipeline.multi();
for (int i = 0; i < 100000; i++) {
pipeline.set("" + i, "" + i);
}
pipeline.exec();
List<Object> results = pipeline.syncAndReturnAll();
long end = System.currentTimeMillis();
System.out.println("Pipelined transaction: " + ((end - start) / 1000.0) + " seconds");
}
/**
* 分布式直连同步调用
*/
@Test
public void test5shardNormal() {
long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
String result = sharding.set("sn" + i, "n" + i);
}
long end = System.currentTimeMillis();
System.out.println("Simple@Sharing SET: " + ((end - start) / 1000.0) + " seconds");
}
/**
* 分布式直连异步调用
*/
@Test
public void test6shardpipelined() {
ShardedJedisPipeline pipeline = sharding.pipelined();
long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
pipeline.set("sp" + i, "p" + i);
}
List<Object> results = pipeline.syncAndReturnAll();
long end = System.currentTimeMillis();
System.out.println("Pipelined@Sharing SET: " + ((end - start) / 1000.0) + " seconds");
}
/**
* 分布式连接池同步调用
*/
@Test
public void test7shardSimplePool() {
ShardedJedis one = pool.getResource();
long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
String result = one.set("spn" + i, "n" + i);
}
long end = System.currentTimeMillis();
pool.returnResource(one);
System.out.println("Simple@Pool SET: " + ((end - start) / 1000.0) + " seconds");
}
/**
* 分布式连接池异步调用
*/
@Test
public void test8shardPipelinedPool() {
ShardedJedis one = pool.getResource();
ShardedJedisPipeline pipeline = one.pipelined();
long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
pipeline.set("sppn" + i, "n" + i);
}
List<Object> results = pipeline.syncAndReturnAll();
long end = System.currentTimeMillis();
pool.returnResource(one);
System.out.println("Pipelined@Pool SET: " + ((end - start) / 1000.0) + " seconds");
}
}
6.3 JedisCluster的使用
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
public class JedisClusterDemo {
JedisCluster jc = null;
@Before
public void before() {
Set<HostAndPort> jedisClusterNodes = new HashSet<HostAndPort>();
// Jedis Cluster will attempt to discover cluster nodes automatically
jedisClusterNodes.add(new HostAndPort("127.0.0.1", 7000));
jc = new JedisCluster(jedisClusterNodes);
}
@Test
public void test_incr() {
String key = "page_view";
jc.del(key);
jc.incr(key);
String result = jc.get(key);
System.out.println(result);
Assert.assertEquals("1", result);
}
@Test
public void test_setAndGetStringVal() {
String key = "foo";
String value = "bar";
jc.set(key, value);
String result = jc.get(key);
System.out.println(result);
Assert.assertEquals(value, result);
}
@Test
public void test_setAndGetStringVal_and_set_expire() throws InterruptedException {
String key = "hello";
String value = "world";
int seconds = 3;
jc.setex(key, seconds, value);
String result = jc.get(key);
System.out.println(result);
Assert.assertEquals(value, result);
Thread.sleep(seconds * 1000);
result = jc.get(key);
System.out.println(result);
Assert.assertEquals(null, result);
}
@Test
public void test_setAndGetHashVal() {
String key = "website";
String field = "google";
String value = "google.com";
jc.del(key);
jc.hset(key, field, value);
String result = jc.hget(key, field);
System.out.println(result);
Assert.assertEquals(value, result);
}
@Test
public void test_setAndGetListVal() {
String key = "mylist";
jc.del(key);
String[] vals = { "a", "b", "c" };
jc.rpush(key, vals);
List<String> result = jc.lrange(key, 0, -1);
System.out.println(result);
Assert.assertEquals(vals, result);
}
@Test
public void test_setAndGetSetVal() {
String key = "language";
jc.del(key);
String[] members = { "java", "ruby", "python" };
jc.sadd(key, members);
Set<String> result = jc.smembers(key);
System.out.println(result);
Assert.assertEquals(members, result);
}
}
FROM: http://developer.huawei.com/cn/ict/Products/BigData/FusionInsightHD/Redis/SDK