添加Pom依赖
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
测试类JedisTest
public class JedisTest {
/**
* 连接单机版
* @throws Exception
*/
@Test
public void testJedis() throws Exception{
//创建一个链接Jedis对象,参数:host,port
Jedis jedis=new Jedis("192.168.25.130",6379);
//直接使用jedis操作redis,所有jedis的命令都对应一个方法
jedis.set("test123","my first jedis test");
String string=jedis.get("test123");
System.out.println(string);
//关闭连接
jedis.close();
}
/**
* 连接单机版,用连接池
* @throws Exception
*/
@Test
public void testJedisPool() throws Exception{
//创建一个连接池对象,两个参数host,port
JedisPool jedisPool=new JedisPool("192.168.25.130",6379);
//从连接池获得一个连接,就是一个jedis对象
Jedis jedis=jedisPool.getResource();
//使用jedis操作redis
String string=jedis.get("test123");
System.out.println(string);
//关闭连接,每次使用完后关闭连接。连接池回收资源
jedis.close();
//关闭连接池
jedisPool.close();
}
/**
* 操作redis集群
* @throws Exception
*/
@Test
public void testJedisCluster()throws Exception{
//创建一个JedisCluster对象。他自带连接池。有一个参数nodes是一个set类型。set中包含若干个HostAndPort对象
Set<HostAndPort> nodes=new HashSet<>();
nodes.add(new HostAndPort("192.168.25.130",7001));
nodes.add(new HostAndPort("192.168.25.130",7002));
nodes.add(new HostAndPort("192.168.25.130",7003));
nodes.add(new HostAndPort("192.168.25.130",7004));
nodes.add(new HostAndPort("192.168.25.130",7005));
nodes.add(new HostAndPort("192.168.25.130",7006));
JedisCluster jedisCluster=new JedisCluster(nodes);
//直接使用jedisCluster对象操作jedis
jedisCluster.set("test","123");
String string=jedisCluster.get("test");
System.out.println(string);
//关闭JedisCluster对象
jedisCluster.close();
}
实际项目中不会把这些ip和端口信息放在业务类中,如果更换信息还需要修改代码,那要怎么做那???而且单机版和集群版切换怎么做??
在项目中建立一个jedis的工具包,存放如下三个文件:接口JedisClient、两个实现类单机版类JedisClientPool、集群版类JedisClientCluster
public interface JedisClient {
String set(String key, String value);
String get(String key);
Boolean exists(String key);
Long expire(String key, int seconds);
Long ttl(String key);
Long incr(String key);
Long hset(String key, String field, String value);
String hget(String key, String field);
Long hdel(String key, String... field);
}
public class JedisClientPool implements JedisClient {
private JedisPool jedisPool;
public void setJedisPool(JedisPool jedisPool) {
this.jedisPool = jedisPool;
}
@Override
public String set(String key, String value) {
Jedis jedis = jedisPool.getResource();
String result = jedis.set(key, value);
jedis.close();
return result;
}
@Override
public String get(String key) {
Jedis jedis = jedisPool.getResource();
String result = jedis.get(key);
jedis.close();
return result;
}
@Override
public Boolean exists(String key) {
Jedis jedis = jedisPool.getResource();
Boolean result = jedis.exists(key);
jedis.close();
return result;
}
@Override
public Long expire(String key, int seconds) {
Jedis jedis = jedisPool.getResource();
Long result = jedis.expire(key, seconds);
jedis.close();
return result;
}
@Override
public Long ttl(String key) {
Jedis jedis = jedisPool.getResource();
Long result = jedis.ttl(key);
jedis.close();
return result;
}
@Override
public Long incr(String key) {
Jedis jedis = jedisPool.getResource();
Long result = jedis.incr(key);
jedis.close();
return result;
}
@Override
public Long hset(String key, String field, String value) {
Jedis jedis = jedisPool.getResource();
Long result = jedis.hset(key, field, value);
jedis.close();
return result;
}
@Override
public String hget(String key, String field) {
Jedis jedis = jedisPool.getResource();
String result = jedis.hget(key, field);
jedis.close();
return result;
}
@Override
public Long hdel(String key, String... field) {
Jedis jedis = jedisPool.getResource();
Long result = jedis.hdel(key, field);
jedis.close();
return result;
}
}
public class JedisClientCluster implements JedisClient {
private JedisCluster jedisCluster;
public void setJedisCluster(JedisCluster jedisCluster) {
this.jedisCluster = jedisCluster;
}
@Override
public String set(String key, String value) {
return jedisCluster.set(key, value);
}
@Override
public String get(String key) {
return jedisCluster.get(key);
}
@Override
public Boolean exists(String key) {
return jedisCluster.exists(key);
}
@Override
public Long expire(String key, int seconds) {
return jedisCluster.expire(key, seconds);
}
@Override
public Long ttl(String key) {
return jedisCluster.ttl(key);
}
@Override
public Long incr(String key) {
return jedisCluster.incr(key);
}
@Override
public Long hset(String key, String field, String value) {
return jedisCluster.hset(key, field, value);
}
@Override
public String hget(String key, String field) {
return jedisCluster.hget(key, field);
}
@Override
public Long hdel(String key, String... field) {
return jedisCluster.hdel(key, field);
}
}
spring目录下建立一个配置文件applicationContent-reids.xml,在单机版还是集群版打开其注释就可以,不用修改代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd">
<!-- 连接池配置 -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<!-- 最大连接数 -->
<property name="maxTotal" value="30"/>
<!-- 最大空闲连接数 -->
<property name="maxIdle" value="10"/>
<!-- 每次释放连接的最大数目 -->
<property name="numTestsPerEvictionRun" value="1024"/>
<!-- 释放连接的扫描间隔(毫秒) -->
<property name="timeBetweenEvictionRunsMillis" value="30000"/>
<!-- 连接最小空闲时间 -->
<property name="minEvictableIdleTimeMillis" value="1800000"/>
<!-- 连接空闲多久后释放, 当空闲时间>该值 且 空闲连接>最大空闲连接数 时直接释放 -->
<property name="softMinEvictableIdleTimeMillis" value="10000"/>
<!-- 获取连接时的最大等待毫秒数,小于零:阻塞不确定的时间,默认-1 -->
<property name="maxWaitMillis" value="1500"/>
<!-- 在获取连接的时候检查有效性, 默认false -->
<property name="testOnBorrow" value="true"/>
<!-- 在空闲时检查有效性, 默认false -->
<property name="testWhileIdle" value="true"/>
<!-- 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true -->
<property name="blockWhenExhausted" value="false"/>
</bean>
<!--连接jedis单机版-->
<!--<bean id="jedisClientPool" class="cn.e3mall.common.jedis.JedisClientPool">-->
<!--<property name="jedisPool" ref="jedisPool"/>-->
<!--</bean>-->
<!--<bean id="jedisPool" class="redis.clients.jedis.JedisPool">-->
<!--<constructor-arg name="host" value="192.168.25.130"/>-->
<!--<constructor-arg name="port" value="6379"/>-->
<!--</bean>-->
<!--连接redis集群-->
<bean id="jedisClientCluster" class="cn.e3mall.common.jedis.JedisClientCluster">
<property name="jedisCluster" ref="jedisCluster"/>
</bean>
<bean id="jedisCluster" class="redis.clients.jedis.JedisCluster">
<constructor-arg name="nodes">
<set>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.25.130"></constructor-arg>
<constructor-arg name="port" value="7001"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.25.130"></constructor-arg>
<constructor-arg name="port" value="7002"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.25.130"></constructor-arg>
<constructor-arg name="port" value="7003"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.25.130"></constructor-arg>
<constructor-arg name="port" value="7004"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.25.130"></constructor-arg>
<constructor-arg name="port" value="7005"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.25.130"></constructor-arg>
<constructor-arg name="port" value="7006"></constructor-arg>
</bean>
</set>
</constructor-arg>
<constructor-arg name="poolConfig" ref="jedisPoolConfig"></constructor-arg>
</bean>
</beans>
测试类:JedisClientTest
public class JedisClientTest {
@Test
public void testJedisClient()throws Exception{
//初始化spring容器
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("classpath:spring/applicationContext-redis.xml");
//从容器中获得JedisClient对象
JedisClient jedisClient=applicationContext.getBean(JedisClient.class);
jedisClient.set("mytest","jedisClient");
String string=jedisClient.get("mytest");
System.out.println(string);
}
}
Jedis连接池介绍:
Jedis连接分为直连和连接池方式;
直连指Jedis每次都会新建TCP连接,使用后再断开连接,对于频繁访问Redis的场景并不是高校的使用方式。
Jedis连接池是指Jedis对象预先放在池子中JedisPool,每次要连接Redis,只需要在池子中借,用完了归还。
客户端连接Redis使用的是TCP协议,直连的方式每次需要建立TCP连接,而连接池的方式可以预先初始化好Jedis连接,所以每次只需要从Jedis连接池借用,借用和归还操作是在本地进行的,只有少量的并发同步开销,远远小于新建TCP连接的开销。
直连 vs 连接池
直连:
优点:
简单方便,适用于少量长期连接的场景
缺点:
存在每次新建关闭TCP连接开销
- 资源无法控制,极端情况会出现内存泄漏
- Jedis对象线程不安全
连接池:
优点:
- 无需每次连接都生成Jedis对象,降低开销
- 使用连接池的形式保护和控制资源的使用
缺点:
- 相当于直连,使用相对麻烦,尤其在资源管理上需要很多参数来保证,一旦规划不合理也会出现问题。
连接池一些参数介绍
- maxActive 连接池中最大连接数
- maxIdle 连接池中最大空闲的连接数
- minIdle 连接池中最少空闲的连接数
- maxWaitMillis 当连接池资源用尽后,调用者的最大等待时间(毫秒),默认-1 永远不超时一直等,不建议使用
- jmxEnabled 是否开启jmx监控,如果应用开启了jmx端口并且jmxEnabled设置为true,就可以通过jconsole或者jvisualvm看到关于连接池的相关统计,有助于了解连接池的使用情况,并且可以针对其做监控统计
- minEvictableIdleTimeMillis 连接的最小空闲时间,达到此值后空闲连接将被移除
- numTestsPerEvictionRun 做空闲连接检测时,每次的采样数
- testOnBorrow 向连接池借用连接时是否做连接有效性检测(ping),无效连接会被移除,每次借用执行一次ping命令
- testOnReturn 向连接池归还连接时是否做连接有效性检测,无效连接会被移除,每次归还多执行一次ping命令
- testWhileIdle 向连接池借用连接时是否做连接空闲检测,空闲超时的连接会被移除。
- timeBetweenEvictionRunsMillis 空闲连接的检测周期(毫秒)
- blockWhenExhausted 当连接池用尽后,调用者是否要等待,这个参数和maxWaitMillis对应的,只有当参数为true时,maxWaitMillis才会生效。
Redis的Pipeline
当我们做批量操作的时候,如果用for循环性能就特别低,Redis提供了mget和mset方法,单机版的时候可以直接用,但是如果是cluster这些原子命令就用不了了,需要借助于pipeline去做。如果自己写mdel的方法,也需要借助pipeline。