1. 程式人生 > >springboot通過redis實現分散式鎖實現並呼叫

springboot通過redis實現分散式鎖實現並呼叫



import org.springframework.data.redis.core.RedisTemplate;


public class RedisLock {


    private RedisTemplate redisTemplate;
    /**
     * 重試時間
     */
    private static final int DEFAULT_ACQUIRY_RETRY_MILLIS = 100;
    /**
     * 鎖的字尾
     */
    private static final String LOCK_SUFFIX = "_redis_lock";
    /**
     * 鎖的key
     */
    private String lockKey;
    /**
     * 鎖超時時間,防止執行緒在入鎖以後,防止阻塞後面的執行緒無法獲取鎖
     */
    private int expireMsecs = 60 * 1000;
    /**
     * 執行緒獲取鎖的等待時間
     */
    private int timeoutMsecs = 10 * 1000;
    /**
     * 是否鎖定標誌
     */
    private volatile boolean locked = false;


    /**
     * 構造器
     * 
     * @param redisTemplate
     * @param lockKey 鎖的key
     */
    public RedisLock(String lockKey) {
        this.redisTemplate = (RedisTemplate) SpringContextUtil.getBean("clusterRedisTemplate");
        this.lockKey = lockKey + LOCK_SUFFIX;
    }


    /**
     * 構造器
     * 
     * @param redisTemplate
     * @param lockKey 鎖的key
     * @param timeoutMsecs 獲取鎖的超時時間
     */
    public RedisLock(String lockKey, int timeoutMsecs) {
        this(lockKey);
        this.timeoutMsecs = timeoutMsecs;
    }


    /**
     * 構造器
     * 
     * @param redisTemplate
     * @param lockKey 鎖的key
     * @param timeoutMsecs 獲取鎖的超時時間
     * @param expireMsecs 鎖的有效期
     */
    public RedisLock(String lockKey, int timeoutMsecs, int expireMsecs) {
        this(lockKey, timeoutMsecs);
        this.expireMsecs = expireMsecs;
    }


    public String getLockKey() {
        return lockKey;
    }


    /**
     * 封裝和jedis方法
     * 
     * @param key
     * @return
     */
    private String get(final String key) {
        Object obj = redisTemplate.opsForValue().get(key);
        return obj != null ? obj.toString() : null;
    }


    /**
     * 封裝和jedis方法
     * 
     * @param key
     * @param value
     * @return
     */
    private boolean setNX(final String key, final String value) {
        return redisTemplate.opsForValue().setIfAbsent(key, value);
    }


    /**
     * 封裝和jedis方法
     * 
     * @param key
     * @param value
     * @return
     */
    private String getSet(final String key, final String value) {
        Object obj = redisTemplate.opsForValue().getAndSet(key, value);
        return obj != null ? (String) obj : null;
    }


    /**
     * 獲取鎖
     * 
     * @return 獲取鎖成功返回ture,超時返回false
     * @throws InterruptedException
     */
    public synchronized boolean lock() throws InterruptedException {
        int timeout = timeoutMsecs;
        while (timeout >= 0) {
            long expires = System.currentTimeMillis() + expireMsecs + 1;
            String expiresStr = String.valueOf(expires); // 鎖到期時間
            if (this.setNX(lockKey, expiresStr)) {
                locked = true;
                return true;
            }
            // redis裡key的時間
            String currentValue = this.get(lockKey);
            // 判斷鎖是否已經過期,過期則重新設定並獲取
            if (currentValue != null && Long.parseLong(currentValue) < System.currentTimeMillis()) {
                // 設定鎖並返回舊值
                String oldValue = this.getSet(lockKey, expiresStr);
                // 比較鎖的時間,如果不一致則可能是其他鎖已經修改了值並獲取
                if (oldValue != null && oldValue.equals(currentValue)) {
                    locked = true;
                    return true;
                }
            }
            timeout -= DEFAULT_ACQUIRY_RETRY_MILLIS;
            // 延時
            Thread.sleep(DEFAULT_ACQUIRY_RETRY_MILLIS);
        }
        return false;
    }


    /**
     * 釋放獲取到的鎖
     */
    public synchronized void unlock() {
        if (locked) {
            redisTemplate.delete(lockKey);
            locked = false;
        }
    }


}


//鎖的使用


 @RequestMapping(value = "/xx", method = RequestMethod.POST)
    public void xx(HttpSession session, String custId) {


        LoginUserVO user = (LoginUserVO)session.getAttribute("user");
        if(user == null) {
            return RestResponseUtil.err(ServiceErrorCode.LOGIN_EXPIRE.getCode(),"未拿到當前登入使用者資訊!");
        }
        //判斷當前使用者custId和前端的custId是否一致
        if(!user.getCustId().equals(custId)){
            return RestResponseUtil.err(ServiceErrorCode.CUST_INFO_MODIFY_ERROR.getCode(),"客戶資訊被篡改!");
        }


        RedisLock lock = new RedisLock("unionCaptcha_" + custId);
        try {
//沒有獲取到鎖
            if (!lock.lock()) {
                return RestResponseUtil.err(ServiceErrorCode.REQUEST_REPEAT_ERROR.getCode(), "請不要頻繁請求驗證碼!");
            }
            //獲取到鎖
   //呼叫服務
            
        } catch (ServiceException e) {
            log.error("custId : " + custId + "xx:", e);
           
        } catch (Exception e) {
            log.error("custId : " + custId + "請求異常:", e);
        } finally {
    //釋放鎖
            lock.unlock();
        }



    }

//applicationContext工具類

public class SpringContextUtil {  
    
    private static ApplicationContext applicationContext;  
  
    //獲取上下文  
    public static ApplicationContext getApplicationContext() {  
        return applicationContext;  
    }  
  
    //設定上下文  
    public static void setApplicationContext(ApplicationContext applicationContext) {  
        SpringContextUtil.applicationContext = applicationContext;  
    }  
  
    //通過名字獲取上下文中的bean  
    public static Object getBean(String name){  
        return applicationContext.getBean(name);  
    }  
      
    //通過型別獲取上下文中的bean  
    public static Object getBean(Class<?> requiredType){  
        return applicationContext.getBean(requiredType);  
    }  
}


@SpringBootApplication
@ComponentScan(basePackages={"com.lixy"})
@EnableAsync
public class Application {
    public static void main(String[] args) throws Exception {
        SpringApplication application = new SpringApplication(Application.class);
//applicationContext放入工具類
        SpringContextUtil.setApplicationContext(application.run(args));

       
    }
}




//redis的注入


@Configuration
public class GlobalConfig {


    private final static int DEFAULT_TIMEOUT = 5000;
    private final static int DEFAULT_MAX_ATTEMPTS = 6;


    @Bean
    @ConditionalOnMissingBean
    public JedisCluster jedisCLuster(@Qualifier("redisPoolConfig") RedisPoolConfig redisPoolConfig) {
   //redis叢集主節點 
        String[] servers = [192.168.10.177:7000,192.168.10.178:7000,192.168.10.204:7000];

        Set<HostAndPort> nodes = new HashSet<>();
        for (String server : servers) {
            String[] hostAndPort = server.split(":");
            nodes.add(new HostAndPort(hostAndPort[0], Integer.parseInt(hostAndPort[1])));
        }
//redis密碼
        String password = "password***";
        if (StringUtils.isEmpty(password)) {
            return new JedisCluster(nodes, redisPoolConfig);
        }
        return new JedisCluster(nodes, DEFAULT_TIMEOUT, DEFAULT_TIMEOUT, DEFAULT_MAX_ATTEMPTS, password, redisPoolConfig);
    }


    @Bean
    @ConditionalOnMissingBean
    public RedisConnectionFactory connectionFactory() {
        String[] servers = [192.168.10.177:7000,192.168.10.178:7000,192.168.10.204:7000];
        JedisConnectionFactory factory =  new JedisConnectionFactory(new RedisClusterConfiguration(Arrays.asList(servers)));
        factory.setPassword("password***");
        return factory;
    }


    @SuppressWarnings({"rawtypes", "unchecked"})
    @Bean(name = "clusterRedisTemplate")
    public RedisTemplate<String, String> redisTemplate() {
        RedisTemplate template = new RedisTemplate();
        template.setConnectionFactory(connectionFactory());
        // 開啟事務支援
        template.setEnableTransactionSupport(true);
        // 使用String格式序列化快取鍵
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        template.setKeySerializer(stringRedisSerializer);
        template.setHashKeySerializer(stringRedisSerializer);
        // 使用json格式序列化快取值
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.setDefaultSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}



@ConfigurationProperties("redis.pool")
@Component("redisPoolConfig")
@Order(10)
public class RedisPoolConfig extends JedisPoolConfig {

}