Commit f54c4339 authored by liuxingyu's avatar liuxingyu

分布式锁代码提交

parent 61f477b7
......@@ -2,58 +2,30 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.8</version>
<relativePath></relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mushiny</groupId>
<artifactId>redisson-lock</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.13.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<version>1.18.20</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.15.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
......@@ -61,88 +33,18 @@
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.7.8</version>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<target>1.8</target>
<source>1.8</source>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>${maven-deploy-plugin.version}</version>
<configuration>
<skip>false</skip>
</configuration>
</plugin>
</plugins>
</build>
<distributionManagement>
<snapshotRepository>
<id>maven-snapshots</id>
<name>maven-snapshots</name>
<url>http://192.168.3.160:8081/repository/maven-snapshots/</url>
</snapshotRepository>
<repository>
<id>maven-release</id>
<name>maven-release</name>
<url>http://192.168.3.160:8081/repository/maven-releases/</url>
<id>project-release</id>
<name>project-release</name>
<url>http://nexusnj612.mushiny.com:8088/repository/project-release/</url>
</repository>
</distributionManagement>
</project>
\ No newline at end of file
/*
* Copyright (c) 2020 牧星仓库管理系统 All rights reserved.
*
* http://www.mushiny.com
*
* 版权所有,侵权必究!
*/
package com.mushiny.redisson;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class AdminApplication {
public static void main(String[] args) {
SpringApplication.run(AdminApplication.class, args);
}
}
package com.mushiny.redisson.config;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 描述信息
*
* @auther lxy
* @since 2023/8/4 13:39
*/
@Configuration
public class RedissonManager {
@Value("${spring.redis.host:192.168.3.104}")
private String addresses;
@Value("${spring.redis.password:q1w2e3r4}")
private String password;
@Value("${spring.redis.port:6379}")
private String port;
@Bean(destroyMethod = "shutdown")
public RedissonClient redissonClient() {
Config config = new Config();
//调用 useSingleServer 方法,选择单机模式,并指定 Redis 服务器的地址
config.useSingleServer().setAddress("redis://" + addresses + ":" + port).setPassword(password);
System.out.println("redis://" + addresses + ":" + port);
//调用 setConnectionMinimumIdleSize 方法,设置连接池最小空闲连接数为 10。
config.useSingleServer().setConnectionMinimumIdleSize(10);
//调用 Redisson.create 方法,创建一个 RedissonClient 对象
RedissonClient redissonClient = Redisson.create(config);
return redissonClient;
}
}
package com.mushiny.redisson.controller;
import com.mushiny.redisson.enums.LockType;
import com.mushiny.redisson.utils.RedissonLockUtil;
import com.mushiny.redisson.utils.ResultForLock;
import jodd.net.HttpStatus;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.util.function.Tuple2;
import javax.servlet.http.HttpServletResponse;
import java.text.SimpleDateFormat;
import java.util.Date;
import static jdk.nashorn.internal.runtime.regexp.joni.Config.log;
/**
* 描述信息
*
* @auther lxy
* @since 2023/8/10 13:03
*/
@RestController
@Slf4j
public class TestController {
@Autowired
RedissonLockUtil redissonLockUtil;
@Autowired
JdbcTemplate jdbcTemplate;
public int count = 0;
public int errCount = 0;
@PostMapping ("api/reentrant")
public ResultForLock<String> reentrant(HttpServletResponse response) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String threadId = Thread.currentThread().getName() + Thread.currentThread().getId();
log.info("enter"+threadId+dateFormat.format(new Date()));
//redissonLockUtil.getLockByKey("265213225444411", LockType.FAIR_LOCK.value());
Tuple2<RLock, Boolean> rlocks = redissonLockUtil.tryLock(30000L, -1L, "265213225444411", LockType.REENTRANT_LOCK.value());
if (rlocks.getT2()) {
log.info(threadId + "拿锁成功"+ dateFormat.format(new Date()));
try {
count++;
jdbcTemplate.execute("UPDATE descrease a SET a.number = a.number-1");
Thread.sleep(31000);
System.out.println("成功执行" + count + "-----------" + dateFormat.format(new Date()));
} catch (Exception e) {
e.printStackTrace();
return new ResultForLock().error();
}finally {
rlocks.getT1().unlock();
}
} else {
errCount++;
response.setStatus(500);
return new ResultForLock().error();
}
return new ResultForLock().ok(threadId);
}
@PostMapping ("api/reentrantReleaseTime")
public ResultForLock<String> reentrantReleaseTime(HttpServletResponse response) {
String threadId = Thread.currentThread().getName() + Thread.currentThread().getId();
Tuple2<RLock, Boolean> rlocks = redissonLockUtil.tryLock(30000L, 1000L, "265213225444411", LockType.REENTRANT_LOCK.value());
if (rlocks.getT2()) {
System.out.println(threadId + "拿锁成功");
try {
count++;
jdbcTemplate.execute("UPDATE descrease a SET a.number = a.number-1");
Thread.sleep(2000);
System.out.println("执行数量为"+count);
} catch (Exception e) {
e.printStackTrace();
return new ResultForLock().error();
}finally {
redissonLockUtil.releaseLock(rlocks.getT1());
}
} else {
System.out.println("拿锁失败");
response.setStatus(500);
return new ResultForLock().error();
}
return new ResultForLock().ok(threadId);
}
/**
* 手动加锁和解锁,最常用的方式
* @return
*/
@PostMapping ("api/reentrantByManual")
public ResultForLock<String> reentrantByManual() {
String threadId = Thread.currentThread().getName() + Thread.currentThread().getId();
RLock rLock = redissonLockUtil.getLockByKey("265213225444411", LockType.REENTRANT_LOCK.value());
// 支持过期解锁功能,10秒钟以后自动解锁, 无需调用unlock方法手动解锁
//lock.lock(10, TimeUnit.SECONDS);
redissonLockUtil.lock(rLock);// * 加锁 锁的有效期默认30秒
try {
count++;
jdbcTemplate.execute("UPDATE descrease a SET a.number = a.number-1");
Thread.sleep(32000);
System.out.println("执行数量为"+count);
} catch (Exception e) {
e.printStackTrace();
return new ResultForLock().error();
}finally {
rLock.unlock();
}
return new ResultForLock().ok(threadId);
}
/**
* 使用公平锁,先请求的线程先拿到锁
* @return
*/
@PostMapping ("api/fair")
public ResultForLock<String> fair() {
String threadId = Thread.currentThread().getName() + Thread.currentThread().getId();
// redissonLockUtil.getLockByKey("265213225444411", LockType.FAIR_LOCK.value());
Tuple2<RLock, Boolean> rlocks = redissonLockUtil.tryLock(20000L, -1L, "265213225444411", LockType.REENTRANT_LOCK.value());
if (rlocks.getT2()) {
System.out.println(threadId + "拿锁成功");
try {
count++;
jdbcTemplate.execute("UPDATE descrease a SET a.number = a.number-1");
Thread.sleep(2000);
System.out.println("执行数量为"+count);
} catch (Exception e) {
e.printStackTrace();
return new ResultForLock().error();
}finally {
rlocks.getT1().unlock();
// redissonLockUtil.releaseLock("265213225444411", LockType.REENTRANT_LOCK.value());
}
} else {
System.out.println("拿锁失败");
return new ResultForLock().error();
}
return new ResultForLock().ok(threadId);
}
}
package com.mushiny.redisson.utils;
import cn.hutool.core.util.RandomUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Random;
/**
* 雪花算法的原理就是生成一个的 64 位比特位的 long 类型的唯一 id。开头一位固定0,41位时间戳,5位机器id,5位服务id,12位序号
*
* @auther lxy
* @since 2023/8/9 13:57
*/
@Component
public class SnowFlakeUtil {
/**
* 起始时间戳,从2023-08-01开始生成
*/
@Value("${snowflake.config.stamp:1690819200000L}")
private final static long START_STAMP = 1690819200000L;
/**
* 序列号占用的位数 12
*/
private final static long SEQUENCE_BIT = 12;
/**
* 机器标识占用的位数
*/
private final static long MACHINE_BIT = 10;
/**
* 机器数量最大值
*/
private final static long MAX_MACHINE_NUM = ~(-1L << MACHINE_BIT);
/**
* 序列号最大值
*/
private final static long MAX_SEQUENCE = ~(-1L << SEQUENCE_BIT);
/**
* 每一部分向左的位移
*/
private final static long MACHINE_LEFT = SEQUENCE_BIT;
private final static long TIMESTAMP_LEFT = SEQUENCE_BIT + MACHINE_BIT;
/**
* 机器标识
*/
@Value("${snowflake.config.machineId:160}")
private long machineId;
/**
* 序列号
*/
private long sequence = 0L;
/**
* 上一次时间戳
*/
private long lastStamp = -1L;
@Value("${snowflake.config.id.length:16")
private int length = 16;
@Value("${snowflake.config.id.strLength:16}")
private int strLength = 25;
public SnowFlakeUtil() {
if (machineId > MAX_MACHINE_NUM || machineId < 0) {
throw new RuntimeException("机器超过最大数量");
}
}
/**
* 产生下一个ID
*/
public synchronized long nextId() {
long currStamp = getNewStamp();
if (currStamp < lastStamp) {
throw new RuntimeException("时钟后移,拒绝生成ID!");
}
if (currStamp == lastStamp) {
// 相同毫秒内,序列号自增
sequence = (sequence + 1) & MAX_SEQUENCE;
// 同一毫秒的序列数已经达到最大
if (sequence == 0L) {
currStamp = getNextMill();
}
} else {
// 不同毫秒内,序列号置为0
sequence = 0L;
}
lastStamp = currStamp;
return transferLength((currStamp - START_STAMP) << TIMESTAMP_LEFT | machineId << MACHINE_LEFT | sequence);
}
/**
* 产生下一个String类型的ID(随机5位字符串加上雪花算法id)
*/
public synchronized String nextStrId() {
long currStamp = getNewStamp();
if (currStamp < lastStamp) {
throw new RuntimeException("时钟后移,拒绝生成ID!");
}
if (currStamp == lastStamp) {
// 相同毫秒内,序列号自增
sequence = (sequence + 1) & MAX_SEQUENCE;
// 同一毫秒的序列数已经达到最大
if (sequence == 0L) {
currStamp = getNextMill();
}
} else {
// 不同毫秒内,序列号置为0
sequence = 0L;
}
lastStamp = currStamp;
return transferLength(String.valueOf((currStamp - START_STAMP) << TIMESTAMP_LEFT | machineId << MACHINE_LEFT | sequence));
}
private long getNextMill() {
long mill = getNewStamp();
while (mill <= lastStamp) {
mill = getNewStamp();
}
return mill;
}
private long getNewStamp() {
return System.currentTimeMillis();
}
/**
* Long类型id转换长度
*
* @param id
* @return
*/
private long transferLength(Long id) {
if (length < 20 && length > 15) {
id = Long.parseLong(StringUtils.rightPad(id.toString(), length, "0"));
}
if (length > 19) {
id = Long.parseLong(StringUtils.rightPad(id.toString(), 19, "0"));
}
return id;
}
/**
* String类型id转换长度
*
* @param id
* @return
*/
private String transferLength(String id) {
if (strLength > 16) {
id = RandomUtil.randomString(strLength - id.length()) + id;
}
return id;
}
public static void main(String[] args) {
SnowFlakeUtil snowFlakeUtil = new SnowFlakeUtil();
Long nextId = snowFlakeUtil.nextId();
String nextStrId = snowFlakeUtil.nextStrId();
System.out.println("Long类型的为" + nextId + "长度为" + nextId.toString().length());
System.out.println("String类型的为" + nextStrId + "长度为" + nextStrId.length());
}
}
server:
port: 10101
servlet:
context-path: /
shutdown: graceful #开启优雅停机,默认IMMEDIATE是立即关机
tomcat:
maxThreads: 1000
connection-timeout: 15000ms
keepAliveTimeout: 15000
maxKeepAliveRequests: 200
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2b8
username: root
password: wasd11647
package pubtools;
import com.mushiny.redisson.enums.LockType;
import com.mushiny.redisson.utils.RedissonLockUtil;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.text.SimpleDateFormat;
import java.util.Date;
@SpringBootTest
class MwmsRedissonApplicationTests {
//
// @Autowired
// RedissonLockUtil redissonLockUtil;
//
// @Test
// void contextLoads() {
// String threadId = Thread.currentThread().getName() + Thread.currentThread().getId();
// redissonLockUtil.getLockByKey("2652132254444", LockType.READ_LOCK.value());
// Boolean getLock = redissonLockUtil.tryLock(null,-1L, "2652132254444", LockType.REENTRANT_LOCK.value());
// if (getLock) {
// System.out.println(threadId + "拿锁成功");
// try {
// SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// Date startDate = new Date();
// System.out.println(threadId + "执行调度ping" + dateFormat.format(startDate));
// Thread.sleep(10000);
// System.out.println("结束时间为" + dateFormat.format(new Date()));
// } catch (Exception e) {
// e.printStackTrace();
// }
// } else {
// System.out.println("拿锁失败");
// }
// }
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment