博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JAVA缓存-Redis入门级使用
阅读量:6432 次
发布时间:2019-06-23

本文共 21288 字,大约阅读时间需要 70 分钟。

#前言

Java缓存实现方案有很多,最基本的自己使用Map去构建缓存,再高级点的使用Ehcache或者Goolgeguava作为内存缓存框架,Ehcache可以满足单机缓存的需求(Ehcache的具体使用在我过往的文章中有所介绍),如果我们是多台机子共用缓存数据的话,Ehcache可通过rmijgroupjms的方式实现,但是实用性与操作性不高且复杂,现时大部分应用仅用Ehcache作为单机缓存使用,这时候我们可以通过搭建缓存服务器解决多机使用的问题,常见的缓存服务器有MemcachedRedis等。

现时业界主流大多使用Redis。所以本文主要介绍在Java中如何使用Redis。至于如何搭建Redis,我在过往的文章中已有所介绍,不知道如何搭建的同学,可以参考我过往的文章,下文所用到相关的Redis信息均为搭建教程中的信息。

PS:文章中所用到的示例代码,部分参考至开源项目,特此声明。

本文同步发布于简书 :

Java连接Redis

Java连接Redis官方推荐的是使用JedisRedisson进行连接操作,SpringRedis有很好的支持,所以此文我结合Spring中的Spring DataRedis进行操作。

1. maven引用

org.springframework.data
spring-data-redis
1.8.7.RELEASE
redis.clients
jedis
2.9.0
org.redisson
redisson
3.5.5
复制代码

2. 建立Redis配置文件

classpath下建立Redis配置文件redis.properties

如果同学们是搭建Redis高可用架构,是通过向外提供VIP虚拟IP的方式连接Redis,则只需在配置文件中将redis.host=172.16.2.185单机IP改为VIP虚拟IPredis.host=172.16.2.250即可实现Redis高可用,而不需要使用Spring提供的RedisSentinel方案实现Redis高可用。

#VIP#redis.host=172.16.2.250redis.host=172.16.2.185redis.port=6379redis.password=123456#最小空闲数redis.minIdle=2#最大空闲数redis.maxIdle=10#最大连接数redis.maxTotal=1000#最大建立连接等待时间redis.maxWaitMillis=3000#客户端超时时间单位是毫秒redis.timeout=120000#明是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个  redis.testOnBorrow=trueredis.expiration=600复制代码

3. 与Spring结合

3.1 配置Jedis实现

classpath下建立文件夹spring用于存放所有与spring相关的配置文件。在spring文件夹下建立spring-redis.xml,主要用于注入Jedis

复制代码
3.2 配置Redisson实现

spring文件夹下建立spring-redisson.xml,主要用于注入Redisson

复制代码

4. 代码实现

4.1 定义获取缓存的工具类CacheUtil

这个类主要是用于获取缓存管理器,因为Jedis封装Redis基本操作的接口比较友好,所以基本操作使用Jedis实现,但是将Redis当做分布式锁使用时,如果是自行用Jedis中的setNX + 时间戳过程方法实现的话,会略显复杂,还可能写的不严谨,存在原子性操作或者死锁等问题。此处的分布式锁实现使用Redisson帮我们封装好的方法实现加锁与解锁,顺便提一句,Redisson加锁操作是使用lua脚本一次执行加锁与设置过期的操作的,所以不存在原子性问题。这处暂时不展开讨论分布式锁的问题,日后有空再和大家一同探讨分布式锁的问题。

