springboot 集成redis集群配置JedisCluster,并用redis实现分布式锁

一、redis在工作是一个常见的工具,这里对redis和springboot形成集群做一个简单示例。
(1)引入对应redis集群所需要maven文件

 
            redis.clients
            jedis
            2.9.0
        
        
            org.springframework.boot
            spring-boot-starter-data-redis
            2.0.5.RELEASE
        

(2) 在配置文件配置 redis.properties 配置redis集群必要参数

#客户端超时时间单位是毫秒 默认是2000
redis.timeout=10000
#最大空闲数
redis.maxIdle=300
#连接池的最大数据库连接数。设为0表示无限制,如果是jedis 2.4以后用redis.maxTotal
#redis.maxActive=600
#控制一个pool可分配多少个jedis实例,用来替换上面的redis.maxActive,如果是jedis 2.4以后用该属性
redis.maxTotal=2000
#最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制。
redis.maxWaitMillis=1000

redis.nodes=192.168.25.133:7001,192.168.25.133:7002,192.168.25.133:7003,192.168.25.133:7004,192.168.25.133:7005,192.168.25.133:7006

(3)初始化JedisCluster

@Configuration
@PropertySource("classpath:conf/redis.properties")
public class RedisConfig {

 
    @Value("${redis.maxIdle}")
    private Integer maxIdle;

    @Value("${redis.timeout}")
    private Integer timeout;
 
    @Value("${redis.maxTotal}")
    private Integer maxTotal;
 
    @Value("${redis.maxWaitMillis}")
    private Integer maxWaitMillis;


    @Value("${redis.nodes}")
    private String clusterNodes;


    @Bean
    public JedisCluster getJedisCluster(){
        String[] cNodes = clusterNodes.split(",");
        HashSet nodes = new HashSet<>();
        //分割集群节点
        for (String node : cNodes) {
            String[] hp = node.split(":");
            nodes.add(new HostAndPort(hp[0], Integer.parseInt(hp[1])));
        }
            JedisPoolConfig jedisPoolConfig=new JedisPoolConfig();
            jedisPoolConfig.setMaxIdle(maxIdle);
            jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
            jedisPoolConfig.setMaxTotal(maxTotal);

        //创建集群对象
        JedisCluster jedisCluster = new JedisCluster(nodes, timeout, jedisPoolConfig);
        return jedisCluster;
    }

}

(3) 对JedisCluster 初始化基本操作接口

public interface JedisService {

    Long delete(String key);

    Long incr(String key);

    Long decr(String key);

    boolean lock(String key, String requestId, int tt1);


    boolean unlock(String key, String requestId);
    
}

(4) 对JedisCluster 操作实现

@Service
public class JedisServiceImpl implements JedisService {

    @Autowired
    public JedisCluster jedisCluster;

    private static final String prefix = "jedis_lock";
    //锁状态
    private static final String LOCK_SUCCESS = "OK";

    //释放状态
    private static final Long RELEASE_SUCCESS=1L;

    //NX标志
    private static final String SET_IF_NOT_EXIST = "NX";

    //超时标志
    private static final String SET_WITH_EXPIRE_TIME = "PX";

    @Override
    public Long delete(String key) {
        return jedisCluster.del(prefix + key);
    }

    @Override
    public Long incr(String key) {
        return jedisCluster.incr(prefix + key);
    }

    @Override
    public Long decr(String key) {
        return jedisCluster.decr(prefix + key);
    }

    @Override
    public boolean lock(String key, String requestId, int tt1) {
        String result = jedisCluster.set(prefix + key, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, tt1);
        if (LOCK_SUCCESS.equals(result)) {
            return true;
        }
        return false;
    }

    @Override
    public boolean unlock(String key, 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 = jedisCluster.eval(script, Collections.singletonList(prefix + key), Collections.singletonList(requestId));
        if (RELEASE_SUCCESS.equals(result)) {
            return true;
        }
        return false;
    }
}

(5)后面通过redis实现分布式秒杀(示例)
秒杀对单个商品进行加锁,这里定义几个自定义注解,对加锁时间,是否加锁进行定义

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CacheLock {
    //redis 锁住key的前缀
    String lockPrefix() default "";
    //轮询锁的时间
    long timeOut() default 5000;
    //redis key的过期时间
    int expireTime() default 10;
}
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LockedComplexObject {
    //含有成员变量的复杂对象中需要加锁的成员变量
    String field() default "";
}

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LockedObject {
}
/**
 * 秒杀接口
 */
public interface SeckillInterface {
	@CacheLock(lockPrefix="TEST_PREFIX")
	public void secKill(String arg1, @LockedObject Long arg2);
}

/**
 * 秒杀实现类
 */
public class SecKillImpl implements SeckillInterface {
	public static Map inventory ;
	static{
		inventory = new HashMap<>();
		inventory.put(10000001L, 10000L);
	}
	
