1. 程式人生 > >SpringBoot通過JedisCluster連線Redis叢集(分散式專案)

SpringBoot通過JedisCluster連線Redis叢集(分散式專案)

分散式專案 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");
            }
        });
    }
}

本文為博主原創文章,轉載請註明出處。謝謝!