金麟豈是池中物,一遇風雲便化龍
前言
微服務如今的發展趨已然是勢不可擋,並且業內已經有Dubbo、SpringCloud等優秀的開源框架,可以說發展的已經是非常成熟了,所以決定順應技術發展浪潮,放棄老舊而臃腫的SSM,轉而搭建以SpringCloud爲基礎的微服務應用。
通過Redis實現緩存與應用集羣間的Session共享是很成熟的應用緩存層解決方案,所以在框架搭建過程中必然是需要整合Redis,並且通過Redis的Sentinel實現了Redis的高可用。在整合Redis的過程中始終沒有找到比較好用的解決方案,看過很多博客,發現都不是自己想要的,所以索性決定自己來寫一個,僅供參考。
依賴注入
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
項目路徑如下
redis.properties
雖然SpringCloud簡化項目配置,但是絕對不是要求研發人員在項目中不可以使用配置,還是那句話約定大於配置,習慣大於約定。所以我們在項目中建立redis.properties用於配置Redis服務。
redis.nodes=192.168.1.205:26379,192.168.1.206:26379,192.168.1.207:26379
redis.masterName=artisanRedis
redis.password=abc123456
redis.maxTotal=10000
redis.maxIdle=100
redis.minIdle=50
redis.timeout=30000
RedisConfig
SpringCloud給我們帶了很多開箱即用的工具,能夠讓我們的代碼更加的優雅。@ConfigurationProperties 讓我們不再爲讀取配置文件而感到煩惱,在SpringBoot 1.5+後需要配合@PropertySource使用,RedisConfig.java便優雅的讀取了Redis配置文件。
package com.artisan.redis.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
/**
*
* @author Fire Monkey
* @date 2018/3/12 下午6:25
* Redis配置文件
*
*/
@Component
@PropertySource("classpath:conf/redis.properties")
@ConfigurationProperties(prefix = "redis")
public class RedisConfig {
/**
* 節點名稱
*/
private String nodes;
/**
* Redis服務名稱
*/
private String masterName;
/**
* 密碼
*/
private String password;
/**
* 最大連接數
*/
private int maxTotal;
/**
* 最大空閒數
*/
private int maxIdle;
/**
* 最小空閒數
*/
private int minIdle;
/**
* 連接超時時間
*/
private int timeout;
public String getNodes() {
return nodes;
}
public void setNodes(String nodes) {
this.nodes = nodes;
}
public String getMasterName() {
return masterName;
}
public void setMasterName(String masterName) {
this.masterName = masterName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getMaxTotal() {
return maxTotal;
}
public void setMaxTotal(int maxTotal) {
this.maxTotal = maxTotal;
}
public int getMaxIdle() {
return maxIdle;
}
public void setMaxIdle(int maxIdle) {
this.maxIdle = maxIdle;
}
public int getMinIdle() {
return minIdle;
}
public void setMinIdle(int minIdle) {
this.minIdle = minIdle;
}
public int getTimeout() {
return timeout;
}
public void setTimeout(int timeout) {
this.timeout = timeout;
}
@Override
public String toString() {
return "RedisConfig{" +
"nodes='" + nodes + '\'' +
", masterName='" + masterName + '\'' +
", password='" + password + '\'' +
", maxTotal='" + maxTotal + '\'' +
", maxIdle='" + maxIdle + '\'' +
", minIdle='" + minIdle + '\'' +
", timeout='" + timeout + '\'' +
'}';
}
}
RedisInitConfig
簡約配置問風格讓我們不能再像從前一樣,隨心所欲的在Spring.xml文件中實例化某個Bean,但是註解@Bean卻爲我們打開了一扇窗。RedisInitConfig.java文件中創建了JedisPoolConfig,JedisSentinelPool這兩個重要的對象,爲後面RedisService對外提供Jedis奠定了基礎。
package com.artisan.redis.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisSentinelPool;
import java.util.HashSet;
import java.util.Set;
/**
*
* @author Fire Monkey
* @date 2018/3/12 下午6:28
* redis初始化配置
*
*/
@Configuration
public class RedisInitConfig {
/**
* 日誌打印對象
*/
private Logger log = LoggerFactory.getLogger(RedisInitConfig.class);
/**
* 注入配置文件信息
*/
@Autowired
private RedisConfig redisConfig;
/**
*
* @author Fire Monkey
* @date 2018/3/12 下午6:53
* @return redis.clients.jedis.JedisPoolConfig
* 初始化連接池配置對象
*
*/
@Bean(value = "jedisPoolConfig")
public JedisPoolConfig initJedisPoolConfig(){
log.info("JedisPool initialize start ...");
JedisPoolConfig config = new JedisPoolConfig();
//最大總量
config.setMaxTotal(redisConfig.getMaxTotal());
//設置最大空閒數量
config.setMaxIdle(redisConfig.getMaxIdle());
//設置最小空閒數量
config.setMinIdle(redisConfig.getMinIdle());
//常規配置
config.setTestOnBorrow(true);
config.setTestOnReturn(true);
log.info("JedisPool initialize end ...");
return config;
}
/**
*
* @author Fire Monkey
* @date 下午7:20
* @return redis.clients.jedis.JedisSentinelPool
* 生成JedisSentinelPool並且放入Spring容器
*
*/
@Bean(value = "sentinelPool")
public JedisSentinelPool initJedisPool(@Qualifier(value = "jedisPoolConfig") JedisPoolConfig jedisPoolConfig){
Set<String> nodeSet = new HashSet<>();
//獲取到節點信息
String nodeString = redisConfig.getNodes();
//判斷字符串是否爲空
if(nodeString == null || "".equals(nodeString)){
log.error("RedisSentinelConfiguration initialize error nodeString is null");
throw new RuntimeException("RedisSentinelConfiguration initialize error nodeString is null");
}
String[] nodeArray = nodeString.split(",");
//判斷是否爲空
if(nodeArray == null || nodeArray.length == 0){
log.error("RedisSentinelConfiguration initialize error nodeArray is null");
throw new RuntimeException("RedisSentinelConfiguration initialize error nodeArray is null");
}
//循環注入至Set中
for(String node : nodeArray){
log.info("Read node : {}。" , node);
nodeSet.add(node);
}
//創建連接池對象
JedisSentinelPool jedisPool = new JedisSentinelPool(redisConfig.getMasterName() ,nodeSet ,jedisPoolConfig ,redisConfig.getTimeout() , redisConfig.getPassword());
return jedisPool;
}
}
RedisService
前面已經將JedisSentinelPool實例化,RedisService只需要將該對象注入,通過JedisSentinelPool獲取到Jedis,對外提供緩存服務,至此SpringCloud整合Jedis結束。
package com.artisan.redis.service;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisSentinelPool;
import redis.clients.jedis.exceptions.JedisConnectionException;
/**
*
* @author Fire Monkey
* @date 2018/3/12 下午7:32
* Reids工具通過Jedis實現與Redis交互
*
*/
@Component
public class RedisService {
/**
* 日誌打印對象
*/
private static Logger log = Logger.getLogger(RedisService.class);
/**
* Jedis對象池 所有Jedis對象均通過該池租賃獲取
*/
private static JedisSentinelPool sentinelPool;
/**
* 緩存數據成功
*/
private final static String CACHE_INFO_SUCCESS = "OK";
/**
*
* @author Fire Monkey
* @date 2018/3/12 下午7:34
* 注入JedisSentinelPool
*
*/
@Autowired
public void setSentinelPool(JedisSentinelPool sentinelPool) {
RedisService.sentinelPool = sentinelPool;
}
/**
*
* @author Fire Monkey
* @date 2018/3/12 下午7:35
* @return redis.clients.jedis.Jedis
* 獲取到Jedis
*
*/
private static Jedis getJedis() {
Jedis jedis;
try {
jedis = sentinelPool.getResource();
return jedis;
} catch (JedisConnectionException e) {
log.error("獲取Redis 異常", e);
throw e;
}
}
/**
*
* @author Fire Monkey
* @date 2018/3/12 下午7:36
* 緩存String類型的數據,數據不過期
*
*/
public static boolean setString(String key, String value) throws Exception {
return setString(key, value, -1);
}
/**
*
* @author Fire Monkey
* @date 2018/3/12 下午7:40
* 緩存String類型的數據並設置超時時間
*
*/
public static boolean setString(String key, String value, int timeout) throws Exception {
String result;
Jedis jedis = null;
try {
jedis = getJedis();
result = jedis.set(key, value);
if (timeout != -1) {
jedis.expire(key, timeout);
}
if (CACHE_INFO_SUCCESS.equals(result)) {
return true;
} else {
return false;
}
} finally {
releaseRedis(jedis);
}
}
/**
*
* @author Fire Monkey
* @date 2018/3/12 下午7:46
* 獲取String類型的數據
*
*/
public static String getString(String key) throws Exception {
Jedis jedis = null;
try {
jedis = getJedis();
return jedis.get(key);
} catch (Exception e) {
throw e;
} finally {
releaseRedis(jedis);
}
}
/**
*
* @author Fire Monkey
* @date 2018/3/12 下午7:49
* @return void
* 釋放Jedis
*
*/
public static void releaseRedis(Jedis jedis) {
if (jedis != null) {
jedis.close();
}
}
/**
*
* @author Fire Monkey
* @date 2018/3/12 下午7:55
* @return boolean
* 通過key刪除緩存中數據
*
*/
public static boolean del(String key) throws Exception {
Long num;
Jedis jedis = null;
Boolean result = false;
try {
jedis = getJedis();
num = jedis.del(key);
if (num.equals(1L)) {
result = true;
}
}finally {
releaseRedis(jedis);
}
return result;
}
}
GitHub地址
github傳送門:[email protected]:MarsMuse/ArtisanCloud.git
寫在最後
這是作者從業來的第一篇博客,寫的不好的地方還請各位見諒,如果這篇博客能夠爲您帶來哪怕是絲毫幫助,我也深感榮幸。