feat: initial iShare project code
This commit is contained in:
32
pigx-common/pigx-common-sequence/pom.xml
Normal file
32
pigx-common/pigx-common-sequence/pom.xml
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-common</artifactId>
|
||||
<version>5.2.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>pigx-common-sequence</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<description>pigx 分布式发号器</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>redis.clients</groupId>
|
||||
<artifactId>jedis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-common-core</artifactId>
|
||||
</dependency>
|
||||
<!--mybatis plus-->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-extension</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.common.sequence;
|
||||
|
||||
import com.pig4cloud.pigx.common.sequence.builder.SnowflakeSeqBuilder;
|
||||
import com.pig4cloud.pigx.common.sequence.properties.SequenceSnowflakeProperties;
|
||||
import com.pig4cloud.pigx.common.sequence.sequence.Sequence;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2019-05-26
|
||||
*/
|
||||
@Configuration
|
||||
@ComponentScan("com.pig4cloud.pigx.common.sequence")
|
||||
@ConditionalOnMissingBean(Sequence.class)
|
||||
public class SequenceAutoConfiguration {
|
||||
|
||||
/**
|
||||
* snowflak 算法作为发号器实现
|
||||
* @param properties
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnBean(SequenceSnowflakeProperties.class)
|
||||
public Sequence snowflakeSequence(SequenceSnowflakeProperties properties) {
|
||||
return SnowflakeSeqBuilder.create().datacenterId(properties.getDatacenterId())
|
||||
.workerId(properties.getWorkerId()).build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.pig4cloud.pigx.common.sequence.builder;
|
||||
|
||||
import com.pig4cloud.pigx.common.sequence.range.BizName;
|
||||
import com.pig4cloud.pigx.common.sequence.range.impl.db.DbSeqRangeMgr;
|
||||
import com.pig4cloud.pigx.common.sequence.sequence.Sequence;
|
||||
import com.pig4cloud.pigx.common.sequence.sequence.impl.DefaultRangeSequence;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
/**
|
||||
* 基于DB取步长,序列号生成器构建者
|
||||
*
|
||||
* @author xuan
|
||||
*/
|
||||
public class DbSeqBuilder implements SeqBuilder {
|
||||
|
||||
/**
|
||||
* 数据库数据源[必选]
|
||||
*/
|
||||
private DataSource dataSource;
|
||||
|
||||
/**
|
||||
* 业务名称[必选]
|
||||
*/
|
||||
private BizName bizName;
|
||||
|
||||
/**
|
||||
* 存放序列号步长的表[可选:默认:sequence]
|
||||
*/
|
||||
private String tableName = "sequence";
|
||||
|
||||
/**
|
||||
* 并发是数据使用了乐观策略,这个是失败重试的次数[可选:默认:100]
|
||||
*/
|
||||
private int retryTimes = 100;
|
||||
|
||||
/**
|
||||
* 获取range步长[可选:默认:1000]
|
||||
*/
|
||||
private int step = 1000;
|
||||
|
||||
/**
|
||||
* 序列号分配起始值[可选:默认:0]
|
||||
*/
|
||||
private long stepStart = 0;
|
||||
|
||||
public static DbSeqBuilder create() {
|
||||
DbSeqBuilder builder = new DbSeqBuilder();
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Sequence build() {
|
||||
// 利用DB获取区间管理器
|
||||
DbSeqRangeMgr dbSeqRangeMgr = new DbSeqRangeMgr();
|
||||
dbSeqRangeMgr.setDataSource(this.dataSource);
|
||||
dbSeqRangeMgr.setTableName(this.tableName);
|
||||
dbSeqRangeMgr.setRetryTimes(this.retryTimes);
|
||||
dbSeqRangeMgr.setStep(this.step);
|
||||
dbSeqRangeMgr.setStepStart(stepStart);
|
||||
dbSeqRangeMgr.init();
|
||||
// 构建序列号生成器
|
||||
DefaultRangeSequence sequence = new DefaultRangeSequence();
|
||||
sequence.setName(this.bizName);
|
||||
sequence.setSeqRangeMgr(dbSeqRangeMgr);
|
||||
return sequence;
|
||||
}
|
||||
|
||||
public DbSeqBuilder dataSource(DataSource dataSource) {
|
||||
this.dataSource = dataSource;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DbSeqBuilder tableName(String tableName) {
|
||||
this.tableName = tableName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DbSeqBuilder retryTimes(int retryTimes) {
|
||||
this.retryTimes = retryTimes;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DbSeqBuilder step(int step) {
|
||||
this.step = step;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DbSeqBuilder bizName(BizName bizName) {
|
||||
this.bizName = bizName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DbSeqBuilder stepStart(long stepStart) {
|
||||
this.stepStart = stepStart;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.pig4cloud.pigx.common.sequence.builder;
|
||||
|
||||
import com.pig4cloud.pigx.common.sequence.range.BizName;
|
||||
import com.pig4cloud.pigx.common.sequence.range.impl.redis.RedisSeqRangeMgr;
|
||||
import com.pig4cloud.pigx.common.sequence.sequence.Sequence;
|
||||
import com.pig4cloud.pigx.common.sequence.sequence.impl.DefaultRangeSequence;
|
||||
|
||||
/**
|
||||
* 基于redis取步长,序列号生成器构建者
|
||||
*
|
||||
* @author xuan on 2018/5/30.
|
||||
*/
|
||||
public class RedisSeqBuilder implements SeqBuilder {
|
||||
|
||||
/**
|
||||
* 连接redis的IP[必选]
|
||||
*/
|
||||
private String ip;
|
||||
|
||||
/**
|
||||
* 连接redis的port[必选]
|
||||
*/
|
||||
private int port;
|
||||
|
||||
/**
|
||||
* 业务名称[必选]
|
||||
*/
|
||||
private BizName bizName;
|
||||
|
||||
/**
|
||||
* 认证权限,看redis是否配置了需要密码auth[可选]
|
||||
*/
|
||||
private String auth;
|
||||
|
||||
/**
|
||||
* 获取range步长[可选,默认:1000]
|
||||
*/
|
||||
private int step = 1000;
|
||||
|
||||
/**
|
||||
* 序列号分配起始值[可选:默认:0]
|
||||
*/
|
||||
private long stepStart = 0;
|
||||
|
||||
public static RedisSeqBuilder create() {
|
||||
RedisSeqBuilder builder = new RedisSeqBuilder();
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Sequence build() {
|
||||
// 利用Redis获取区间管理器
|
||||
RedisSeqRangeMgr redisSeqRangeMgr = new RedisSeqRangeMgr();
|
||||
redisSeqRangeMgr.setIp(this.ip);
|
||||
redisSeqRangeMgr.setPort(this.port);
|
||||
redisSeqRangeMgr.setAuth(this.auth);
|
||||
redisSeqRangeMgr.setStep(this.step);
|
||||
redisSeqRangeMgr.setStepStart(stepStart);
|
||||
redisSeqRangeMgr.init();
|
||||
// 构建序列号生成器
|
||||
DefaultRangeSequence sequence = new DefaultRangeSequence();
|
||||
sequence.setName(this.bizName);
|
||||
sequence.setSeqRangeMgr(redisSeqRangeMgr);
|
||||
return sequence;
|
||||
}
|
||||
|
||||
public RedisSeqBuilder ip(String ip) {
|
||||
this.ip = ip;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RedisSeqBuilder port(int port) {
|
||||
this.port = port;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RedisSeqBuilder auth(String auth) {
|
||||
this.auth = auth;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RedisSeqBuilder step(int step) {
|
||||
this.step = step;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RedisSeqBuilder bizName(BizName bizName) {
|
||||
this.bizName = bizName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RedisSeqBuilder stepStart(long stepStart) {
|
||||
this.stepStart = stepStart;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.pig4cloud.pigx.common.sequence.builder;
|
||||
|
||||
import com.pig4cloud.pigx.common.sequence.sequence.Sequence;
|
||||
|
||||
/**
|
||||
* 序列号生成器构建者
|
||||
*
|
||||
* @author xuan on 2018/5/30.
|
||||
*/
|
||||
public interface SeqBuilder {
|
||||
|
||||
/**
|
||||
* 构建一个序列号生成器
|
||||
* @return 序列号生成器
|
||||
*/
|
||||
Sequence build();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.pig4cloud.pigx.common.sequence.builder;
|
||||
|
||||
import com.pig4cloud.pigx.common.sequence.sequence.Sequence;
|
||||
import com.pig4cloud.pigx.common.sequence.sequence.impl.SnowflakeSequence;
|
||||
|
||||
/**
|
||||
* 基于雪花算法,序列号生成器构建者
|
||||
*
|
||||
* @author xuan on 2018/5/30.
|
||||
*/
|
||||
public class SnowflakeSeqBuilder implements SeqBuilder {
|
||||
|
||||
/**
|
||||
* 数据中心ID,值的范围在[0,31]之间,一般可以设置机房的IDC[必选]
|
||||
*/
|
||||
private long datacenterId;
|
||||
|
||||
/**
|
||||
* 工作机器ID,值的范围在[0,31]之间,一般可以设置机器编号[必选]
|
||||
*/
|
||||
private long workerId;
|
||||
|
||||
public static SnowflakeSeqBuilder create() {
|
||||
SnowflakeSeqBuilder builder = new SnowflakeSeqBuilder();
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Sequence build() {
|
||||
SnowflakeSequence sequence = new SnowflakeSequence();
|
||||
sequence.setDatacenterId(this.datacenterId);
|
||||
sequence.setWorkerId(this.workerId);
|
||||
return sequence;
|
||||
}
|
||||
|
||||
public SnowflakeSeqBuilder datacenterId(long datacenterId) {
|
||||
this.datacenterId = datacenterId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SnowflakeSeqBuilder workerId(long workerId) {
|
||||
this.workerId = workerId;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.pig4cloud.pigx.common.sequence.exception;
|
||||
|
||||
/**
|
||||
* 序列号生成异常
|
||||
*
|
||||
* @author xuan on 2018/1/10.
|
||||
*/
|
||||
public class SeqException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public SeqException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public SeqException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.pig4cloud.pigx.common.sequence.properties;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2019-05-26
|
||||
* <p>
|
||||
* 发号器通用属性
|
||||
*/
|
||||
@Data
|
||||
class BaseSequenceProperties {
|
||||
|
||||
/**
|
||||
* 获取range步长[可选,默认:1000]
|
||||
*/
|
||||
private int step = 1000;
|
||||
|
||||
/**
|
||||
* 序列号分配起始值[可选:默认:0]
|
||||
*/
|
||||
private long stepStart = 0;
|
||||
|
||||
/**
|
||||
* 业务名称
|
||||
*/
|
||||
private String bizName = "pigx";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.pig4cloud.pigx.common.sequence.properties;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2019-05-26
|
||||
*/
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2019/5/26
|
||||
* <p>
|
||||
* 发号器DB配置属性
|
||||
*/
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "pigx.xsequence.db")
|
||||
public class SequenceDbProperties extends BaseSequenceProperties {
|
||||
|
||||
/**
|
||||
* 表名称
|
||||
*/
|
||||
private String tableName = "pigx_sequence";
|
||||
|
||||
/**
|
||||
* 重试次数
|
||||
*/
|
||||
private int retryTimes = 1;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.pig4cloud.pigx.common.sequence.properties;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2019-05-26
|
||||
*/
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2019/5/26
|
||||
* <p>
|
||||
* 发号器Redis配置属性
|
||||
*/
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "pigx.xsequence.redis")
|
||||
public class SequenceRedisProperties extends BaseSequenceProperties {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.pig4cloud.pigx.common.sequence.properties;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2019-05-26
|
||||
* <p>
|
||||
* Snowflake 发号器属性
|
||||
*/
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "pigx.xsequence.snowflake")
|
||||
public class SequenceSnowflakeProperties extends BaseSequenceProperties {
|
||||
|
||||
/**
|
||||
* 数据中心ID,值的范围在[0,31]之间,一般可以设置机房的IDC[必选]
|
||||
*/
|
||||
private long datacenterId;
|
||||
|
||||
/**
|
||||
* 工作机器ID,值的范围在[0,31]之间,一般可以设置机器编号[必选]
|
||||
*/
|
||||
private long workerId;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.pig4cloud.pigx.common.sequence.range;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2019-05-26
|
||||
* <p>
|
||||
* 名称生成器
|
||||
*/
|
||||
public interface BizName {
|
||||
|
||||
/**
|
||||
* 生成名称
|
||||
* @return 返回文本序号
|
||||
*/
|
||||
String create();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.pig4cloud.pigx.common.sequence.range;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* 序列号区间对象模型
|
||||
*
|
||||
* @author xuan on 2018/1/10.
|
||||
*/
|
||||
public class SeqRange {
|
||||
|
||||
/**
|
||||
* 区间的序列号开始值
|
||||
*/
|
||||
private final long min;
|
||||
|
||||
/**
|
||||
* 区间的序列号结束值
|
||||
*/
|
||||
private final long max;
|
||||
|
||||
/**
|
||||
* 区间的序列号当前值
|
||||
*/
|
||||
private final AtomicLong value;
|
||||
|
||||
/**
|
||||
* 区间的序列号是否分配完毕,每次分配完毕就会去重新获取一个新的区间
|
||||
*/
|
||||
private volatile boolean over = false;
|
||||
|
||||
public SeqRange(long min, long max) {
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
this.value = new AtomicLong(min);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回并递增下一个序列号
|
||||
* @return 下一个序列号,如果返回-1表示序列号分配完毕
|
||||
*/
|
||||
public long getAndIncrement() {
|
||||
long currentValue = value.getAndIncrement();
|
||||
if (currentValue > max) {
|
||||
over = true;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
public long getMin() {
|
||||
return min;
|
||||
}
|
||||
|
||||
public long getMax() {
|
||||
return max;
|
||||
}
|
||||
|
||||
public boolean isOver() {
|
||||
return over;
|
||||
}
|
||||
|
||||
public void setOver(boolean over) {
|
||||
this.over = over;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "max: " + max + ", min: " + min + ", value: " + value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.pig4cloud.pigx.common.sequence.range;
|
||||
|
||||
import com.pig4cloud.pigx.common.sequence.exception.SeqException;
|
||||
|
||||
/**
|
||||
* 区间管理器
|
||||
*
|
||||
* @author xuan on 2018/1/10.
|
||||
*/
|
||||
public interface SeqRangeMgr {
|
||||
|
||||
/**
|
||||
* 获取指定区间名的下一个区间
|
||||
* @param name 区间名
|
||||
* @return 返回区间
|
||||
* @throws SeqException 异常
|
||||
*/
|
||||
SeqRange nextRange(String name) throws SeqException;
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
void init();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
package com.pig4cloud.pigx.common.sequence.range.impl.db;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
|
||||
import com.pig4cloud.pigx.common.core.util.SpringContextHolder;
|
||||
import com.pig4cloud.pigx.common.sequence.exception.SeqException;
|
||||
import com.pig4cloud.pigx.common.sequence.range.impl.db.provider.SqlProviderFactory;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.*;
|
||||
|
||||
/**
|
||||
* 操作DB帮助类
|
||||
*
|
||||
* @author xuan on 2018/4/29.
|
||||
*/
|
||||
abstract class BaseDbHelper {
|
||||
|
||||
private static final SqlProviderFactory SQL_PROVIDER_FACTORY = SpringContextHolder
|
||||
.getBean(SqlProviderFactory.class);
|
||||
|
||||
private static final long DELTA = 100000000L;
|
||||
|
||||
private final static DefaultIdentifierGenerator identifierGenerator = new DefaultIdentifierGenerator();
|
||||
|
||||
/**
|
||||
* 创建表
|
||||
* @param dataSource DB来源
|
||||
* @param tableName 表名
|
||||
*/
|
||||
static void createTable(DataSource dataSource, String tableName) {
|
||||
|
||||
Connection conn = null;
|
||||
Statement stmt = null;
|
||||
|
||||
try {
|
||||
conn = dataSource.getConnection();
|
||||
stmt = conn.createStatement();
|
||||
stmt.executeUpdate(SQL_PROVIDER_FACTORY.getCreateTableSql().replace("#tableName", tableName));
|
||||
}
|
||||
catch (SQLException e) {
|
||||
throw new SeqException(e);
|
||||
}
|
||||
finally {
|
||||
closeQuietly(stmt);
|
||||
closeQuietly(conn);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入数据区间
|
||||
* @param dataSource DB来源
|
||||
* @param tableName 表名
|
||||
* @param name 区间名称
|
||||
* @param stepStart 初始位置
|
||||
*/
|
||||
private static void insertRange(DataSource dataSource, String tableName, String name, long stepStart) {
|
||||
|
||||
Connection conn = null;
|
||||
PreparedStatement stmt = null;
|
||||
|
||||
try {
|
||||
conn = dataSource.getConnection();
|
||||
stmt = conn.prepareStatement(SQL_PROVIDER_FACTORY.getInsertRangeSql().replace("#tableName", tableName));
|
||||
stmt.setLong(1, identifierGenerator.nextId(null));
|
||||
stmt.setString(2, name);
|
||||
stmt.setLong(3, stepStart);
|
||||
stmt.setTimestamp(4, new Timestamp(System.currentTimeMillis()));
|
||||
stmt.setTimestamp(5, new Timestamp(System.currentTimeMillis()));
|
||||
stmt.executeUpdate();
|
||||
}
|
||||
catch (SQLException e) {
|
||||
throw new SeqException(e);
|
||||
}
|
||||
finally {
|
||||
closeQuietly(stmt);
|
||||
closeQuietly(conn);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新区间,乐观策略
|
||||
* @param dataSource DB来源
|
||||
* @param tableName 表名
|
||||
* @param newValue 更新新数据
|
||||
* @param oldValue 更新旧数据
|
||||
* @param name 区间名称
|
||||
* @return 成功/失败
|
||||
*/
|
||||
static boolean updateRange(DataSource dataSource, String tableName, Long newValue, Long oldValue, String name) {
|
||||
|
||||
Connection conn = null;
|
||||
PreparedStatement stmt = null;
|
||||
|
||||
try {
|
||||
conn = dataSource.getConnection();
|
||||
stmt = conn.prepareStatement(SQL_PROVIDER_FACTORY.getUpdateRangeSql().replace("#tableName", tableName));
|
||||
stmt.setLong(1, newValue);
|
||||
stmt.setTimestamp(2, new Timestamp(System.currentTimeMillis()));
|
||||
stmt.setString(3, name);
|
||||
stmt.setLong(4, oldValue);
|
||||
int affectedRows = stmt.executeUpdate();
|
||||
return affectedRows > 0;
|
||||
}
|
||||
catch (SQLException e) {
|
||||
throw new SeqException(e);
|
||||
}
|
||||
finally {
|
||||
closeQuietly(stmt);
|
||||
closeQuietly(conn);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询区间,如果区间不存在,会新增一个区间,并返回null,由上层重新执行
|
||||
* @param dataSource DB来源
|
||||
* @param tableName 来源
|
||||
* @param name 区间名称
|
||||
* @param stepStart 初始位置
|
||||
* @return 区间值
|
||||
*/
|
||||
static Long selectRange(DataSource dataSource, String tableName, String name, long stepStart) {
|
||||
Connection conn = null;
|
||||
PreparedStatement stmt = null;
|
||||
ResultSet rs = null;
|
||||
long oldValue;
|
||||
|
||||
try {
|
||||
conn = dataSource.getConnection();
|
||||
stmt = conn.prepareStatement(SQL_PROVIDER_FACTORY.getSelectRangeSql().replace("#tableName", tableName));
|
||||
stmt.setString(1, name);
|
||||
|
||||
rs = stmt.executeQuery();
|
||||
if (!rs.next()) {
|
||||
// 没有此类型数据,需要初始化
|
||||
insertRange(dataSource, tableName, name, stepStart);
|
||||
return null;
|
||||
}
|
||||
oldValue = rs.getLong(1);
|
||||
|
||||
if (oldValue < 0) {
|
||||
String msg = "Sequence value cannot be less than zero, value = " + oldValue
|
||||
+ ", please check table sequence" + tableName;
|
||||
throw new SeqException(msg);
|
||||
}
|
||||
|
||||
if (oldValue > Long.MAX_VALUE - DELTA) {
|
||||
String msg = "Sequence value overflow, value = " + oldValue + ", please check table sequence"
|
||||
+ tableName;
|
||||
throw new SeqException(msg);
|
||||
}
|
||||
|
||||
return oldValue;
|
||||
}
|
||||
catch (SQLException e) {
|
||||
throw new SeqException(e);
|
||||
}
|
||||
finally {
|
||||
closeQuietly(rs);
|
||||
closeQuietly(stmt);
|
||||
closeQuietly(conn);
|
||||
}
|
||||
}
|
||||
|
||||
private static void closeQuietly(AutoCloseable closeable) {
|
||||
if (null != closeable) {
|
||||
try {
|
||||
closeable.close();
|
||||
}
|
||||
catch (Throwable e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Boolean existTable(DataSource dataSource, String tableName) {
|
||||
|
||||
String existTableSql = SQL_PROVIDER_FACTORY.getExistTableSql();
|
||||
if (StrUtil.isBlank(existTableSql)) {
|
||||
return true;
|
||||
}
|
||||
Connection conn = null;
|
||||
Statement stmt = null;
|
||||
|
||||
try {
|
||||
conn = dataSource.getConnection();
|
||||
stmt = conn.createStatement();
|
||||
ResultSet resultSet = stmt
|
||||
.executeQuery(SQL_PROVIDER_FACTORY.getExistTableSql().replace("#tableName", tableName));
|
||||
|
||||
if (!resultSet.next()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int count = resultSet.getInt(1);
|
||||
|
||||
if (0 == count) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (SQLException e) {
|
||||
throw new SeqException(e);
|
||||
}
|
||||
finally {
|
||||
closeQuietly(stmt);
|
||||
closeQuietly(conn);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
package com.pig4cloud.pigx.common.sequence.range.impl.db;
|
||||
|
||||
import com.pig4cloud.pigx.common.sequence.exception.SeqException;
|
||||
import com.pig4cloud.pigx.common.sequence.range.SeqRange;
|
||||
import com.pig4cloud.pigx.common.sequence.range.SeqRangeMgr;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
/**
|
||||
* DB区间管理器
|
||||
*
|
||||
* @author xuan on 2018/4/29.
|
||||
*/
|
||||
public class DbSeqRangeMgr implements SeqRangeMgr {
|
||||
|
||||
/**
|
||||
* 区间步长
|
||||
*/
|
||||
private int step = 1000;
|
||||
|
||||
/**
|
||||
* 区间起始位置,真实从stepStart+1开始
|
||||
*/
|
||||
private long stepStart = 0;
|
||||
|
||||
/**
|
||||
* 获取区间失败重试次数
|
||||
*/
|
||||
private int retryTimes = 100;
|
||||
|
||||
/**
|
||||
* DB来源
|
||||
*/
|
||||
private DataSource dataSource;
|
||||
|
||||
/**
|
||||
* 表名,默认range
|
||||
*/
|
||||
private String tableName = "range";
|
||||
|
||||
@Override
|
||||
public SeqRange nextRange(String name) throws SeqException {
|
||||
if (isEmpty(name)) {
|
||||
throw new SecurityException("[DbSeqRangeMgr-nextRange] name is empty.");
|
||||
}
|
||||
|
||||
Long oldValue;
|
||||
Long newValue;
|
||||
|
||||
for (int i = 0; i < getRetryTimes(); i++) {
|
||||
oldValue = BaseDbHelper.selectRange(getDataSource(), getRealTableName(), name, getStepStart());
|
||||
|
||||
if (null == oldValue) {
|
||||
// 区间不存在,重试
|
||||
continue;
|
||||
}
|
||||
|
||||
newValue = oldValue + getStep();
|
||||
|
||||
if (BaseDbHelper.updateRange(getDataSource(), getRealTableName(), newValue, oldValue, name)) {
|
||||
return new SeqRange(oldValue + 1, newValue);
|
||||
}
|
||||
// else 失败重试
|
||||
}
|
||||
|
||||
throw new SeqException("Retried too many times, retryTimes = " + getRetryTimes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
checkParam();
|
||||
|
||||
// 判断是否需要创建表
|
||||
if (BaseDbHelper.existTable(getDataSource(), getRealTableName())) {
|
||||
BaseDbHelper.createTable(getDataSource(), getRealTableName());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isEmpty(String str) {
|
||||
return null == str || str.trim().length() == 0;
|
||||
}
|
||||
|
||||
private String getRealTableName() {
|
||||
return getTableName();
|
||||
}
|
||||
|
||||
private void checkParam() {
|
||||
if (step <= 0) {
|
||||
throw new SecurityException("[DbSeqRangeMgr-checkParam] step must greater than 0.");
|
||||
}
|
||||
if (stepStart < 0) {
|
||||
throw new SecurityException("[DbSeqRangeMgr-setStepStart] stepStart < 0.");
|
||||
}
|
||||
if (retryTimes <= 0) {
|
||||
throw new SecurityException("[DbSeqRangeMgr-setRetryTimes] retryTimes must greater than 0.");
|
||||
}
|
||||
if (null == dataSource) {
|
||||
throw new SecurityException("[DbSeqRangeMgr-setDataSource] dataSource is null.");
|
||||
}
|
||||
if (isEmpty(tableName)) {
|
||||
throw new SecurityException("[DbSeqRangeMgr-setTableName] tableName is empty.");
|
||||
}
|
||||
}
|
||||
|
||||
//////// getter and setter
|
||||
|
||||
public int getStep() {
|
||||
return step;
|
||||
}
|
||||
|
||||
public void setStep(int step) {
|
||||
this.step = step;
|
||||
}
|
||||
|
||||
public long getStepStart() {
|
||||
return stepStart;
|
||||
}
|
||||
|
||||
public void setStepStart(long stepStart) {
|
||||
this.stepStart = stepStart;
|
||||
}
|
||||
|
||||
public int getRetryTimes() {
|
||||
return retryTimes;
|
||||
}
|
||||
|
||||
public void setRetryTimes(int retryTimes) {
|
||||
this.retryTimes = retryTimes;
|
||||
}
|
||||
|
||||
public DataSource getDataSource() {
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
public void setDataSource(DataSource dataSource) {
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
public String getTableName() {
|
||||
return tableName;
|
||||
}
|
||||
|
||||
public void setTableName(String tableName) {
|
||||
this.tableName = tableName;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.pig4cloud.pigx.common.sequence.range.impl.db.provider;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 数据源提供者
|
||||
*
|
||||
* @author 达梦数据库支持
|
||||
* @date 2022-04-27
|
||||
*
|
||||
* sql server
|
||||
*/
|
||||
@Component
|
||||
public class DmSqlProvider implements SqlProvider {
|
||||
|
||||
/**
|
||||
* 获取表是否存在
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public String getExistTableSql() {
|
||||
return "select count(*) from user_tables where table_name =upper('#tableName')";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取建表语句
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public String getCreateTableSql() {
|
||||
return "CREATE TABLE #tableName (ID BIGINT NOT NULL,VALUE BIGINT NOT NULL"
|
||||
+ ",NAME VARCHAR (64) NOT NULL,GMT_CREATE TIMESTAMP (0) NOT NULL,GMT_MODIFIED TIMESTAMP (0) NOT NULL"
|
||||
+ ",NOT CLUSTER PRIMARY KEY (ID),CONSTRAINT UK_NAME UNIQUE (NAME)) STORAGE (ON MAIN,CLUSTERBTR);";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean support(DbType dbType) {
|
||||
return DbType.DM.equals(dbType);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.pig4cloud.pigx.common.sequence.range.impl.db.provider;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 瀚高数据源
|
||||
*
|
||||
* @author lengleng
|
||||
* @date 2023-04-26
|
||||
*/
|
||||
@Component
|
||||
public class HighGoSqlProvider implements SqlProvider {
|
||||
|
||||
/**
|
||||
* 获取建表语句
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public String getCreateTableSql() {
|
||||
return "CREATE TABLE IF NOT EXISTS #tableName (ID int8 PRIMARY KEY NOT NULL"
|
||||
+ ",VALUE int8 NOT NULL,NAME VARCHAR (266) NOT NULL,gmt_create TIMESTAMP (6) NOT NULL"
|
||||
+ ",gmt_modified TIMESTAMP (6) NOT NULL)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean support(DbType dbType) {
|
||||
return DbType.HIGH_GO.equals(dbType);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.pig4cloud.pigx.common.sequence.range.impl.db.provider;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2023/4/23 kingbase数据库支持
|
||||
*/
|
||||
@Component
|
||||
public class KingbaseSqlProvider implements SqlProvider {
|
||||
|
||||
/**
|
||||
* 获取建表语句
|
||||
* @return SQL
|
||||
*/
|
||||
@Override
|
||||
public String getCreateTableSql() {
|
||||
return "CREATE TABLE IF NOT EXISTS #tableName (ID INT8 PRIMARY KEY NOT NULL,VALUE INT8 NOT NULL,NAME VARCHAR (266) NOT NULL,gmt_create TIMESTAMP (6) NOT NULL,gmt_modified TIMESTAMP (6) NOT NULL)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean support(DbType dbType) {
|
||||
return DbType.KINGBASE_ES.equals(dbType);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.pig4cloud.pigx.common.sequence.range.impl.db.provider;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 数据源提供者
|
||||
*
|
||||
* @author hanyichao
|
||||
* @date 2022-04-27
|
||||
*
|
||||
* sql server
|
||||
*/
|
||||
@Component
|
||||
public class MssqlSqlProvider implements SqlProvider {
|
||||
|
||||
/**
|
||||
* 获取建表语句
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public String getCreateTableSql() {
|
||||
return "IF NOT EXISTS (\n"
|
||||
+ "SELECT*FROM sys.all_objects WHERE object_id=OBJECT_ID(N'#tableName') AND type IN ('U')) \n"
|
||||
+ "CREATE TABLE #tableName (id bigint NOT NULL,VALUE bigint NOT NULL,name nvarchar (64) COLLATE "
|
||||
+ "Chinese_PRC_CI_AS NOT NULL,gmt_create datetime2 (7) NOT NULL,gmt_modified datetime2 (7) NOT NULL) GO";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean support(DbType dbType) {
|
||||
return DbType.SQL_SERVER2005.equals(dbType);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.pig4cloud.pigx.common.sequence.range.impl.db.provider;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 数据源提供者
|
||||
*
|
||||
* @author lishangbu
|
||||
* @date 2021/12/29
|
||||
*/
|
||||
@Component
|
||||
public class MysqlSqlProvider implements SqlProvider {
|
||||
|
||||
/**
|
||||
* 获取建表语句
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public String getCreateTableSql() {
|
||||
return "CREATE TABLE IF NOT EXISTS #tableName(" + "id bigint(20) NOT NULL AUTO_INCREMENT,"
|
||||
+ "value bigint(20) NOT NULL," + "name varchar(64) NOT NULL," + "gmt_create DATETIME NOT NULL,"
|
||||
+ "gmt_modified DATETIME NOT NULL," + "PRIMARY KEY (`id`),UNIQUE uk_name (`name`)" + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean support(DbType dbType) {
|
||||
return DbType.MYSQL.equals(dbType);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.pig4cloud.pigx.common.sequence.range.impl.db.provider;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* oracle数据源
|
||||
*
|
||||
* @author lishangbu
|
||||
* @date 2021/12/29
|
||||
*/
|
||||
@Component
|
||||
public class OracleSqlProvider implements SqlProvider {
|
||||
|
||||
/**
|
||||
* 获取表是否存在
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public String getExistTableSql() {
|
||||
return "select count(*) from user_tables where table_name =upper('#tableName')";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取建表语句
|
||||
* @return SQL
|
||||
*/
|
||||
@Override
|
||||
public String getCreateTableSql() {
|
||||
return "CREATE TABLE #tableName " + "(id NUMBER (20,0) VISIBLE NOT NULL,value NUMBER (20,0) VISIBLE NOT NULL"
|
||||
+ ",name VARCHAR2 (96 BYTE) VISIBLE,gmt_create DATE VISIBLE NOT NULL"
|
||||
+ ",gmt_modified DATE VISIBLE NOT NULL)";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取更新范围语句
|
||||
* @return
|
||||
*/
|
||||
public String getUpdateRangeSql() {
|
||||
return "UPDATE #tableName SET value=?,gmt_modified=? WHERE name=? AND value=?";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取查询范围语句
|
||||
* @return
|
||||
*/
|
||||
public String getSelectRangeSql() {
|
||||
return "SELECT value FROM #tableName WHERE name=?";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean support(DbType dbType) {
|
||||
return DbType.ORACLE.equals(dbType);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.pig4cloud.pigx.common.sequence.range.impl.db.provider;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 数据源提供者
|
||||
*
|
||||
* @author lengleng
|
||||
* @date 2022-01-13
|
||||
*/
|
||||
@Component
|
||||
public class PostgreSqlProvider implements SqlProvider {
|
||||
|
||||
/**
|
||||
* 获取建表语句
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public String getCreateTableSql() {
|
||||
return "CREATE TABLE IF NOT EXISTS #tableName (ID int8 PRIMARY KEY NOT NULL"
|
||||
+ ",VALUE int8 NOT NULL,NAME VARCHAR (266) NOT NULL,gmt_create TIMESTAMP (6) NOT NULL"
|
||||
+ ",gmt_modified TIMESTAMP (6) NOT NULL)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean support(DbType dbType) {
|
||||
return DbType.POSTGRE_SQL.equals(dbType);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.pig4cloud.pigx.common.sequence.range.impl.db.provider;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
|
||||
/**
|
||||
* 数据源提供者
|
||||
*
|
||||
* @author lishangbu
|
||||
* @date 2021/12/29
|
||||
*/
|
||||
public interface SqlProvider {
|
||||
|
||||
/**
|
||||
* 获取表是否存在
|
||||
* @return
|
||||
*/
|
||||
default String getExistTableSql() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取建表语句
|
||||
* @return
|
||||
*/
|
||||
String getCreateTableSql();
|
||||
|
||||
/**
|
||||
* 获取插入范围语句
|
||||
* @return
|
||||
*/
|
||||
default String getInsertRangeSql() {
|
||||
return "INSERT INTO #tableName (id,name,value,gmt_create,gmt_modified)" + " VALUES(?,?,?,?,?)";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取更新范围语句
|
||||
* @return
|
||||
*/
|
||||
default String getUpdateRangeSql() {
|
||||
return "UPDATE #tableName SET value=?,gmt_modified=? WHERE name=? AND value=?";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取查询范围语句
|
||||
* @return
|
||||
*/
|
||||
default String getSelectRangeSql() {
|
||||
return "SELECT value FROM #tableName WHERE name=?";
|
||||
}
|
||||
|
||||
Boolean support(DbType dbType);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.pig4cloud.pigx.common.sequence.range.impl.db.provider;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import com.baomidou.mybatisplus.extension.toolkit.JdbcUtils;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* sql语句提供工厂
|
||||
*
|
||||
* @author lishangbu
|
||||
* @date 2021/12/29
|
||||
*/
|
||||
@Lazy(value = false)
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class SqlProviderFactory {
|
||||
|
||||
private final DataSourceProperties dataSourceProperties;
|
||||
|
||||
private final List<SqlProvider> sqlProviders;
|
||||
|
||||
private final Environment environment;
|
||||
|
||||
public SqlProvider getSqlProvider() {
|
||||
String url = dataSourceProperties.getUrl();
|
||||
// druid 形式 进行降级获取
|
||||
if (StrUtil.isBlank(url)) {
|
||||
url = environment.getProperty("spring.datasource.druid.url");
|
||||
}
|
||||
|
||||
DbType dbType = JdbcUtils.getDbType(url);
|
||||
for (SqlProvider sqlProvider : sqlProviders) {
|
||||
if (sqlProvider.support(dbType)) {
|
||||
return sqlProvider;
|
||||
}
|
||||
}
|
||||
throw new UnsupportedOperationException("不支持的数据源");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取插入范围语句
|
||||
* @return 插入范围语句
|
||||
*/
|
||||
public String getCreateTableSql() {
|
||||
SqlProvider sqlProvider = getSqlProvider();
|
||||
return sqlProvider.getCreateTableSql();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取插入范围语句
|
||||
* @return 插入范围语句
|
||||
*/
|
||||
public String getInsertRangeSql() {
|
||||
SqlProvider sqlProvider = getSqlProvider();
|
||||
return sqlProvider.getInsertRangeSql();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取查询范围语句
|
||||
* @return
|
||||
*/
|
||||
public String getSelectRangeSql() {
|
||||
SqlProvider sqlProvider = getSqlProvider();
|
||||
return sqlProvider.getSelectRangeSql();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取更新范围语句
|
||||
* @return 更新范围语句
|
||||
*/
|
||||
public String getUpdateRangeSql() {
|
||||
SqlProvider sqlProvider = getSqlProvider();
|
||||
return sqlProvider.getUpdateRangeSql();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取是否创建表语句
|
||||
* @return SQL语句
|
||||
*/
|
||||
public String getExistTableSql() {
|
||||
SqlProvider sqlProvider = getSqlProvider();
|
||||
return sqlProvider.getExistTableSql();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.pig4cloud.pigx.common.sequence.range.impl.name;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import com.pig4cloud.pigx.common.sequence.range.BizName;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2019-05-26
|
||||
* <p>
|
||||
* 根据时间重置bizname
|
||||
*/
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class DateBizName implements BizName {
|
||||
|
||||
private String bizName;
|
||||
|
||||
/**
|
||||
* 生成空间名称
|
||||
*/
|
||||
@Override
|
||||
public String create() {
|
||||
return bizName + DateUtil.today();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.pig4cloud.pigx.common.sequence.range.impl.name;
|
||||
|
||||
import com.pig4cloud.pigx.common.sequence.range.BizName;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2019-05-26 根据传入返回bizname
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class DefaultBizName implements BizName {
|
||||
|
||||
private String bizName;
|
||||
|
||||
/**
|
||||
* 生成空间名称
|
||||
*/
|
||||
@Override
|
||||
public String create() {
|
||||
return bizName;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
package com.pig4cloud.pigx.common.sequence.range.impl.redis;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.pig4cloud.pigx.common.sequence.exception.SeqException;
|
||||
import com.pig4cloud.pigx.common.sequence.range.SeqRange;
|
||||
import com.pig4cloud.pigx.common.sequence.range.SeqRangeMgr;
|
||||
import redis.clients.jedis.Jedis;
|
||||
|
||||
/**
|
||||
* Redis区间管理器
|
||||
*
|
||||
* @author xuan on 2018/5/8.
|
||||
*/
|
||||
public class RedisSeqRangeMgr implements SeqRangeMgr {
|
||||
|
||||
/**
|
||||
* 前缀防止key重复
|
||||
*/
|
||||
private final static String KEY_PREFIX = "x_sequence_";
|
||||
|
||||
/**
|
||||
* redis客户端
|
||||
*/
|
||||
private Jedis jedis;
|
||||
|
||||
/**
|
||||
* IP
|
||||
*/
|
||||
private String ip;
|
||||
|
||||
/**
|
||||
* PORT
|
||||
*/
|
||||
private Integer port;
|
||||
|
||||
/**
|
||||
* 验证权限
|
||||
*/
|
||||
private String auth;
|
||||
|
||||
/**
|
||||
* 区间步长
|
||||
*/
|
||||
private int step = 1000;
|
||||
|
||||
/**
|
||||
* 区间起始位置,真实从stepStart+1开始
|
||||
*/
|
||||
private long stepStart = 0;
|
||||
|
||||
/**
|
||||
* 标记业务key是否存在,如果false,在取nextRange时,会取check一把 这个boolean只为提高性能,不用每次都取redis check
|
||||
*/
|
||||
private volatile boolean keyAlreadyExist;
|
||||
|
||||
@Override
|
||||
public SeqRange nextRange(String name) throws SeqException {
|
||||
if (!keyAlreadyExist) {
|
||||
Boolean isExists = jedis.exists(getRealKey(name));
|
||||
if (!isExists) {
|
||||
// 第一次不存在,进行初始化,setnx不存在就set,存在就忽略
|
||||
jedis.setnx(getRealKey(name), String.valueOf(stepStart));
|
||||
}
|
||||
keyAlreadyExist = true;
|
||||
}
|
||||
|
||||
Long max = jedis.incrBy(getRealKey(name), step);
|
||||
Long min = max - step + 1;
|
||||
return new SeqRange(min, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
checkParam();
|
||||
jedis = new Jedis(ip, port);
|
||||
if (StrUtil.isNotBlank(auth)) {
|
||||
jedis.auth(auth);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkParam() {
|
||||
if (isEmpty(ip)) {
|
||||
throw new SecurityException("[RedisSeqRangeMgr-checkParam] ip is empty.");
|
||||
}
|
||||
if (null == port) {
|
||||
throw new SecurityException("[RedisSeqRangeMgr-checkParam] port is null.");
|
||||
}
|
||||
}
|
||||
|
||||
private String getRealKey(String name) {
|
||||
return KEY_PREFIX + name;
|
||||
}
|
||||
|
||||
private boolean isEmpty(String str) {
|
||||
return null == str || str.trim().length() == 0;
|
||||
}
|
||||
|
||||
public String getIp() {
|
||||
return ip;
|
||||
}
|
||||
|
||||
public void setIp(String ip) {
|
||||
this.ip = ip;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public int getStep() {
|
||||
return step;
|
||||
}
|
||||
|
||||
public void setStep(int step) {
|
||||
this.step = step;
|
||||
}
|
||||
|
||||
public String getAuth() {
|
||||
return auth;
|
||||
}
|
||||
|
||||
public void setAuth(String auth) {
|
||||
this.auth = auth;
|
||||
}
|
||||
|
||||
public long getStepStart() {
|
||||
return stepStart;
|
||||
}
|
||||
|
||||
public void setStepStart(long stepStart) {
|
||||
this.stepStart = stepStart;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.pig4cloud.pigx.common.sequence.sequence;
|
||||
|
||||
import com.pig4cloud.pigx.common.sequence.range.BizName;
|
||||
import com.pig4cloud.pigx.common.sequence.range.SeqRangeMgr;
|
||||
|
||||
/**
|
||||
* 序列号区间生成器接口
|
||||
*
|
||||
* @author xuan on 2018/5/6.
|
||||
*/
|
||||
public interface RangeSequence extends Sequence {
|
||||
|
||||
/**
|
||||
* 设置区间管理器
|
||||
* @param seqRangeMgr 区间管理器
|
||||
*/
|
||||
void setSeqRangeMgr(SeqRangeMgr seqRangeMgr);
|
||||
|
||||
/**
|
||||
* 设置获取序列号名称
|
||||
* @param name 名称
|
||||
*/
|
||||
void setName(BizName name);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.pig4cloud.pigx.common.sequence.sequence;
|
||||
|
||||
import com.pig4cloud.pigx.common.sequence.exception.SeqException;
|
||||
|
||||
/**
|
||||
* 序列号生成器接口
|
||||
*
|
||||
* @author xuan on 2018/1/10.
|
||||
*/
|
||||
public interface Sequence {
|
||||
|
||||
/**
|
||||
* 生成下一个序列号
|
||||
* @return 序列号
|
||||
* @throws SeqException 序列号异常
|
||||
*/
|
||||
long nextValue() throws SeqException;
|
||||
|
||||
/**
|
||||
* 下一个生成序号(带格式)
|
||||
* @return
|
||||
* @throws SeqException
|
||||
*/
|
||||
String nextNo() throws SeqException;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package com.pig4cloud.pigx.common.sequence.sequence.impl;
|
||||
|
||||
import cn.hutool.core.date.DatePattern;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import com.pig4cloud.pigx.common.sequence.exception.SeqException;
|
||||
import com.pig4cloud.pigx.common.sequence.range.BizName;
|
||||
import com.pig4cloud.pigx.common.sequence.range.SeqRange;
|
||||
import com.pig4cloud.pigx.common.sequence.range.SeqRangeMgr;
|
||||
import com.pig4cloud.pigx.common.sequence.sequence.RangeSequence;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* 序列号区间生成器接口默认实现
|
||||
*
|
||||
* @author xuan on 2018/1/10.
|
||||
* <p>
|
||||
* 根据biz name 自增
|
||||
*/
|
||||
public class DefaultRangeSequence implements RangeSequence {
|
||||
|
||||
/**
|
||||
* 获取区间是加一把独占锁防止资源冲突
|
||||
*/
|
||||
private final Lock lock = new ReentrantLock();
|
||||
|
||||
/**
|
||||
* 序列号区间管理器
|
||||
*/
|
||||
private SeqRangeMgr seqRangeMgr;
|
||||
|
||||
/**
|
||||
* 当前序列号区间
|
||||
*/
|
||||
private volatile SeqRange currentRange;
|
||||
|
||||
private static Map<String, SeqRange> seqRangeMap = new ConcurrentHashMap<>(8);
|
||||
|
||||
/**
|
||||
* 需要获取区间的业务名称
|
||||
*/
|
||||
private BizName bizName;
|
||||
|
||||
@Override
|
||||
public long nextValue() throws SeqException {
|
||||
String name = bizName.create();
|
||||
|
||||
currentRange = seqRangeMap.get(name);
|
||||
// 当前区间不存在,重新获取一个区间
|
||||
if (null == currentRange) {
|
||||
lock.lock();
|
||||
try {
|
||||
if (null == currentRange) {
|
||||
currentRange = seqRangeMgr.nextRange(name);
|
||||
seqRangeMap.put(name, currentRange);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
// 当value值为-1时,表明区间的序列号已经分配完,需要重新获取区间
|
||||
long value = currentRange.getAndIncrement();
|
||||
if (value == -1) {
|
||||
lock.lock();
|
||||
try {
|
||||
for (;;) {
|
||||
if (currentRange.isOver()) {
|
||||
currentRange = seqRangeMgr.nextRange(name);
|
||||
seqRangeMap.put(name, currentRange);
|
||||
}
|
||||
|
||||
value = currentRange.getAndIncrement();
|
||||
if (value == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
if (value < 0) {
|
||||
throw new SeqException("Sequence value overflow, value = " + value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 下一个生成序号(带格式)
|
||||
* @return
|
||||
* @throws SeqException
|
||||
*/
|
||||
@Override
|
||||
public String nextNo() throws SeqException {
|
||||
return String.format("%s%05d", DateUtil.format(new Date(), DatePattern.PURE_DATE_FORMAT), nextValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSeqRangeMgr(SeqRangeMgr seqRangeMgr) {
|
||||
this.seqRangeMgr = seqRangeMgr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(BizName name) {
|
||||
this.bizName = name;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
package com.pig4cloud.pigx.common.sequence.sequence.impl;
|
||||
|
||||
import com.pig4cloud.pigx.common.sequence.exception.SeqException;
|
||||
import com.pig4cloud.pigx.common.sequence.sequence.Sequence;
|
||||
|
||||
/**
|
||||
* 使用雪花算法 一个long类型的数据,64位。以下是每位的具体含义。 <br>
|
||||
* snowflake的结构如下(每部分用-分开): <br>
|
||||
* 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 <br>
|
||||
* (1)第一位为未使用 (2)接下来的41位为毫秒级时间(41位的长度可以使用69年) (3)然后是5位datacenterId (4)5位workerId
|
||||
* (5)最后12位是毫秒内的计数(12位的计数顺序号支持每个节点每毫秒产生4096个ID序号) <br>
|
||||
* 一共加起来刚好64位,为一个Long型。(转换成字符串长度为18)
|
||||
*
|
||||
* @author xuan on 2018/5/9.
|
||||
*/
|
||||
public class SnowflakeSequence implements Sequence {
|
||||
|
||||
/**
|
||||
* 开始时间截 (2018-01-01)
|
||||
*/
|
||||
private final long twepoch = 1514736000000L;
|
||||
|
||||
/**
|
||||
* 机器id所占的位数
|
||||
*/
|
||||
private final long workerIdBits = 5L;
|
||||
|
||||
/**
|
||||
* 数据标识id所占的位数
|
||||
*/
|
||||
private final long datacenterIdBits = 5L;
|
||||
|
||||
/**
|
||||
* 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
|
||||
*/
|
||||
private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
|
||||
|
||||
/**
|
||||
* 支持的最大数据标识id,结果是31
|
||||
*/
|
||||
private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
|
||||
|
||||
/**
|
||||
* 序列在id中占的位数
|
||||
*/
|
||||
private final long sequenceBits = 12L;
|
||||
|
||||
/**
|
||||
* 机器ID向左移12位
|
||||
*/
|
||||
private final long workerIdShift = sequenceBits;
|
||||
|
||||
/**
|
||||
* 数据标识id向左移17位(12+5)
|
||||
*/
|
||||
private final long datacenterIdShift = sequenceBits + workerIdBits;
|
||||
|
||||
/**
|
||||
* 时间截向左移22位(5+5+12)
|
||||
*/
|
||||
private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
|
||||
|
||||
/**
|
||||
* 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)
|
||||
*/
|
||||
private final long sequenceMask = -1L ^ (-1L << sequenceBits);
|
||||
|
||||
/**
|
||||
* 工作机器ID(0~31)
|
||||
*/
|
||||
private long workerId;
|
||||
|
||||
/**
|
||||
* 数据中心ID(0~31)
|
||||
*/
|
||||
private long datacenterId;
|
||||
|
||||
/**
|
||||
* 毫秒内序列(0~4095)
|
||||
*/
|
||||
private long sequence = 0L;
|
||||
|
||||
/**
|
||||
* 上次生成ID的时间截
|
||||
*/
|
||||
private long lastTimestamp = -1L;
|
||||
|
||||
@Override
|
||||
public synchronized long nextValue() throws SeqException {
|
||||
long timestamp = timeGen();
|
||||
|
||||
// 如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
|
||||
if (timestamp < lastTimestamp) {
|
||||
throw new SeqException("[SnowflakeSequence-nextValue] 当前时间小于上次生成序列号的时间,时间被回退了,请确认服务器时间的设置.");
|
||||
}
|
||||
|
||||
// 如果是同一时间生成的,则进行毫秒内序列
|
||||
if (lastTimestamp == timestamp) {
|
||||
sequence = (sequence + 1) & sequenceMask;
|
||||
// 毫秒内序列溢出
|
||||
if (sequence == 0) {
|
||||
// 阻塞到下一个毫秒,获得新的时间戳
|
||||
timestamp = tilNextMillis(lastTimestamp);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// 时间戳改变,毫秒内序列重置
|
||||
sequence = 0L;
|
||||
}
|
||||
|
||||
// 上次生成ID的时间截
|
||||
lastTimestamp = timestamp;
|
||||
|
||||
// 移位并通过或运算拼到一起组成64位的ID
|
||||
return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift)
|
||||
| (workerId << workerIdShift) | sequence;
|
||||
}
|
||||
|
||||
/**
|
||||
* 阻塞到下一个毫秒,直到获得新的时间戳
|
||||
* @param lastTimestamp 上次生成ID的时间截
|
||||
* @return 当前时间戳
|
||||
*/
|
||||
private long tilNextMillis(long lastTimestamp) {
|
||||
long timestamp = timeGen();
|
||||
while (timestamp <= lastTimestamp) {
|
||||
timestamp = timeGen();
|
||||
}
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回以毫秒为单位的当前时间
|
||||
* @return 当前时间(毫秒)
|
||||
*/
|
||||
private long timeGen() {
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public void setWorkerId(long workerId) {
|
||||
if (workerId > maxWorkerId) {
|
||||
throw new SeqException("[SnowflakeSequence-setWorkerId] workerId 不能大于31.");
|
||||
}
|
||||
|
||||
this.workerId = workerId;
|
||||
}
|
||||
|
||||
public void setDatacenterId(long datacenterId) {
|
||||
if (datacenterId > maxDatacenterId) {
|
||||
throw new SeqException("[SnowflakeSequence-setDatacenterId] datacenterId 不能大于31.");
|
||||
}
|
||||
|
||||
this.datacenterId = datacenterId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 下一个生成序号(带格式)
|
||||
* @return
|
||||
* @throws SeqException
|
||||
*/
|
||||
@Override
|
||||
public String nextNo() throws SeqException {
|
||||
return String.valueOf(nextValue());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
com.pig4cloud.pigx.common.sequence.SequenceAutoConfiguration
|
||||
Reference in New Issue
Block a user