redis实现的分布式锁,多线程控制

问题

最近遇到的需求是,event主题库服务会接收其他N个服务发送来的http或者mq的更新请求,但是event主题库服务后续是多实例部署,就存才 同时更新一单数据的风险,需要保证单线程操作更新业务,查阅了资料:实现逻辑是redis实现多实例锁,多线程并发等待,直接上代码,,

1,初始化线程池



import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPoolExecutorUtil {
    /**
     * 线程数
     */
    private static final int CORE_SIZE = 10;
    /**
     * 最大值
     */
    private static final int MAX_SIZE = 20;
    /**
     * 线程空闲多久进行回收
     */
    private static final long KEEP_ALIVE_TIME = 60;
    /**
     * 阻塞队列的大小
     */
    private static final int QUEUE_SIZE = 2000;

    /**
     * 使用多少的队列
     */
    private static int USER_QUEUE_SIZE = 0;

    /**
     * 使用多少的队列
     *
     * @return 队列值
     */
    public static int getUserQueueSize() {
        return USER_QUEUE_SIZE;
    }

    private static final ThreadPoolExecutor threadPool = new ThreadPoolExecutor(CORE_SIZE, MAX_SIZE, KEEP_ALIVE_TIME,
            TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(QUEUE_SIZE), new ThreadPoolExecutor.AbortPolicy());


    public static ThreadPoolExecutor getThreadPool() {
        new ThreadPoolExecutor(CORE_SIZE, MAX_SIZE, KEEP_ALIVE_TIME,
                TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(QUEUE_SIZE), new ThreadPoolExecutor.AbortPolicy());

        USER_QUEUE_SIZE = threadPool.getQueue().size();
        return threadPool;
    }
}

2,初始化redis配置(redis的单实例模式)


import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;


@ConfigurationProperties(prefix = "spring.redis")
@Component
@Data
public class RedisProperties {
    private String host;
    private int port;
    private String password;
}

package com.bxt.event.infrastructure.redis;

import lombok.Data;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.Jedis;

import javax.annotation.Resource;

@Configuration
public class RedisConfig {

    public static Jedis getJedisCluster(RedisProperties redisProperties) {
        // 获取redis的ip及端口号等相关信息
        Jedis jedis = new Jedis(redisProperties.getHost(), redisProperties.getPort(), 10000, 10000, false);
        jedis.auth(redisProperties.getPassword());
        return jedis;
    }
}

3,初始化redis锁(redis的api原理自行百度)

import redis.clients.jedis.Jedis;

import java.util.Collections;

public class RedisTool {

    private static final String LOCK_SUCCESS = "OK";
    private static final String SET_IF_NOT_EXIST = "NX";
    private static final String SET_WITH_EXPIRE_TIME = "PX";
    private static final Long RELEASE_SUCCESS = 1L;

    /**
     * 尝试获取分布式锁
     * @param jedis Redis客户端
     * @param lockKey 锁
     * @param requestId 请求标识
     * @param expireTime 超期时间
     * @return 是否获取成功
     */
    public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
        String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
        return LOCK_SUCCESS.equals(result);
    }


    /**
     * 释放分布式锁
     * @param jedis Redis客户端
     * @param lockKey 锁
     * @param requestId 请求标识
     * @return 是否释放成功
     */
    public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
        return RELEASE_SUCCESS.equals(result);
    }

}

4,代码实例(使用)


import com.alibaba.fastjson.JSON;
import com.bxt.common.exception.InteractiveException;
import com.bxt.event.dto.TrafficEventVO;
import com.bxt.event.infrastructure.redis.RedisProperties;
import com.bxt.event.infrastructure.redis.RedisTool;
import com.bxt.event.po.TrafficEventPO;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import redis.clients.jedis.Jedis;

import java.util.Date;
import java.util.UUID;

@Data
@Slf4j
@AllArgsConstructor
public class RedisLockUpdate implements Runnable {

    private TrafficEventService trafficEventService;
    private String TRAFFIC_UPDATE_LOCKKEY;
    private String fanoutExchange;
    private RabbitTemplate rabbitTemplate;
    private TrafficEventPO param;
    private String operation;
    private TrafficEventVO source;
    private Jedis jedis;

    @Override
    public void run() {
        boolean flag = false;
        String uuid = UUID.randomUUID().toString();
        while (true) {
            if (RedisTool.tryGetDistributedLock(jedis, TRAFFIC_UPDATE_LOCKKEY, uuid, 1000 * 60)) {
                try {
                   	//业务逻辑
                   	// ***
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    RedisTool.releaseDistributedLock(jedis, TRAFFIC_UPDATE_LOCKKEY, uuid);
                    flag = true;
                }
            }

            if (flag) {
                break;
            }
        }
    }
}

private void operationTrafficEvent(TrafficEventVO source, String operation) {
        TrafficEventPO target = mapStructBase.TrafficEventVOConvertToTrafficEventPO(source);
        target.transformData(source);

        Jedis jedis = RedisConfig.getJedisCluster(redisProperties);
        RedisLockUpdate redisLockUpdate
                = new RedisLockUpdate(trafficEventService, TRAFFIC_UPDATE_LOCKKEY, fanoutExchange, rabbitTemplate, target, operation, source, jedis);

        ThreadPoolExecutor poolExecutor = ThreadPoolExecutorUtil.getThreadPool();
        poolExecutor.execute(redisLockUpdate);
    }

有写的不好的地方欢迎大家指正,轻喷

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值