✨ 添加新特性。 支付收单模块

This commit is contained in:
pigxcloud committed 2019-06-03 07:41:34 +08:00
1 parent 2ee8021b5e
commit 075c31a508
139 files changed
+4703 -4864

No files matched your search

@@ -0,0 +1,89 @@
package com.pig4cloud.common.sequence;
import com.pig4cloud.common.sequence.builder.DbSeqBuilder;
import com.pig4cloud.common.sequence.builder.RedisSeqBuilder;
import com.pig4cloud.common.sequence.builder.SnowflakeSeqBuilder;
import com.pig4cloud.common.sequence.properties.SequenceDbProperties;
import com.pig4cloud.common.sequence.properties.SequenceRedisProperties;
import com.pig4cloud.common.sequence.properties.SequenceSnowflakeProperties;
import com.pig4cloud.common.sequence.range.impl.name.DateBizName;
import com.pig4cloud.common.sequence.range.impl.name.DefaultBizName;
import com.pig4cloud.common.sequence.sequence.Sequence;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
/**
* @author lengleng
* @date 2019-05-26
*/
@Configuration
@ComponentScan("com.pig4cloud.common.sequence")
@ConditionalOnMissingBean(Sequence.class)
public class SequenceAutoConfiguration {
/**
* 数据库作为发号器的存储介质
*
* @param dataSource
* @param properties
* @return
*/
@Bean
@ConditionalOnBean(DataSource.class)
@ConditionalOnProperty("xsequence.db")
public Sequence dbSequence(DataSource dataSource,
SequenceDbProperties properties) {
return DbSeqBuilder
.create()
.bizName(new DefaultBizName(properties.getBizName()))
.dataSource(dataSource)
.step(properties.getStep())
.retryTimes(properties.getRetryTimes())
.tableName(properties.getTableName())
.build();
}
/**
* Redis 作为发号器的存储介质
*
* @param redisProperties
* @param properties
* @return
*/
@Bean
@ConditionalOnProperty("xsequence.redis")
public Sequence redisSequence(RedisProperties redisProperties,
SequenceRedisProperties properties) {
return RedisSeqBuilder
.create()
.bizName(new DateBizName(properties.getBizName()))
.ip(redisProperties.getHost())
.port(redisProperties.getPort())
.auth(redisProperties.getPassword())
.step(properties.getStep())
.build();
}
/**
* snowflak 算法作为发号器实现
*
* @param properties
* @return
*/
@Bean
@ConditionalOnProperty("xsequence.snowflak")
public Sequence snowflakeSequence(SequenceSnowflakeProperties properties) {
return SnowflakeSeqBuilder
.create()
.datacenterId(properties.getDatacenterId())
.workerId(properties.getWorkerId())
.build();
}
}
@@ -0,0 +1,97 @@
package com.pig4cloud.common.sequence.builder;
import com.pig4cloud.common.sequence.range.BizName;
import com.pig4cloud.common.sequence.range.impl.db.DbSeqRangeMgr;
import com.pig4cloud.common.sequence.sequence.Sequence;
import com.pig4cloud.common.sequence.sequence.impl.DefaultRangeSequence;
import javax.sql.DataSource;
/**
* 基于DB取步长,序列号生成器构建者
* Created by xuan on 2018/5/30.
*/
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,94 @@
package com.pig4cloud.common.sequence.builder;
import com.pig4cloud.common.sequence.range.BizName;
import com.pig4cloud.common.sequence.range.impl.redis.RedisSeqRangeMgr;
import com.pig4cloud.common.sequence.sequence.Sequence;
import com.pig4cloud.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,19 @@
package com.pig4cloud.common.sequence.builder;
import com.pig4cloud.common.sequence.sequence.Sequence;
/**
* 序列号生成器构建者
* @author xuan on 2018/5/30.
*/
public interface SeqBuilder {
/**
* 构建一个序列号生成器
*
* @return 序列号生成器
*/
Sequence build();
}
@@ -0,0 +1,45 @@
package com.pig4cloud.common.sequence.builder;
import com.pig4cloud.common.sequence.sequence.Sequence;
import com.pig4cloud.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,19 @@
package com.pig4cloud.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,27 @@
package com.pig4cloud.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,31 @@
package com.pig4cloud.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 = "xsequence.db")
public class SequenceDbProperties extends BaseSequenceProperties {
/**
* 表名称
*/
private String tableName;
/**
* 重试次数
*/
private int retryTimes;
}
@@ -0,0 +1,23 @@
package com.pig4cloud.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 = "xsequence.redis")
public class SequenceRedisProperties extends BaseSequenceProperties {
}
@@ -0,0 +1,26 @@
package com.pig4cloud.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 = "xsequence.snowflake")
public class SequenceSnowflakeProperties extends BaseSequenceProperties {
/**
* 数据中心ID,值的范围在[0,31]之间,一般可以设置机房的IDC[必选]
*/
private long datacenterId;
/**
* 工作机器ID,值的范围在[0,31]之间,一般可以设置机器编号[必选]
*/
private long workerId;
}
@@ -0,0 +1,15 @@
package com.pig4cloud.common.sequence.range;
/**
* @author lengleng
* @date 2019-05-26
* <p>
* 名称生成器
*/
public interface BizName {
/**
* 生成名称
*/
String create();
}
@@ -0,0 +1,70 @@
package com.pig4cloud.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.common.sequence.range;
import com.pig4cloud.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,172 @@
package com.pig4cloud.common.sequence.range.impl.db;
import com.pig4cloud.common.sequence.exception.SeqException;
import javax.sql.DataSource;
import java.sql.*;
/**
* 操作DB帮助类
* @author xuan on 2018/4/29.
*/
abstract class BaseDbHelper {
private static final long DELTA = 100000000L;
private final static String SQL_CREATE_TABLE =
"CREATE TABLE IF NOT EXISTS #tableName(" + "id bigint(20) NOT NULL AUTO_INCREMENT,"
+ "value bigint(20) NOT NULL," + "name varchar(32) NOT NULL," + "gmt_create DATETIME NOT NULL,"
+ "gmt_modified DATETIME NOT NULL," + "PRIMARY KEY (`id`),UNIQUE uk_name (`name`)" + ")";
private final static String SQL_INSERT_RANGE =
"INSERT IGNORE INTO #tableName(name,value,gmt_create,gmt_modified)" + " VALUE(?,?,?,?)";
private final static String SQL_UPDATE_RANGE =
"UPDATE #tableName SET value=?,gmt_modified=? WHERE name=? AND " + "value=?";
private final static String SQL_SELECT_RANGE = "SELECT value FROM #tableName WHERE name=?";
/**
* 创建表
*
* @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_CREATE_TABLE.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_INSERT_RANGE.replace("#tableName", tableName));
stmt.setString(1, name);
stmt.setLong(2, stepStart);
stmt.setTimestamp(3, new Timestamp(System.currentTimeMillis()));
stmt.setTimestamp(4, 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_UPDATE_RANGE.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_SELECT_RANGE.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
}
}
}
}
@@ -0,0 +1,139 @@
package com.pig4cloud.common.sequence.range.impl.db;
import com.pig4cloud.common.sequence.exception.SeqException;
import com.pig4cloud.common.sequence.range.SeqRange;
import com.pig4cloud.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();
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,26 @@
package com.pig4cloud.common.sequence.range.impl.name;
import cn.hutool.core.date.DateUtil;
import com.pig4cloud.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,24 @@
package com.pig4cloud.common.sequence.range.impl.name;
import com.pig4cloud.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,137 @@
package com.pig4cloud.common.sequence.range.impl.redis;
import cn.hutool.core.util.StrUtil;
import com.pig4cloud.common.sequence.exception.SeqException;
import com.pig4cloud.common.sequence.range.SeqRange;
import com.pig4cloud.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,26 @@
package com.pig4cloud.common.sequence.sequence;
import com.pig4cloud.common.sequence.range.BizName;
import com.pig4cloud.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,28 @@
package com.pig4cloud.common.sequence.sequence;
import com.pig4cloud.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,111 @@
package com.pig4cloud.common.sequence.sequence.impl;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.pig4cloud.common.sequence.exception.SeqException;
import com.pig4cloud.common.sequence.range.BizName;
import com.pig4cloud.common.sequence.range.SeqRange;
import com.pig4cloud.common.sequence.range.SeqRangeMgr;
import com.pig4cloud.common.sequence.sequence.RangeSequence;
import java.util.Date;
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 BizName bizName;
@Override
public long nextValue() throws SeqException {
String name = bizName.create();
//当前区间不存在,重新获取一个区间
if (null == currentRange) {
lock.lock();
try {
if (null == currentRange) {
currentRange = seqRangeMgr.nextRange(name);
}
} finally {
lock.unlock();
}
}
//当value值为-1时,表明区间的序列号已经分配完,需要重新获取区间
long value = currentRange.getAndIncrement();
if (value == -1) {
lock.lock();
try {
for (; ; ) {
if (currentRange.isOver()) {
currentRange = seqRangeMgr.nextRange(name);
}
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,177 @@
package com.pig4cloud.common.sequence.sequence.impl;
import com.pig4cloud.common.sequence.exception.SeqException;
import com.pig4cloud.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());
}
}