package com.easylink.mall.core.cache.redis;import com.easylink.mall.core.support.util.PropertiesFileUtil;public class CacheUtil {	/**	 * 缓存管理器,主要执行缓存操作	 */	private static CacheManager cacheManager;	/**	 * 锁管理器,主要执行加锁与解锁操作	 */	private static CacheManager lockManager;	public static void setCacheManager(CacheManager cacheManager) {		CacheUtil.cacheManager = cacheManager;	}	public static void setLockManager(CacheManager cacheManager) {		CacheUtil.lockManager = cacheManager;	}	public static CacheManager getCache() {		return cacheManager;	}	public static CacheManager getLockManager() {		return lockManager;	}	/** 获取锁 */	public static boolean tryLock(String key) {		int expires = 1000 * PropertiesFileUtil.getInstance("redis.properties").getInt("redis.lock.expires", 180);		return lockManager.setnx(key, expires);	}	/** 获取锁 */	public static boolean getLock(String key) {		return lockManager.lock(key);	}	/** 解锁 */	public static void unlock(String key) {		lockManager.unlock(key);	}}复制代码
4.2 定义缓存操作管理接口CacheManager
package com.easylink.mall.core.cache.redis;import java.io.Serializable;import java.util.Set;/** * 缓存操作管理接口 *  * @author Ben. * */public interface CacheManager {	/**	 * 根据key获取对象	 * 	 * @param key	 * @return	 */	Object get(final String key);	/**	 * 根据正则表达式获取对象	 * 	 * @param pattern	 *            正则表达式	 * @return	 */	Set getAll(final String pattern);	/**	 * 设置key-value	 * 	 * @param key	 * @param value	 * @param seconds	 *            过期时间(秒)	 */	void set(final String key, final Serializable value, int seconds);	/**	 * 设置key-value 过期时间使用默认配置值	 * 	 * @param key	 * @param value	 */	void set(final String key, final Serializable value);	/**	 * 根据key判断某一对象是否存在	 * 	 * @param key	 * @return 是否存在	 */	Boolean exists(final String key);	/**	 * 根据key删除对象	 * 	 * @param key	 */	void del(final String key);	/**	 * 根据正则表达式删除对象	 * 	 * @param pattern	 *            正则表达式	 * @return	 */	void delAll(final String pattern);	/**	 * 根据key获取对应对象的类型	 * 	 * @param key	 * @return 对应对象的类型	 */	String type(final String key);	/**	 * 设置key的过期时间	 * 	 * @param key	 * @param seconds	 * @return 是否设置成功	 */	Boolean expire(final String key, final int seconds);	/**	 * 设置key在指定时间点后过期	 * 	 * @param key	 * @param unixTime	 * @return 是否成功	 */	Boolean expireAt(final String key, final long unixTime);	/**	 * 获取对应key的过期时间	 * 	 * @param key	 * @return	 */	Long ttl(final String key);	/**	 * 设置新值并返回旧值	 * 	 * @param key	 * @param value	 * @return 旧值	 */	Object getSet(final String key, final Serializable value);	/**	 * 对key进行加锁	 * 	 * @param key	 * @return 是否加锁成功	 */	boolean lock(String key);	/**	 * 对key进行解锁	 * 	 * @param key	 */	void unlock(String key);	/**	 * 根据key设置对应哈希表对象的field - value	 * 	 * @param key	 * @param field	 * @param value	 */	void hset(String key, Serializable field, Serializable value);	/**	 * 根据key获取对应哈希表的对应field的对象	 * 	 * @param key	 * @param field	 * @return	 */	Object hget(String key, Serializable field);	/**	 * 根据key删除对应哈希表的对应field的对象	 * 	 * @param key	 * @param field	 * @return	 */	void hdel(String key, Serializable field);	/**	 * 指定的 key 不存在时,为 key 设置指定的value	 * 	 * @param key	 * @param value	 * @return 是否设置成功	 */	boolean setnx(String key, Serializable value);	/**	 * 对应key的值自增	 * 	 * @param key	 * @return 自增后的值	 */	Long incr(String key);	/**	 * 用指定的字符串覆盖给定 key 所储存的字符串值,覆盖的位置从偏移量 offset 开始	 * 	 * @param key	 * @param offset	 *            偏移量	 * @param value	 */	void setrange(String key, long offset, String value);	/**	 * 用于获取存储在指定 key 中字符串的子字符串。字符串的截取范围由 start 和 end 两个偏移量决定(包括 start 和 end 在内)。	 * 	 * @param key	 * @param startOffset	 * @param endOffset	 * @return	 */	String getrange(String key, long startOffset, long endOffset);	/**	 * 将value设置至指定key的set集合中	 * 	 * @param key	 * @param value	 */	void sadd(String key, Serializable value);	/**	 * 获取指定key的set集合	 * 	 * @param key	 * @return	 */	Set
sall(String key); /** * 删除指定key的set集合中的value * * @param key * @param value * @return */ boolean sdel(String key, Serializable value);}复制代码
4.3 定义RedisHelper

