JAVA快取-Redis入門級使用

liangzzz發表於2018-09-04

#前言

Java快取實現方案有很多,最基本的自己使用Map去構建快取,再高階點的使用Ehcache或者Goolgeguava作為記憶體快取框架,Ehcache可以滿足單機快取的需求(Ehcache的具體使用在我過往的文章中有所介紹),如果我們是多臺機子共用快取資料的話,Ehcache可通過rmijgroupjms的方式實現,但是實用性與操作性不高且複雜,現時大部分應用僅用Ehcache作為單機快取使用,這時候我們可以通過搭建快取伺服器解決多機使用的問題,常見的快取伺服器有MemcachedRedis等。

現時業界主流大多使用Redis。所以本文主要介紹在Java中如何使用Redis。至於如何搭建Redis,我在過往的文章中已有所介紹,不知道如何搭建的同學,可以參考我過往的文章,下文所用到相關的Redis資訊均為搭建教程中的資訊。

PS:文章中所用到的示例程式碼,部分參考至開源專案iBase4J,特此宣告。

本文同步釋出於簡書 :www.jianshu.com/p/5a9946870…

Java連線Redis

Java連線Redis官方推薦的是使用JedisRedisson進行連線操作,SpringRedis有很好的支援,所以此文我結合Spring中的Spring DataRedis進行操作。

1. maven引用

<dependency>
	<groupId>org.springframework.data</groupId>
	<artifactId>spring-data-redis</artifactId>
	<version>1.8.7.RELEASE</version>
</dependency>
<dependency>
	<groupId>redis.clients</groupId>
	<artifactId>jedis</artifactId>
	<version>2.9.0</version>
</dependency>
<dependency>
	<groupId>org.redisson</groupId>
	<artifactId>redisson</artifactId>
	<version>3.5.5</version>
</dependency>
複製程式碼

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.250
redis.host=172.16.2.185
redis.port=6379
redis.password=123456
#最小空閒數
redis.minIdle=2
#最大空閒數
redis.maxIdle=10
#最大連線數
redis.maxTotal=1000
#最大建立連線等待時間
redis.maxWaitMillis=3000
#客戶端超時時間單位是毫秒
redis.timeout=120000
#明是否在從池中取出連線前進行檢驗,如果檢驗失敗,則從池中去除連線並嘗試取出另一個  
redis.testOnBorrow=true
redis.expiration=600
複製程式碼

3. 與Spring結合

3.1 配置Jedis實現

classpath下建立資料夾spring用於存放所有與spring相關的配置檔案。在spring資料夾下建立spring-redis.xml,主要用於注入Jedis

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd 
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd"
>
	<context:property-placeholder location="classpath*:redis.properties" ignore-unresolvable="true"/>
	
	<!-- jedis 配置-->
    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig" >
        <!--最小空閒數-->  
        <property name="minIdle" value="${redis.minIdle}" />
        <!--最大空閒數-->  
        <property name="maxIdle" value="${redis.maxIdle}" />
        <!--最大連線數-->  
        <property name="maxTotal" value="${redis.maxTotal}" />
        <!--最大建立連線等待時間-->  
        <property name="maxWaitMillis" value="${redis.maxWaitMillis}" />
        <!--是否在從池中取出連線前進行檢驗,如果檢驗失敗,則從池中去除連線並嘗試取出另一個-->  
        <property name="testOnBorrow" value="${redis.testOnBorrow}" />  
    </bean >
		
	<!-- redis伺服器中心 -->
	<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
      <property name="poolConfig" ref="jedisPoolConfig"/>
      <property name="port" value="${redis.port}"/>
      <property name="hostName" value="${redis.host}"/>
      <property name="password" value="${redis.password}"/>
      <property name="timeout" value="${redis.timeout}" />
  </bean>
	<!-- 快取序列化方式 -->
	<bean id="keySerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer" />
	
	<bean id="valueSerializer" class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer" />
	
	<!-- 快取 -->
	<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
		<property name="connectionFactory" ref="jedisConnectionFactory" />
		<property name="enableTransactionSupport" value="true" />
		<property name="keySerializer" ref="keySerializer" />
		<property name="valueSerializer" ref="valueSerializer" />
		<property name="hashKeySerializer" ref="keySerializer" />
		<property name="hashValueSerializer" ref="valueSerializer" />
	</bean>
	
	<bean class="com.easylink.mall.core.cache.redis.RedisHelper" >
		<property name="redisTemplate" ref="redisTemplate" />
	</bean>
	<!-- 快取管理 -->
	<bean id="redisCacheManager" class="org.springframework.data.redis.cache.RedisCacheManager">
		<constructor-arg index="0" ref="redisTemplate" />
		<property name="transactionAware" value="true" />
		<property name="defaultExpiration" value="${redis.expiration}" />
	</bean>