	@Override
		public void secKill(String arg1, Long arg2) {
			//最简单的秒杀,这里仅作为demo示例
			Long aLong = reduceInventory(arg2);
			System.out.println(arg2+"开始秒杀第"+aLong);
		}
		//模拟秒杀操作,姑且认为一个秒杀就是将库存减一,实际情景要复杂的多
		public Long reduceInventory(Long commodityId){
			inventory.put(commodityId,inventory.get(commodityId) - 1);
		return inventory.get(commodityId);
	}
}

/**
 * 秒杀拦截拦截注解
 */
public class CacheLockInterceptor implements InvocationHandler {

    public static int ERROR_COUNT =0;

    private final String LOCK = "TRUE";
    public static final Random RANDOM = new Random();

    private Object proxied;

    private JedisService jedisService;

    public CacheLockInterceptor(Object proxied,JedisService jedisService) {
        this.proxied = proxied;
        this.jedisService = jedisService;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        CacheLock cacheLock = method.getAnnotation(CacheLock.class);
        if (Objects.isNull(cacheLock)) {
            System.out.println("is cacheLock annotation");
            return method.invoke(proxied, args);
        }
        //获取方法中参数注解
        Annotation[][] annotations = method.getParameterAnnotations();
        //根据获取到参数注解和参数列表获取参数加锁参数
        Object lockedObject=getLockedObject(annotations, args);
        String objectValue = lockedObject.toString();
        long l = cacheLock.timeOut();
        long timeout=System.currentTimeMillis()+l*1000;
        boolean lock = false;
        while (System.currentTimeMillis()

通过springboot启动测试类,测试结果

@RunWith(SpringRunner.class)
@SpringBootTest
public class FreemakeraddApplicationTests {
    @Autowired
    private JedisService jedisService;
    private static Long commidityId1 = 10000001L;

    @Test
    public void testSecKill(){
        int threadCount = 1000;
        int splitPoint = 500;
        CountDownLatch endCount = new CountDownLatch(splitPoint);
        CountDownLatch beginCount = new CountDownLatch(1);
        SecKillImpl testClass = new SecKillImpl();

        Thread[] threads = new Thread[threadCount];
        //起500个线程,秒杀第一个商品
        for(int i= 0;i < splitPoint;i++){
            threads[i] = new Thread(new  Runnable() {
                public void run() {
                    try {
                        //等待在一个信号量上,挂起
                        beginCount.await();
                        //用动态代理的方式调用secKill方法
                        SeckillInterface proxy = (SeckillInterface) Proxy.newProxyInstance(SeckillInterface.class.getClassLoader(),
                                new Class[]{SeckillInterface.class}, new CacheLockInterceptor(testClass,jedisService));
                        proxy.secKill("test", commidityId1);
                        endCount.countDown();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            });
            threads[i].start();

        }


        long startTime = System.currentTimeMillis();
        //主线程释放开始信号量,并等待结束信号量
        beginCount.countDown();

        try {
            //主线程等待结束信号量
            endCount.await();
            //观察秒杀结果是否正确
            System.out.println(SecKillImpl.inventory.get(commidityId1));

            System.out.println("error count" + CacheLockInterceptor.ERROR_COUNT);
            System.out.println("total cost " + (System.currentTimeMillis() - startTime));
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

测试结果(最后剩9500)

在这里插入代码片
intecepor 释放锁
出现锁等待
10000001开始秒杀第9505
出现锁等待
出现锁等待
出现锁等待
intecepor 释放锁
出现锁等待
出现锁等待
10000001开始秒杀第9504
出现锁等待
intecepor 释放锁
出现锁等待
出现锁等待
10000001开始秒杀第9503
intecepor 释放锁
10000001开始秒杀第9502
出现锁等待
intecepor 释放锁
10000001开始秒杀第9501
intecepor 释放锁
10000001开始秒杀第9500
intecepor 释放锁
9500
10000
error count0
total cost 5223

(7)redis集群一些操作命令
先查redis集群图片
在这里插入图片描述
redis_start_all.sh

cd /usr/local/taotao-servers/redis-cluster/redis01/bin
./redis-server redis.conf

cd /usr/local/taotao-servers/redis-cluster/redis02/bin
./redis-server redis.conf

cd /usr/local/taotao-servers/redis-cluster/redis03/bin
./redis-server redis.conf

cd /usr/local/taotao-servers/redis-cluster/redis04/bin
./redis-server redis.conf

cd /usr/local/taotao-servers/redis-cluster/redis05/bin
./redis-server redis.conf

cd /usr/local/taotao-servers/redis-cluster/redis06/bin
./redis-server redis.conf
//对启动文件修改启动文件
chmod 777  redis_start_all.sh

//启动脚本命令
./redis_start_all.sh


//进入redisCluster集群
./redis-cli -p 7001 -c 

//集群信息
cluster info

//.查看集群里有多少个节点
cluster nodes

springboot 集成redis集群配置JedisCluster,并用redis实现分布式锁_第1张图片
redis集群安装网址

https://blog.csdn.net/qq_42815754/article/details/82912130

你可能感兴趣的:(redis)