基于SpringRedisTemplate实现CacheManager接口,主要用于对缓存的基本操作,不用于分布式锁作用,此处的分布式锁实现不严谨,不当做参考

/** * Redis缓存辅助类 *  * @author ShenHuaJie * @version 2016年4月2日 下午4:17:22 */public final class RedisHelper implements CacheManager {	private static final Logger logger = Logger.getLogger(RedisHelper.class);	private RedisSerializer
keySerializer; private RedisSerializer
valueSerializer; private RedisTemplate
redisTemplate; private final Integer EXPIRE = PropertiesFileUtil.getInstance("redis.properties").getInt("redis.expiration"); @SuppressWarnings("unchecked") public void setRedisTemplate(RedisTemplate
redisTemplate) { this.redisTemplate = redisTemplate; this.keySerializer = (RedisSerializer
) redisTemplate.getKeySerializer(); this.valueSerializer = (RedisSerializer
) redisTemplate.getValueSerializer(); CacheUtil.setCacheManager(this); } @Override public final Object get(final String key) { // 先过期 expire(key, EXPIRE); // 后取值 return redisTemplate.boundValueOps(key).get(); } @Override public final Set getAll(final String pattern) { Set values = new HashSet(); Set
keys = redisTemplate.keys(pattern); for (Serializable key : keys) { expire(key.toString(), EXPIRE); values.add(redisTemplate.opsForValue().get(key)); } return values; } @Override public final void set(final String key, final Serializable value, int seconds) { redisTemplate.boundValueOps(key).set(value); expire(key, seconds); } @Override public final void set(final String key, final Serializable value) { redisTemplate.boundValueOps(key).set(value); expire(key, EXPIRE); } @Override public final Boolean exists(final String key) { return redisTemplate.hasKey(key); } @Override public final void del(final String key) { redisTemplate.delete(key); } @Override public final void delAll(final String pattern) { redisTemplate.delete(redisTemplate.keys(pattern)); } @Override public final String type(final String key) { expire(key, EXPIRE); return redisTemplate.type(key).getClass().getName(); } @Override public final Boolean expire(final String key, final int seconds) { return redisTemplate.expire(key, seconds, TimeUnit.SECONDS); } @Override public final Boolean expireAt(final String key, final long unixTime) { return redisTemplate.expireAt(key, new Date(unixTime)); } @Override public final Long ttl(final String key) { return redisTemplate.getExpire(key, TimeUnit.SECONDS); } @Override public final void setrange(final String key, final long offset, final String value) { redisTemplate.boundValueOps(key).set(value, offset); expire(key, EXPIRE); } @Override public final String getrange(final String key, final long startOffset, final long endOffset) { expire(key, EXPIRE); return redisTemplate.boundValueOps(key).get(startOffset, endOffset); } @Override public final Object getSet(final String key, final Serializable value) { expire(key, EXPIRE); return redisTemplate.boundValueOps(key).getAndSet(value); } @Override public boolean setnx(String key, Serializable value) { RedisConnectionFactory factory = redisTemplate.getConnectionFactory(); RedisConnection redisConnection = null; try { redisConnection = RedisConnectionUtils.getConnection(factory); if (redisConnection == null) { return redisTemplate.boundValueOps(key).setIfAbsent(value); } logger.info(keySerializer); logger.info(valueSerializer); return redisConnection.setNX(keySerializer.serialize(key), valueSerializer.serialize(value)); } finally { RedisConnectionUtils.releaseConnection(redisConnection, factory); } } @Override public boolean lock(String key) { RedisConnectionFactory factory = redisTemplate.getConnectionFactory(); RedisConnection redisConnection = null; try { redisConnection = RedisConnectionUtils.getConnection(factory); if (redisConnection == null) { return redisTemplate.boundValueOps(key).setIfAbsent("0"); } return redisConnection.setNX(keySerializer.serialize(key), valueSerializer.serialize("0")); } finally { RedisConnectionUtils.releaseConnection(redisConnection, factory); } } @Override public void unlock(String key) { redisTemplate.delete(key); } @Override public void hset(String key, Serializable field, Serializable value) { redisTemplate.boundHashOps(key).put(field, value); } @Override public Object hget(String key, Serializable field) { return redisTemplate.boundHashOps(key).get(field); } @Override public void hdel(String key, Serializable field) { redisTemplate.boundHashOps(key).delete(field); } @Override public void sadd(String key, Serializable value) { redisTemplate.boundSetOps(key).add(value); } @Override public Set
sall(String key) { return redisTemplate.boundSetOps(key).members(); } @Override public boolean sdel(String key, Serializable value) { return redisTemplate.boundSetOps(key).remove(value) == 1; } @Override public Long incr(String key) { return redisTemplate.boundValueOps(key).increment(1L); }}复制代码
4.4 定义RedissonHelper