</beans>
複製程式碼
3.2 配置Redisson實現

spring資料夾下建立spring-redisson.xml,主要用於注入Redisson

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd 
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd"
>
	<context:property-placeholder location="classpath*:redis.properties" ignore-unresolvable="true"/>
	
	<!-- redisson 配置-->
	<bean id="redissonClient" class="com.easylink.mall.core.cache.redisson.Client" init-method="init">
		<property name="password" value="${redis.password}" />
		<!-- SingleServer -->
		<property name="address" value="redis://${redis.host}:${redis.port}" />
		<!-- ClusterServers -->
		<!-- <property name="nodeAddresses" value="${redis.cluster}}" /> -->
		<!-- MasterSlaveServers -->
		<!-- <property name="masterAddress" value="${redis.master}" />
		<property name="slaveAddresses" value="${redis.slave}" /> -->
	</bean>
	
	<!-- redisson 工具類 -->
	<bean class="com.easylink.mall.core.cache.redisson.RedissonHelper">
		<property name="redissonClient" ref="redissonClient" />
	</bean>
</beans>
複製程式碼

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<Object> 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<String> keySerializer;
	private RedisSerializer<Object> valueSerializer;
	private RedisTemplate<Serializable, Serializable> redisTemplate;
	private final Integer EXPIRE = PropertiesFileUtil.getInstance("redis.properties").getInt("redis.expiration");

	@SuppressWarnings("unchecked")
	public void setRedisTemplate(RedisTemplate<Serializable, Serializable> redisTemplate) {
		this.redisTemplate = redisTemplate;
		this.keySerializer = (RedisSerializer<String>) redisTemplate.getKeySerializer();
		this.valueSerializer = (RedisSerializer<Object>) 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<Object> getAll(final String pattern) {
		Set<Object> values = new HashSet<Object>();
		Set<Serializable> 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<Object> getRedisBucket(String key) {
		return redissonClient.getBucket(key);
	}

	@Override
	public final Object get(final String key) {
		RBucket<Object> temp = getRedisBucket(key);
		expire(temp, EXPIRE);
		return temp.get();
	}

	@Override
	public final void set(final String key, final Serializable value) {
		RBucket<Object> temp = getRedisBucket(key);
		temp.set(value);
		expire(temp, EXPIRE);
	}

	@Override
	public final void set(final String key, final Serializable value, int seconds) {
		RBucket<Object> temp = getRedisBucket(key);
		temp.set(value);
		expire(temp, seconds);
	}

	public final void multiSet(final Map<String, Object> temps) {
		redissonClient.getBuckets().set(temps);
	}

	@Override
	public final Boolean exists(final String key) {
		RBucket<Object> 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<Object> 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<Object> rBucket = getRedisBucket(key);
		return rBucket.remainTimeToLive();
	}

	@Override
	public final Object getSet(final String key, final Serializable value) {
		RBucket<Object> rBucket = getRedisBucket(key);
		return rBucket.getAndSet(value);
	}

	@Override
	public Set<Object> getAll(String pattern) {
		Set<Object> set = new HashSet<Object>();
		Iterable<String> keys = redissonClient.getKeys().getKeysByPattern(pattern);
		for (Iterator<String> 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<Object> 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<Object> 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<String> nodeAddresses = new HashSet<String>();

	/**
	 * Redis master server address
	 */
	private String masterAddress;

	/**
	 * Redis slave servers addresses
	 */
	private Set<String> slaveAddresses = new HashSet<String>();

	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<String, PropertiesConfiguration> configMap = new HashMap<String, PropertiesConfiguration>();
	// 預設資原始檔名稱
	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的一些知識和常見問題,如:基礎的資料結構,快取和資料庫一致性問題,快取雪崩問題,快取擊穿問題等。謝謝大家的支援,如果此文對你有所幫助,請點個贊,謝謝。

相關文章