1.spring-data-redis與Jedis簡單整合
spring-data-redis與Jedis簡單整合,Redis沒有任何集羣只是單節點工做,使用鏈接池
1.建立spring-context-jedis.xml配置文件
<?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" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation=" http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd" default-lazy-init="false"> <!-- 鏈接池配置. --> <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"> <!-- 鏈接池中最大鏈接數。高版本:maxTotal,低版本:maxActive --> <property name="maxTotal" value="8" /> <!-- 鏈接池中最大空閒的鏈接數. --> <property name="maxIdle" value="4" /> <!-- 鏈接池中最少空閒的鏈接數. --> <property name="minIdle" value="1" /> <!-- 當鏈接池資源耗盡時,調用者最大阻塞的時間,超時將跑出異常。單位,毫秒數;默認爲-1.表示永不超時。高版本:maxWaitMillis,低版本:maxWait --> <property name="maxWaitMillis" value="5000" /> <!-- 鏈接空閒的最小時間,達到此值後空閒鏈接將可能會被移除。負值(-1)表示不移除. --> <property name="minEvictableIdleTimeMillis" value="300000" /> <!-- 對於「空閒連接」檢測線程而言,每次檢測的連接資源的個數。默認爲3 --> <property name="numTestsPerEvictionRun" value="3" /> <!-- 「空閒連接」檢測線程,檢測的週期,毫秒數。若是爲負值,表示不運行「檢測線程」。默認爲-1. --> <property name="timeBetweenEvictionRunsMillis" value="60000" /> <!-- testOnBorrow:向調用者輸出「連接」資源時,是否檢測是有有效,若是無效則從鏈接池中移除,並嘗試獲取繼續獲取。默認爲false。建議保持默認值. --> <!-- testOnReturn:向鏈接池「歸還」連接時,是否檢測「連接」對象的有效性。默認爲false。建議保持默認值.--> <!-- testWhileIdle:向調用者輸出「連接」對象時,是否檢測它的空閒超時;默認爲false。若是「連接」空閒超時,將會被移除。建議保持默認值. --> <!-- whenExhaustedAction:當「鏈接池」中active數量達到閥值時,即「連接」資源耗盡時,鏈接池須要採起的手段, 默認爲1(0:拋出異常。1:阻塞,直到有可用連接資源。2:強制建立新的連接資源) --> </bean> <!-- Spring提供的Redis鏈接工廠 --> <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" destroy-method="destroy"> <!-- 鏈接池配置. --> <property name="poolConfig" ref="jedisPoolConfig" /> <!-- Redis服務主機. --> <property name="hostName" value="192.168.110.101" /> <!-- Redis服務端口號. --> <property name="port" value="6379" /> <!-- Redis服務鏈接密碼. --> <!-- <property name="password" value="${redis.password}" /> --> <!-- 連超時設置. --> <property name="timeout" value="15000" /> <!-- 是否使用鏈接池. --> <property name="usePool" value="true" /> </bean> <!-- Spring提供的訪問Redis類. --> <bean id="jedisTemplate" class="org.springframework.data.redis.core.RedisTemplate"> <property name="connectionFactory" ref="jedisConnectionFactory" /> <property name="keySerializer"> <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" /> </property> <property name="valueSerializer"> <!-- <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" /> --> <bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer" /> </property> </bean></beans>
2.使用Spring提供的RedisTemplate類
public static void main(String[] args){ ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-context-jedis.xml"); // 獲取Spring提供的RedisTemplate類此類封裝了Jedis,簡化操做 RedisTemplate<String, List<String>> redisTemplate = applicationContext.getBean("jedisTemplate", RedisTemplate.class); // Spring 提供的各類Redis結構的key-value操做類 ValueOperations<String, List<String>> value = redisTemplate.opsForValue(); HashOperations<String, Object, Object> hash = redisTemplate.opsForHash(); ListOperations<String, List<String>> list = redisTemplate.opsForList(); HyperLogLogOperations<String, List<String>> hyperLogLog = redisTemplate.opsForHyperLogLog(); SetOperations<String, List<String>> set = redisTemplate.opsForSet(); ZSetOperations<String, List<String>> zSet = redisTemplate.opsForZSet(); List<String> listValue = new ArrayList<String>(); listValue.add("001"); listValue.add("002"); value.set("list", listValue); System.out.println(value.get("list")); // 關閉Spring容器釋放資源 applicationContext.close();}
3.關於RedisTemplate類源碼學習
RedisTemplate類的屬性
private boolean enableTransactionSupport = false;private boolean exposeConnection = false;private boolean initialized = false;private boolean enableDefaultSerializer = true;// 默認的序列化實現private RedisSerializer<?> defaultSerializer = new JdkSerializationRedisSerializer();// 各類操做的序列化方式定義private RedisSerializer keySerializer = null;private RedisSerializer valueSerializer = null;private RedisSerializer hashKeySerializer = null;private RedisSerializer hashValueSerializer = null;private RedisSerializer<String> stringSerializer = new StringRedisSerializer();private ScriptExecutor<K> scriptExecutor;// Spring 提供的各類Redis結構的key-value操做類// cache singleton objects (where possible)private ValueOperations<K, V> valueOps;private ListOperations<K, V> listOps;private SetOperations<K, V> setOps;private ZSetOperations<K, V> zSetOps;private HyperLogLogOperations<K, V> hllOps;
在一個應用中RedisTemplate類對象能夠是單例的,由於其屬性「 valueOps、listOps、setOps、zSetOps、hllOps
」的各類操做也是線程安全的,源碼以下:
獲取其
「
valueOps
、
listOps
、
setOps
、
zSetOps
、
hllOps
」
屬性:
public ValueOperations<K, V> opsForValue(){ if (valueOps == null) { valueOps = new DefaultValueOperations<K, V>(this); } return valueOps;}public ListOperations<K, V> opsForList(){ if (listOps == null) { listOps = new DefaultListOperations<K, V>(this); } return listOps;}// 省略部分......
其屬性「
valueOps
、
listOps
、
setOps
、
zSetOps
、
hllOps
」的各類操做也是線程安全的,例如
valueOps
屬性對象默認實現類
DefaultValueOperations
<
K
,
V
>
源碼:
public void set(K key, V value){ final byte[] rawValue = rawValue(value); execute(new ValueDeserializingRedisCallback(key) { protected byte[] inRedis(byte[] rawKey, RedisConnection connection) { connection.set(rawKey, rawValue); return null; } }, true);}// 省略其餘操做......
再看看
execute
的實現以下:
//org.springframework.data.redis.core.AbstractOperations<K, V><T> T execute(RedisCallback<T> callback, boolean b) { return template.execute(callback, b);}// template.execute實現以下:// org.springframework.data.redis.core.RedisTemplate<K, V>public <T> T execute(RedisCallback<T> action, boolean exposeConnection) { return execute(action, exposeConnection, false);}// execute實現以下:// org.springframework.data.redis.core.RedisTemplate<K, V> --- 最終實現public <T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline) { Assert.isTrue(initialized, "template not initialized; call afterPropertiesSet() before using it"); Assert.notNull(action, "Callback object must not be null"); RedisConnectionFactory factory = getConnectionFactory(); RedisConnection conn = null; try { if (enableTransactionSupport) { // only bind resources in case of potential transaction synchronization conn = RedisConnectionUtils.bindConnection(factory, enableTransactionSupport); } else { conn = RedisConnectionUtils.getConnection(factory); } boolean existingConnection = TransactionSynchronizationManager.hasResource(factory); RedisConnection connToUse = preProcessConnection(conn, existingConnection); boolean pipelineStatus = connToUse.isPipelined(); if (pipeline && !pipelineStatus) { connToUse.openPipeline(); } RedisConnection connToExpose = (exposeConnection ? connToUse : createRedisConnectionProxy(connToUse)); T result = action.doInRedis(connToExpose); // close pipeline if (pipeline && !pipelineStatus) { connToUse.closePipeline(); } // TODO: any other connection processing? return postProcessResult(result, connToUse, existingConnection); } finally { if (!enableTransactionSupport) { RedisConnectionUtils.releaseConnection(conn, factory); } }}
此時已經能夠看出每次操做都會建立一個新的
RedisConnection
對象使用完成會調用
RedisConnectionUtils
.
releaseConnection
(
conn
,
factory
)
方法釋放鏈接,若想查看其建立
RedisConnection
鏈接和
RedisConnectionUtils
.
releaseConnection
(
conn
,
factory
)
釋放鏈接過程可繼續查看其源碼,此處就不贅述了。
2.JedisConnectionFactory中使用sentinel集羣
1.在
spring-context-jedis.xml
中配置sentinel信息
<!-- Redis sentinel集羣配置 --><bean id="sentinelConfig" class="org.springframework.data.redis.connection.RedisSentinelConfiguration"> <constructor-arg index="0" type="java.lang.String" value="master001" /> <constructor-arg index="1" type="java.util.Set"> <set> <value>192.168.110.100:26379</value> <value>192.168.110.100:36379</value> <value>192.168.110.100:46379</value> </set> </constructor-arg></bean><!-- Spring提供的Redis鏈接工廠 --><bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" destroy-method="destroy"> <!-- Redis sentinel集羣配置 --> <constructor-arg index="0" type="org.springframework.data.redis.connection.RedisSentinelConfiguration" ref="sentinelConfig" /> <!-- 鏈接池配置. --> <constructor-arg index="1" type="redis.clients.jedis.JedisPoolConfig" ref="jedisPoolConfig" /> <!-- Redis服務主機. --> <property name="hostName" value="192.168.110.101" /> <!-- Redis服務端口號. --> <property name="port" value="6379" /> <!-- Redis服務鏈接密碼. --> <!-- <property name="password" value="${redis.password}" /> --> <!-- 連超時設置. --> <property name="timeout" value="15000" /> <!-- 是否使用鏈接池. --> <property name="usePool" value="true" /></bean>
2.使用測試代碼
public static void main(String[] args){ ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-context-jedis.xml"); // 獲取Spring提供的RedisTemplate類此類封裝了Jedis,簡化操做 RedisTemplate<String, String> redisTemplate = applicationContext.getBean("jedisTemplate", RedisTemplate.class); ValueOperations<String, String> value = redisTemplate.opsForValue(); value.set("K001", "V001"); System.out.println(value.get("K001")); // 關閉Redis Master服務 Scanner scanner = new Scanner(System.in); String input = scanner.nextLine(); System.out.println(input); value.set("K002", "V002"); System.out.println(value.get("K002")); // 關閉Spring容器釋放資源 applicationContext.close();}
代碼輸出,注意中間的打印:
2015-10-3 15:10:59 redis.clients.jedis.JedisSentinelPool initSentinels信息: Trying to find master from available Sentinels...2015-10-3 15:10:59 redis.clients.jedis.JedisSentinelPool initSentinels信息: Redis master running at 192.168.110.101:6379, starting Sentinel listeners...2015-10-3 15:10:59 redis.clients.jedis.JedisSentinelPool initPool信息: Created JedisPool to master at 192.168.110.101:6379V0012015-10-3 15:11:38 redis.clients.jedis.JedisSentinelPool initPool信息: Created JedisPool to master at 192.168.110.103:6379V002
3.JedisConnectionFactory中使用JedisShardInfo
Spring-Data-Redis好像並不支持Redis分片集羣,可是JedisConnectionFactory源碼中有一個JedisShardInfo屬性,源碼以下:
- private JedisShardInfo shardInfo;
// 省略......public void afterPropertiesSet() { if (shardInfo == null) { shardInfo = new JedisShardInfo(hostName, port); if (StringUtils.hasLength(password)) { shardInfo.setPassword(password); } if (timeout > 0) { setTimeoutOn(shardInfo, timeout); } } if (usePool) { this.pool = createPool(); }}// 省略......protected Jedis fetchJedisConnector() { try { if (usePool && pool != null) { return pool.getResource(); } Jedis jedis = new Jedis(getShardInfo()); // force initialization (see Jedis issue #82) jedis.connect(); return jedis; } catch (Exception ex) { throw new RedisConnectionFailureException("Cannot get Jedis connection", ex); }}
-------------------------------------------------------------------------------------------------------------------------------