1 redis的 java api
在本章主要学习在java中使用redis,在java中可以轻易使用redis,或者通过spring的redisTemplate使用Redis。为了实际的工作和学习的需要,本章以spring的视角介绍在java里面如何使用redis,基础章节会以xml的配置方式为主,在实践章节会以java的配置为主介绍redis,你可以根据需要使用xml或者注解来实现你想要的功能。
1.1.1
在java程序中使用redis,在java中使用redis工具,需要下载jedis.jar包,把它加载到工程的路径中,所以首先打开网站http://mvnrepository.com/artifact/redis.clients/jedis,如图
把它导入工程路径就可以使用了,可以使用以下代码测试。
1-1: java连接redis
Jedis jedis =new Jedis(“localhost”,6379); //连接Redis
//jedis.auth(“password”); //如果需要密码
Int i=0; //记录操作次数
try{
long start =System.currentTimeMillis(); //开始毫秒数
while (true) {
Long end =system.currentTimeMillis();
If (end - start >=1000){ //当大于等于1000毫秒(相当于1秒)时,结束操作
Break;
}
i++;
Jedis.set(“test” + i, i + “”);
}
}finally{ //关闭连接
Jedis.close();
}
System.out.println(“redis每秒操作: “ + i + “次”);//打印1秒内对redis的操作次数。
这段代码主要在于测试Redis的写入性能,这是笔者使用自己电脑(使用window操作系统)测试的结果。
Redis每秒操作:23308次。
这里每秒只操作了两万多次,而事实上Redis的速度比这个操作的速度快的多,这里慢是应为我们只是一条条的将命令发送给redis执行。如果使用流水线技术他会快的多,将可以达到10万次每秒的操作,十分有利于系统性能的提高。注意,这只是一个简单的连接,更多的时候,我们可以使用连接池去管理它,java redis 的连接池提供了类redis.clients.jedis.JedisPool 用来创建Redis的连接池对象。使用这个对象,需要使用类redis.clients.jedis.JedisPoolConfig对连接池进行配置,连接池使用redis代码如下:
1-2使用Redis连接池
JedisPoolConfig poolCfg =new JedisPoolConfig();
//最大空闲数
poolCfg.setMaxIdle(50);
//最大连接数
PoolCfg.setMaxTotal(100);
//最大等待毫秒数
poolCfg.setMaxWaitMillis(20000);
//使用配置创建连接池
JedisPool pool =new JedisPool (poolCfg, “localhost”);
//从连接池中获取单个连接
Jedis jedis =pool.getResource();
//如果需要密码
//jedis.auth(“password”);
在写代码的时候注释了解每一步的含义,对连接池进行有效的管理。
由于redis只能提供基于字符串型的操作,而在java中使用的却已类对象为主,所以需要redis存储的字符串和java对象相互转换。如果自己编写这些规则,工作量还是比较大的,比如一个角色对象,我们没有办法直接把对象存入redis中,需要进一步进行转换,所以对操作对象而言,使用redis是比较难得,好在spring对这些进行了封装和支持,它提供了序列化的设计框架和一些序列化的类,使用后可以通过序列化把Java对象转换,使得redis能把它存储起来,并且在读取的时候,再把由序列化过的字符串转化为java对象,这样在java环境中使用redis就更加简单了,所以更多时候可以使用spring提供的RedisTemplate的机制来使用redis。
1.1.2在spring中使用redis
上一章介绍了在没有封装的情况下使用java api 的缺点,需要自己编写规则把java对象和redis的字符串进行相互交换,而在spring中这些问题都可以轻松处理。在spring中使用redis,
除了需要Redis,除了需要jedis.Jar外,需要下载spring-data-redis.jar,打开网址http://mvnrepository.com/artifact/org.springframework.data/spring-data-redis,可以看到以下界面,如图所示
这里值得注意的是jar包和spring版本兼容的问题,我使用的jar包版本是1.7.2,而spring的版本是4.3.2,如果使用其他版本可能存在不兼容的问题,从而产生异常,这是我从实际操作中得来的经验。
把下载的jar包导入到工程环境中,这样就可以在使用spring提供的RedisTemplate操作redis了,只是在使用前,需要对spring提供的方案进行探讨,以便更好地使用它们。(关于spring Data Redis项目了解后你可以更好地理解如何使用spring操作redis。
在大部分情况下我们都会用到连接池,于是先用spring配置一个JedisPoolConfig对象,这个配置相对而言比较简单,代码如下所示。
代码1-3 使用spring配置JedisPoolConfig对象
<bean id=”poolConfig” class=”redis.clients.Jedis.JedisPoolConfig”>
// 最大空闲数
<property name=”maxIdle” value=”50” />
//最大连接数
<property name=”maxTotal” value=”100” />
//最大等待时间
<property name=”maxWaitMillis” value=”20000”/>
</bean>
这样就配置了一个连接池的配置,继续往下配置。
在使用spring提供的RedisTemplate之前需要配置Spring所提供的的连接工厂,在spring data redis方案中它提供了4种工厂模型。
·jredisconnectionFactory。
·jedisConnectionFactory
·LettuceConnectionFactory。
·SrpConnectionFactory
虽然使用哪种实现工厂都是可以的,但是要根据环境进行测试,以验证使用哪个
方案的性能是最佳的。无论如何他们都是由接口RedisConnectionFactory的实现类,更多的时候都是通过接口定义去理解它们,所以他们是具有接口适用性的,我是使用的最为广泛的JedisConnectionFactoty为例进行详解。
例如,在spring种配置一个jedisconnectionFactory对象,代码如1-4所示
代码 1-4 配置jedisConnectionFactory
<bean id=”connectionFactory” class=”org.springframework.data.redis.connection.jedis.JedisConnectionFactory”>
<property name=”hostname” value=”localhost”/>
<property name=”port” value=”6379”/>
//<property name=”password” value=”password”/>
<property name=”poolConfig” ref=”poolConfig/>
</bean>
解释一下它的属性配置。
·hostname,代表的是服务器,默认值是localhost,所以如果是本机可以不配置它。
·port,代表的是接口端口,默认值是6379,所以可以使用默认的redis端口,也可以不配置他。
·password,代表的是密码,在需要密码连接redis的场合需要配置他。
·poolConfig,是连接池配置对象,可以设置连接池的属性。
这样就完成了一个Redis连接工厂的配置,这里配置的是JedisConnectionFactory,如过需要的是LettuceConnectionFactory,可以把代码清单1-3中中的bean元素的class属性修改为org.springframework.data.redis.connection.lettuce.lettuceConnectionFactor即可,这取决于项目的需要和特殊性,有了RedisConnectionFactory工厂,就可以使用RedisTemplate了。
普通的连接使用没有办法把java对象直接存入Redis,而需要我们自己提供方案,这时候往往就是将对象序列化,然后使用redis进行存储,而取回序列化的内容后,在通过转换变为java对象,spring模板提供了封装的方案,在它内部提供了redisSerializer接口(org.springframework.data.redis.serializer.RedisSerializer)和一些实现类,其原理如图1-11所示。
可以选择spring提供的方案去处理序列化,当然也可以去实现在spring data redis中定义的redisSerializer接口,在spring中提供了以下几种实现redisSerializer接口的序列化器。
·genericJackson2jsonredisserializer,通用的使用json2.jar包,将redis对象的序列化器。
·Jackson2jsonredisserializer<T>,通过jackson2.jar包提供的序列化进行转换。
·jdkserializationredisSerializer<T>,使用jdk的序列化器进行转换。
·Oxmserializer,使用字符串进行序列化。
·generictostringSerializer,通过通用的字符串序列化进行相互转换。
使用它们就能够帮助我们把对象通过序列化存储到redis中,也可以把redis存储的内容转换为java对象,为此spring提供的redisTemplate还有两个属性。
·keySerializer--------键序列器。
·valueSerializer--------值序列器。
有了上面的了解,就可以配置RedisTemplate了。假设选用StringRedisSerializer作为Redis的key的序列化器,而使用jdkSerializationRedisSerializer作为其value的序列化器,则可以按照代码清单1-5的方法来配置RedisTemplate。
代码清单 1-5 配置Spring Redis template
<bean id=”jdkSerializationRediserializer” class=”org.springframe.data.redis.serializer.jdkSerializationRedisSerializer”/>
<bean id=”stringRedisSerializer” class=”org.springframework.data.redis.serializer.StringRedlisSerializer”/>
<bean id=”redisTemplate” class=”org.springframe.data.redis.core.RedisTemplate”>
<property name=”connectionFactory” ref=”connectionFactory”/>
<property name=”keySerializer” ref=”stringRedisSerializer”/>
<property name=”valuSerializer” ref=”jdkSerializationRedisSerializer” />
</bean>
这样就配置了一个RedisTemplate对象,并且spring data redis知道会用对应的序列化器去转换redis的键值。
举个例子,新建一个角色对象,使用redis保存它的对象,如代码清单1-6所示。
代码清单 1-6:使用redis保存角色类对象
Package com.learn.ssm.chapter17.pojo;
Import java.io.Serializable;
/*注意,对象要可序列化,需要实现Serizable接口,往往要重写serialVersionUID*/
Public class Role implements Serializable{
Private static final long serialVersionUID= 6977402643848374753L;
Private long id;
Private String roleName;
Private String note;
/*setter and getter**/
}--
因为要序列化对象,所以需要实现Serializable接口,表面他能够序列化,而serialVersionUID代表的是序列化的版本编号。
假设在applicationContext.xml中配置了代码清单1-3至1-5的配置,那么就可以测试保存这个Role对象了,测试代码如代码清单1-7所示。
代码清单 1-7:使用RedisTemplate保存Role对象
ApplicationContext applicationContext=new ClassPathXmlApplicationContext(“applicationContext.xml”);
RedisTemplate redisTemplate = applicationContext.getBean(RedisTemplate.class);
Role role =new Role();
Role.setId(1L);
role.setRoleName(“role_name_1”);
Role.setNote(“note_1”);
redisTemplate.opsForValue.set(“role_1”,role);
Role eole1 =(Role) redisTemplate.opsForvalue().get(“role_1”);
System.out.println(role1.getRoleName());
在system.out.println(role1.getRoleName());这行打下断点,可以看到如图1-12图的测试结果。
显然这样已经成功保存和获取了一个java对象,这段代码演示的是如何使用StringRedisSerializer序列化Redis的key,而使用jdkSerializationRedisSerializer序列化Redis的value,当然也可以根据需要去选择,甚至是自定义序列化器。
注意,以上的使用都是基于RedisTemplate,基于连接池的操作,换句话说,并不能保证每次使用RedisTemplate是操作同一个对redis的连接,比如1-7代码清单下面的代码。
redisTemplate.opsForValue.set(“role_1”,role);
Role eole1 =(Role) redisTemplate.opsForvalue().get(“role_1”);
图1-12
Set和get方法看起来很简单,他可能就来自同一个redis连接池的不同redis连接。为了使得所有的操作都来自同一个连接,可以使用SessonCallback或者RedisCallback这两个接口,Rediscallback是比较底层的封装,其使用不是很友好,所以更多的时候会使用sessioncallback接口,通过这个接口就可以把多个命令放入到同一个redis连接中去执行,如代码清单1-8所示,它主要是实现了代码清单1-7中的功能。
代码清单1-8: 使用sessioncallback接口
ApplicationContext application =new ClassPathXmlApplicationContext(“applicationContext.xml”);
RedisTemplate redistemplate=applicationContext.getBean(RedisTemplate.class);
Role role=new Role();
Role.setId(1);
Role.setRoleName(“role_name_1”);
Role.setNote(“role_note_1”);
SessionCallback callBack =new SessionCallback<Role>(){
@override
Public Role execute (RedisOperations ops) throws DataAccessException {
ops.boundValueOps(“role_1”).set(role);
Return (Role) ops.boundValueOps(“role_1”).get();
}
};
Role savedRole =(Role)redisTemplate.execute(callBack);
System.out.println(savedRole.getId());
这样set和get命令就能保证在同一个连接池的同一个redis连接操作,这里使用的是使用匿名内部类的形式,而事实上如果采用java 8 的jdk版本,也可以使用lambda表达式进行编写SessionCallback的业务逻辑,这样逻辑会更为清晰明了。由于前后使用的都是同一个连接,因此对于资源损耗就比较小,在使用redis操作多个命令或者使用事务时也常常会用到他。