基于Redisson实现CacheManager接口,主要用于实现基于Redis的分布式锁

package com.easylink.mall.core.cache.redisson;import java.io.Serializable;import java.util.Date;import java.util.HashSet;import java.util.Iterator;import java.util.Map;import java.util.Set;import java.util.concurrent.TimeUnit;import org.redisson.api.RBucket;import org.redisson.api.RType;import org.redisson.api.RedissonClient;import com.easylink.mall.core.cache.redis.CacheManager;import com.easylink.mall.core.cache.redis.CacheUtil;import com.easylink.mall.core.support.util.PropertiesFileUtil;/** *  * Redis缓存辅助类 *  */public class RedissonHelper implements CacheManager {	private RedissonClient redissonClient;	private final Integer EXPIRE = PropertiesFileUtil.getInstance("redis.properties").getInt("redis.expiration");	public void setRedissonClient(Client client) {		this.redissonClient = client.getRedissonClient();		CacheUtil.setLockManager(this);	}	private RBucket getRedisBucket(String key) {		return redissonClient.getBucket(key);	}	@Override	public final Object get(final String key) {		RBucket temp = getRedisBucket(key);		expire(temp, EXPIRE);		return temp.get();	}	@Override	public final void set(final String key, final Serializable value) {		RBucket temp = getRedisBucket(key);		temp.set(value);		expire(temp, EXPIRE);	}	@Override	public final void set(final String key, final Serializable value, int seconds) {		RBucket temp = getRedisBucket(key);		temp.set(value);		expire(temp, seconds);	}	public final void multiSet(final Map
temps) { redissonClient.getBuckets().set(temps); } @Override public final Boolean exists(final String key) { RBucket
temp = getRedisBucket(key); return temp.isExists(); } @Override public final void del(final String key) { redissonClient.getKeys().delete(key); } @Override public final void delAll(final String pattern) { redissonClient.getKeys().deleteByPattern(pattern); } @Override public final String type(final String key) { RType type = redissonClient.getKeys().getType(key); if (type == null) { return null; } return type.getClass().getName(); } /** * 在某段时间后失效 * * @return */ private final void expire(final RBucket bucket, final int seconds) { bucket.expire(seconds, TimeUnit.SECONDS); } /** * 在某个时间点失效 * * @param key * @param unixTime * @return * */ @Override public final Boolean expireAt(final String key, final long unixTime) { return redissonClient.getBucket(key).expireAt(new Date(unixTime)); } @Override public final Long ttl(final String key) { RBucket rBucket = getRedisBucket(key); return rBucket.remainTimeToLive(); } @Override public final Object getSet(final String key, final Serializable value) { RBucket rBucket = getRedisBucket(key); return rBucket.getAndSet(value); } @Override public Set getAll(String pattern) { Set set = new HashSet(); Iterable
keys = redissonClient.getKeys().getKeysByPattern(pattern); for (Iterator
iterator = keys.iterator(); iterator.hasNext();) { String key = iterator.next(); set.add(getRedisBucket(key).get()); } return set; } @Override public Boolean expire(String key, int seconds) { RBucket
bucket = getRedisBucket(key); expire(bucket, seconds); return true; } @Override public void hset(String key, Serializable field, Serializable value) { redissonClient.getMap(key).put(field, value); } @Override public Object hget(String key, Serializable field) { return redissonClient.getMap(key).get(field); } @Override public void hdel(String key, Serializable field) { redissonClient.getMap(key).remove(field); } public void sadd(String key, Serializable value) { redissonClient.getSet(key).add(value); } public Set sall(String key) { return redissonClient.getSet(key).readAll(); } public boolean sdel(String key, Serializable value) { return redissonClient.getSet(key).remove(value); } @Override public boolean lock(String key) { return redissonClient.getLock(key).tryLock(); } @Override public void unlock(String key) { redissonClient.getLock(key).unlock(); } @Override public boolean setnx(String key, Serializable value) { try { return redissonClient.getLock(key).tryLock(Long.valueOf(value.toString()), TimeUnit.MILLISECONDS); } catch (InterruptedException e) { return false; } } @Override public Long incr(String key) { return null; } @Override public void setrange(String key, long offset, String value) { } @Override public String getrange(String key, long startOffset, long endOffset) { return null; }}复制代码
4.5 定义Redisson客户端配置实现类
package com.easylink.mall.core.cache.redisson;import java.util.HashSet;import java.util.Set;import org.apache.commons.lang3.StringUtils;import org.redisson.Redisson;import org.redisson.api.RedissonClient;import org.redisson.config.ClusterServersConfig;import org.redisson.config.Config;import org.redisson.config.MasterSlaveServersConfig;import org.redisson.config.SingleServerConfig;/** * Redis连接配置 *  * @author ShenHuaJie * @since 2017年8月23日 上午9:36:53 */public class Client {	/**	 * Redis server address	 *	 */	private String address;	/**	 * Password for Redis authentication. Should be null if not needed	 */	private String password;	/**	 * Redis cluster node urls list	 */	private Set
nodeAddresses = new HashSet
(); /** * Redis master server address */ private String masterAddress; /** * Redis slave servers addresses */ private Set
slaveAddresses = new HashSet
(); private RedissonClient redissonClient; public void init() { Config config = new Config(); if (StringUtils.isNotBlank(address)) { SingleServerConfig serverConfig = config.useSingleServer().setAddress(address); if (StringUtils.isNotBlank(password)) { serverConfig.setPassword(password); } } else if (!nodeAddresses.isEmpty()) { ClusterServersConfig serverConfig = config.useClusterServers() .addNodeAddress(nodeAddresses.toArray(new String[] {})); if (StringUtils.isNotBlank(password)) { serverConfig.setPassword(password); } } else if (masterAddress != null && !slaveAddresses.isEmpty()) { MasterSlaveServersConfig serverConfig = config.useMasterSlaveServers().setMasterAddress(masterAddress) .addSlaveAddress(slaveAddresses.toArray(new String[] {})); if (StringUtils.isNotBlank(password)) { serverConfig.setPassword(password); } } this.redissonClient = Redisson.create(config); } public RedissonClient getRedissonClient() { return redissonClient; } public void setAddress(String address) { this.address = address; } public void setPassword(String password) { this.password = password; } public void setNodeAddresses(String nodeAddresse) { if (nodeAddresse != null) { String[] nodeAddresses = nodeAddresse.split(","); for (int i = 0; i < nodeAddresses.length; i++) { if (StringUtils.isNotEmpty(nodeAddresses[i])) { this.nodeAddresses.add(nodeAddresses[i]); } } } } public void setMasterAddress(String masterAddress) { this.masterAddress = masterAddress; } public void setSlaveAddresses(String slaveAddresse) { if (slaveAddresse != null) { String[] slaveAddresses = slaveAddresse.split(","); for (int i = 0; i < slaveAddresses.length; i++) { if (StringUtils.isNotEmpty(slaveAddresses[i])) { this.slaveAddresses.add(slaveAddresses[i]); } } } }}复制代码
4.6 定义properties配置文件读取工具类
package com.easylink.mall.core.support.util;import java.util.HashMap;import org.apache.commons.configuration2.PropertiesConfiguration;import org.apache.commons.configuration2.builder.fluent.Configurations;import org.apache.commons.configuration2.ex.ConfigurationException;import org.apache.log4j.Logger;/** * 资源文件读取工具 *  * @author Ben. * */public class PropertiesFileUtil {	private static Logger logger = Logger.getLogger(PropertiesFileUtil.class);	// 当打开多个资源文件时,缓存资源文件	private static HashMap
configMap = new HashMap
(); // 默认资源文件名称 private static final String NAME = "config.properties"; // 私有构造方法,创建单例 private PropertiesFileUtil() { } public static synchronized PropertiesConfiguration getInstance() { return getInstance(NAME); } public static synchronized PropertiesConfiguration getInstance(String name) { PropertiesConfiguration propertiesConfiguration = configMap.get(name); if (propertiesConfiguration == null) { Configurations configs = new Configurations(); try { propertiesConfiguration = configs.properties(name); } catch (ConfigurationException e) { logger.error("can not load properties file,name : " + name); } configMap.put(name, propertiesConfiguration); } return propertiesConfiguration; }}复制代码

5. 测试

编写单元测试用例,测试是否搭建成功

5.1 测试基本操作
package com.easylink.mall.core.test;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import com.easylink.mall.core.cache.redis.CacheUtil;@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(locations = "classpath:spring/spring-redis.xml")public class JedisTest {	 @Test	public void execute() {		CacheUtil.getCache().set("test-jedis", "test-value");		Object testValue = CacheUtil.getCache().get("test-jedis");		System.out.println(String.valueOf(testValue));	}}复制代码
5.2 测试分布式锁
package com.easylink.mall.core.test;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import com.easylink.mall.core.cache.redis.CacheUtil;@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(locations = "classpath:spring/spring-redisson.xml")public class RedissonTest {	@Test	public void execute() {		// 加锁		CacheUtil.getLockManager().lock("test-jedis");		// 解锁		CacheUtil.getLockManager().unlock("test-jedis");	}}复制代码

总结

至此,上述过程已经说明了如何在JAVA中使用Redis进行一些缓存的基本操作或者是当作分布式锁去使用。内容比较简单,基础。但是适用于初学者去学习,毕竟先学会入门的使用,然后再对其的某些功能或者特性去深入研究,这样能让自己更好的去学习一种技术。楼主由于太久没有更新文章,所以先写一篇简单的找一下感觉。迟点再和大家一同探究Redis的一些知识和常见问题,如:基础的数据结构,缓存和数据库一致性问题,缓存雪崩问题,缓存击穿问题等。谢谢大家的支持,如果此文对你有所帮助,请点个赞,谢谢。

转载地址:http://vqxga.baihongyu.com/

你可能感兴趣的文章
Bootstrap3.0学习第十九轮(JavaScript插件——标签页)
查看>>
android 无法生成R文件的原因剖析
查看>>
Android:WebView
查看>>
Ping批量函数
查看>>
ios 向sqlite数据库插入和读取图片数据
查看>>
Ad Muncher 目前半价优惠^_^
查看>>
[转载]将archlinux&nbsp;2013-06-01版,安装配置为个人工作站
查看>>
IOS ----UIButton用法详解
查看>>
nutch 大量网站
查看>>
系统数据库--恢复Master数据库
查看>>
ASP.NET 窗体间传值实现方法详解
查看>>
选择HttpHandler还是HttpModule?
查看>>
Windows Phone开发(46):与Socket有个约会
查看>>
Windows 8 应用开发 - 应用栏
查看>>
判断asp.net中session过期的方法
查看>>
Windows下安装WebLogic
查看>>
No space left on device 解决Linux系统磁盘空间满的办法
查看>>
[转]好文章:Android的AlertDialog详解
查看>>
MD5在线查询的实现
查看>>
Destination Host Unreachable
查看>>