SpringBoot通過JedisCluster連線Redis叢集(分散式專案)
阿新 • • 發佈:2018-11-10
分散式專案 SpringBoot + Redis使用
現在開發流行微服務、分散式,基於這種情況需要使用redis不想每個專案都去寫重複的程式碼,所以我在工具包中封裝了redis這一塊的連線,但是問題是所有的專案都會依賴我的基礎包,卻不是所有的專案都要使用redis。所以我需要根據業務系統的配置檔案中是否配置了redis叢集相關資訊來確定是否去連redis。
首先引入maven包
<dependency>
<groupId>redis.clients</groupId>
<artifactId >jedis</artifactId>
<version>2.9.0</version>
</dependency>
分散式鎖
- 1.新建類JedisLock
package com.xxx.util.redis;
import redis.clients.jedis.JedisCluster;
/**
* Redis distributed lock implementation.
*/
public class JedisLock {
JedisCluster jedis;
/** Lock key path. */
String lockKey;
/** Lock expiration in miliseconds. */
int expireMsecs = 60 * 1000; //鎖超時,防止執行緒在入鎖以後,無限的執行等待
/** Acquire timeout in miliseconds. */
int timeoutMsecs = 10 * 1000; //鎖等待,防止執行緒飢餓
boolean locked = false;
/**
* Detailed constructor with default acquire timeout 10000 msecs and lock expiration of 60000
* msecs.
*
* @param jedis
* @param lockKey lock key (ex. account:1, ...)
*/
public JedisLock(JedisCluster jedis, String lockKey) {
this.jedis = jedis;
this.lockKey = lockKey;
}
/**
* Detailed constructor with default lock expiration of 60000 msecs.
*
* @param jedis
* @param lockKey lock key (ex. account:1, ...)
* @param timeoutMsecs acquire timeout in miliseconds (default: 10000 msecs)
*/
public JedisLock(JedisCluster jedis, String lockKey, int timeoutMsecs) {
this(jedis, lockKey);
this.timeoutMsecs = timeoutMsecs;
}
/**
* Detailed constructor.
*
* @param jedis
* @param lockKey lock key (ex. account:1, ...)
* @param timeoutMsecs acquire timeout in miliseconds (default: 10000 msecs)
* @param expireMsecs lock expiration in miliseconds (default: 60000 msecs)
*/
public JedisLock(JedisCluster jedis, String lockKey, int timeoutMsecs, int expireMsecs) {
this(jedis, lockKey, timeoutMsecs);
this.expireMsecs = expireMsecs;
}
/**
* Detailed constructor with default acquire timeout 10000 msecs and lock expiration of 60000
* msecs.
*
* @param lockKey lock key (ex. account:1, ...)
*/
public JedisLock(String lockKey) {
this(null, lockKey);
}
/**
* Detailed constructor with default lock expiration of 60000 msecs.
*
* @param lockKey lock key (ex. account:1, ...)
* @param timeoutMsecs acquire timeout in miliseconds (default: 10000 msecs)
*/
public JedisLock(String lockKey, int timeoutMsecs) {
this(null, lockKey, timeoutMsecs);
}
/**
* Detailed constructor.
*
* @param lockKey lock key (ex. account:1, ...)
* @param timeoutMsecs acquire timeout in miliseconds (default: 10000 msecs)
* @param expireMsecs lock expiration in miliseconds (default: 60000 msecs)
*/
public JedisLock(String lockKey, int timeoutMsecs, int expireMsecs) {
this(null, lockKey, timeoutMsecs, expireMsecs);
}
/** @return lock key */
public String getLockKey() {
return lockKey;
}
/**
* Acquire lock.
*
* @return true if lock is acquired, false acquire timeouted
* @throws InterruptedException in case of thread interruption
*/
public boolean acquire() throws InterruptedException {
return acquire(jedis);
}
/**
* Acquire lock.
*
* @param jedis
* @return true if lock is acquired, false acquire timeouted
* @throws InterruptedException in case of thread interruption
*/
public boolean acquire(JedisCluster jedis) throws InterruptedException {
int timeout = timeoutMsecs;
while (timeout >= 0) {
long expires = System.currentTimeMillis() + expireMsecs + 1;
String expiresStr = String.valueOf(expires); //鎖到期時間
if (jedis.setnx(lockKey, expiresStr) == 1) {
locked = true;
return true;
}
String currentValueStr = jedis.get(lockKey); //redis裡的時間
if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) {
//判斷是否為空,不為空的情況下,如果被其他執行緒設定了值,則第二個條件判斷是過不去的
String oldValueStr = jedis.getSet(lockKey, expiresStr);
//獲取上一個鎖到期時間,並設定現在的鎖到期時間,
if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {
//如過這個時候,多個執行緒恰好都到了這裡,但是隻有一個執行緒的設定值和當前值相同,他才有權利獲取鎖
locked = true;
return true;
}
}
timeout -= 100;
Thread.sleep(100);
}
return false;
}
/** Acqurired lock release. */
public void release() {
release(jedis);
}
/** Acqurired lock release. */
public void release(JedisCluster jedis) {
if (locked) {
jedis.del(lockKey);
locked = false;
}
}
}
- 2.新建類SimpleLock
package com.xxx.util.redis;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.JedisCluster;
public class SimpleLock {
private static Logger logger = LoggerFactory.getLogger(SimpleLock.class);
private JedisLock jedisLock;
private String lockKey;
private JedisCluster jedis;
private int timeoutMsecs;
private int expireMsecs;
public SimpleLock(String lockKey, JedisCluster jedis) {
this(lockKey, 120000, 300000, jedis);
}
public SimpleLock(String lockKey, int timeoutMsecs, int expireMsecs, JedisCluster jedis) {
this.lockKey = lockKey;
this.jedis = jedis;
this.timeoutMsecs = timeoutMsecs;
this.expireMsecs = expireMsecs;
this.jedisLock = new JedisLock(jedis, lockKey.intern(), timeoutMsecs, expireMsecs);
}
public synchronized void wrap(Runnable runnable) throws Exception {
long begin = System.currentTimeMillis();
try {
logger.info(
"begin lock,lockKey={},timeoutMsecs={},expireMsecs={}",
lockKey,
timeoutMsecs,
expireMsecs);
if (jedisLock.acquire()) { // 啟用鎖
runnable.run();
} else {
logger.info("The time wait for lock more than [{}] ms ", timeoutMsecs);
throw new Exception("can not get lock");
}
} catch (Throwable t) {
// 分散式鎖異常
logger.warn(t.getMessage(), t);
throw t;
} finally {
this.lockRelease(jedisLock);
}
logger.info("[{}]cost={}", lockKey, System.currentTimeMillis() - begin);
}
/**
* 釋放鎖
* @param lock
*/
private void lockRelease(JedisLock lock) {
if (lock != null) {
try {
lock.release();
} catch (Exception e) {
}
}
logger.debug(
"release logck,lockKey={},timeoutMsecs={},expireMsecs={}",
lockKey,
timeoutMsecs,
expireMsecs);
}
}
注:根據實際情況確定是否需要使用到鎖,以上並非一定需要有的。一切的一切都是業務決定
使用JedisCluster連線redis叢集
新建類RedisAutoConfiguration
package com.xxx.util.redis;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import java.util.HashSet;
import java.util.Set;
/**
* @author <a href="mailto:[email protected]">zhibo.lv</a>
*/
@Configuration
public class RedisAutoConfiguration {
@Value("${redis.node1.ip:127.0.0.1}")
private String node1Ip;
@Value("${redis.node1.port:0}")
private int node1Port;
@Value("${redis.node2.ip:127.0.0.1}")
private String node2Ip;
@Value("${redis.node2.port:0}")
private int node2Port;
@Value("${redis.node3.ip:127.0.0.1}")
private String node3Ip;
@Value("${redis.node3.port:0}")
private int node3Port;
@Value("${redis.node.password:null}")
private String password;
@Value("${redis.connection_timeout:2000}")
private int connectionTimeout;
@Value("${redis.so_timeout:2000}")
private int soTimeout;
@Value("${redis.max_attempts:10}")
private int maxAttempts;
@Value("${redis.pool.maxTotal:800}")
private int maxTotal;
@Value("${redis.pool.minIdle:50}")
private int minIdle;
@Value("${redis.pool.maxIdle:200}")
private int maxIdle;
@Value("${redis.pool.maxWait:3000}")
private int maxWaitMillis;
@Bean
public JedisCluster jedisCluster() {
Set<HostAndPort> nodes = new HashSet<HostAndPort>();
if (!node1Ip.equals("127.0.0.1") && !(node1Port == 0)){
nodes.add(new HostAndPort(node1Ip, node1Port));
}
if (!node2Ip.equals("127.0.0.1") && !(node2Port == 0)){
nodes.add(new HostAndPort(node2Ip, node2Port));
}
if (!node3Ip.equals("127.0.0.1") && !(node3Port == 0)){
nodes.add(new HostAndPort(node3Ip, node3Port));
}
JedisCluster jedisCluster = null;
if (!nodes.isEmpty()){
GenericObjectPoolConfig pool = new GenericObjectPoolConfig();
pool.setMaxTotal(maxTotal);
pool.setMinIdle(minIdle);
pool.setMaxIdle(maxIdle);
pool.setMaxWaitMillis(maxWaitMillis);
jedisCluster =
new JedisCluster(nodes, connectionTimeout, soTimeout, maxAttempts, password, pool);
}
return jedisCluster;
}
}
之前有說過因為所有的專案都會依賴我的基礎包,但又不是所有的專案都會去配置redis。所以有了上面的設定預設值 然後判斷ip是否被修改,如果被修改了說明配置的redis相關配置則有了後續的操作。相對比較靈活一點了,同時支援了生產環境測試環境機器數量不一樣的情況。
如何使用?
- 在需要使用redis的相關業務專案上新增如下配置
#redis
redis.connection_timeout=2000
redis.max_attempts=10
redis.pool.maxIdle=200
redis.pool.maxTotal=800
redis.pool.maxWait=3000
redis.pool.minIdle=50
redis.so_timeout=2000
redis.node.password=redisPassword
redis.node1.ip=127.0.0.1
redis.node1.port=8000
redis.node2.ip= 127.0.0.1
redis.node2.port= 8001
redis.node3.ip= 127.0.0.1
redis.node3.port= 8002
- 然後就可以愉快的玩耍了
package com.xxx.redis;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.JedisCluster;
import javax.annotation.PostConstruct;
@Component
public class RedisTest {
private static final Logger log = LoggerFactory.getLogger(RedisTest.class);
@Autowired
private JedisCluster jedisCluster;
/**
* redis key字首
*/
public final static String PREFIX = "token:user:";
@PostConstruct
public void init() {
log.info("init redis jedisCommands -------------");
String token = "hjyqakteqxpfy6431045747319394309";
jedisCluster.set(PREFIX+token,"1000");
String value = jedisCluster.get(PREFIX+token);
log.info("jedisCommands.get(\"test\") = {}------------", value);
Long del = jedisCluster.del(PREFIX+token);
log.info("jedisCommands.get(\"test\") = {}--del-{}----------", jedisCluster.get(PREFIX+token), del);
//設定失效時長
jedisCluster.setex(PREFIX+token,60,"1000");
}
}
- 當然如果你需要用到分散式鎖那麼往下看
package com.xxx.redis;
import com.xxx.util.redis.SimpleLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.JedisCluster;
import javax.annotation.PostConstruct;
@Component
public class RedisTest {
private static final Logger log = LoggerFactory.getLogger(RedisTest.class);
@Autowired
private JedisCluster jedisCluster;
/**
* redis key字首
*/
public final static String PREFIX = "token:user:";
@PostConstruct
public void init() throws Exception{
new SimpleLock("redisKey:test",jedisCluster).wrap(new Runnable() {
@Override
public void run() {
log.info("init redis jedisCommands -------------");
String token = "hjyqakteqxpfy6431045747319394309";
jedisCluster.set(PREFIX+token,"1000");
String value = jedisCluster.get(PREFIX+token);
log.info("jedisCommands.get(\"test\") = {}------------", value);
Long del = jedisCluster.del(PREFIX+token);
log.info("jedisCommands.get(\"test\") = {}--del-{}----------", jedisCluster.get(PREFIX+token), del);
//設定失效時長
jedisCluster.setex(PREFIX+token,60,"1000");
}
});
}
}
本文為博主原創文章,轉載請註明出處。謝謝!