初始化提交

This commit is contained in:
USER-20221017CE\Administrator
2022-11-01 12:14:54 +08:00
commit d31fad2aa9
1733 changed files with 370203 additions and 0 deletions

118
tulingmall-unqid/pom.xml Normal file
View File

@@ -0,0 +1,118 @@
<?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>
<artifactId>tuling-mall</artifactId>
<groupId>com.tuling</groupId>
<version>0.0.5</version>
</parent>
<artifactId>tulingmall-unqid</artifactId>
<packaging>jar</packaging>
<name>tulingmall-unqid</name>
<description>图灵分布式ID生成服务</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!--leaf需要的依赖-->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>org.perf4j</groupId>
<artifactId>perf4j</artifactId>
<version>0.9.16</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.2</version>
</dependency>
<!--spring-boot的依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--加入springcloud alibaba-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<!-- <exclusions>-->
<!-- <exclusion>-->
<!-- <groupId>com.alibaba.nacos</groupId>-->
<!-- <artifactId>nacos-client</artifactId>-->
<!-- </exclusion>-->
<!-- </exclusions>-->
</dependency>
<!-- <dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-nacos-config</artifactId>
<version>2.1.1.RELEASE</version>
<exclusions>
<exclusion>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
</exclusion>
</exclusions>
</dependency>-->
<!-- <dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>1.4.2</version>
</dependency>-->
<!-- <dependency>-->
<!-- <groupId>com.alibaba.nacos</groupId>-->
<!-- <artifactId>nacos-common</artifactId>-->
<!-- <version>1.4.3</version>-->
<!-- </dependency>-->
<!--数据库相关-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.18</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.3.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.3.2.RELEASE</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,10 @@
DROP TABLE IF EXISTS `leaf_alloc`;
CREATE TABLE `leaf_alloc` (
`biz_tag` varchar(128) NOT NULL DEFAULT '' COMMENT '区分业务',
`max_id` bigint(20) NOT NULL DEFAULT '1' COMMENT '该biz_tag目前所被分配的ID号段的最大值',
`step` int(11) NOT NULL COMMENT '每次分配的号段长度',
`description` varchar(256) DEFAULT NULL COMMENT '文字描述',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`biz_tag`)
) ENGINE=InnoDB;

View File

@@ -0,0 +1,18 @@
package com.tuling;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient
@SpringBootApplication
//@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})
public class TulingmallUnqidApplication {
public static void main(String[] args) {
SpringApplication.run(TulingmallUnqidApplication.class, args);
}
}

View File

@@ -0,0 +1,9 @@
package com.tuling.leafcore;
import com.tuling.leafcore.common.Result;
public interface IDGen {
Result get(String key);
boolean init();
}

View File

@@ -0,0 +1,27 @@
package com.tuling.leafcore.common;
public class CheckVO {
private long timestamp;
private int workID;
public CheckVO(long timestamp, int workID) {
this.timestamp = timestamp;
this.workID = workID;
}
public long getTimestamp() {
return timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
public int getWorkID() {
return workID;
}
public void setWorkID(int workID) {
this.workID = workID;
}
}

View File

@@ -0,0 +1,22 @@
package com.tuling.leafcore.common;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.Properties;
public class PropertyFactory {
private static final Logger logger = LoggerFactory.getLogger(PropertyFactory.class);
private static final Properties prop = new Properties();
static {
try {
prop.load(PropertyFactory.class.getClassLoader().getResourceAsStream("leaf.properties"));
} catch (IOException e) {
logger.warn("Load Properties Ex", e);
}
}
public static Properties getProperties() {
return prop;
}
}

View File

@@ -0,0 +1,39 @@
package com.tuling.leafcore.common;
public class Result {
private long id;
private Status status;
public Result() {
}
public Result(long id, Status status) {
this.id = id;
this.status = status;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Result{");
sb.append("id=").append(id);
sb.append(", status=").append(status);
sb.append('}');
return sb.toString();
}
}

View File

@@ -0,0 +1,6 @@
package com.tuling.leafcore.common;
public enum Status {
SUCCESS,
EXCEPTION
}

View File

@@ -0,0 +1,75 @@
package com.tuling.leafcore.common;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
public class Utils {
private static final Logger logger = LoggerFactory.getLogger(Utils.class);
public static String getIp() {
String ip;
try {
List<String> ipList = getHostAddress(null);
// default the first
ip = (!ipList.isEmpty()) ? ipList.get(0) : "";
} catch (Exception ex) {
ip = "";
logger.warn("Utils get IP warn", ex);
}
return ip;
}
public static String getIp(String interfaceName) {
String ip;
interfaceName = interfaceName.trim();
try {
List<String> ipList = getHostAddress(interfaceName);
ip = (!ipList.isEmpty()) ? ipList.get(0) : "";
} catch (Exception ex) {
ip = "";
logger.warn("Utils get IP warn", ex);
}
return ip;
}
/**
* 获取已激活网卡的IP地址
*
* @param interfaceName 可指定网卡名称,null则获取全部
* @return List<String>
*/
private static List<String> getHostAddress(String interfaceName) throws SocketException {
List<String> ipList = new ArrayList<String>(5);
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
NetworkInterface ni = interfaces.nextElement();
Enumeration<InetAddress> allAddress = ni.getInetAddresses();
while (allAddress.hasMoreElements()) {
InetAddress address = allAddress.nextElement();
if (address.isLoopbackAddress()) {
// skip the loopback addr
continue;
}
if (address instanceof Inet6Address) {
// skip the IPv6 addr
continue;
}
String hostAddress = address.getHostAddress();
if (null == interfaceName) {
ipList.add(hostAddress);
} else if (interfaceName.equals(ni.getDisplayName())) {
ipList.add(hostAddress);
}
}
}
return ipList;
}
}

View File

@@ -0,0 +1,15 @@
package com.tuling.leafcore.common;
import com.tuling.leafcore.IDGen;
public class ZeroIDGen implements IDGen {
@Override
public Result get(String key) {
return new Result(0, Status.SUCCESS);
}
@Override
public boolean init() {
return true;
}
}

View File

@@ -0,0 +1,294 @@
package com.tuling.leafcore.segment;
import com.tuling.leafcore.IDGen;
import com.tuling.leafcore.common.Result;
import com.tuling.leafcore.common.Status;
import com.tuling.leafcore.segment.dao.IDAllocDao;
import com.tuling.leafcore.segment.model.LeafAlloc;
import com.tuling.leafcore.segment.model.Segment;
import com.tuling.leafcore.segment.model.SegmentBuffer;
import org.perf4j.StopWatch;
import org.perf4j.slf4j.Slf4JStopWatch;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
public class SegmentIDGenImpl implements IDGen {
private static final Logger logger = LoggerFactory.getLogger(SegmentIDGenImpl.class);
/**
* IDCache未初始化成功时的异常码
*/
private static final long EXCEPTION_ID_IDCACHE_INIT_FALSE = -1;
/**
* key不存在时的异常码
*/
private static final long EXCEPTION_ID_KEY_NOT_EXISTS = -2;
/**
* SegmentBuffer中的两个Segment均未从DB中装载时的异常码
*/
private static final long EXCEPTION_ID_TWO_SEGMENTS_ARE_NULL = -3;
/**
* 最大步长不超过100,0000
*/
private static final int MAX_STEP = 1000000;
/**
* 一个Segment维持时间为15分钟
*/
private static final long SEGMENT_DURATION = 15 * 60 * 1000L;
private ExecutorService service = new ThreadPoolExecutor(5, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), new UpdateThreadFactory());
private volatile boolean initOK = false;
private Map<String, SegmentBuffer> cache = new ConcurrentHashMap<String, SegmentBuffer>();
private IDAllocDao dao;
public static class UpdateThreadFactory implements ThreadFactory {
private static int threadInitNumber = 0;
private static synchronized int nextThreadNum() {
return threadInitNumber++;
}
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "Thread-Segment-Update-" + nextThreadNum());
}
}
@Override
public boolean init() {
logger.info("初始化 ...");
// 确保加载到kv后才初始化成功
updateCacheFromDb();
initOK = true;
updateCacheFromDbAtEveryMinute();
return initOK;
}
private void updateCacheFromDbAtEveryMinute() {
ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setName("check-idCache-thread");
t.setDaemon(true);
return t;
}
});
service.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
updateCacheFromDb();
}
}, 60, 60, TimeUnit.SECONDS);
}
private void updateCacheFromDb() {
logger.info("update cache from db");
StopWatch sw = new Slf4JStopWatch();
try {
List<String> dbTags = dao.getAllTags();
if (dbTags == null || dbTags.isEmpty()) {
return;
}
List<String> cacheTags = new ArrayList<String>(cache.keySet());
Set<String> insertTagsSet = new HashSet<>(dbTags);
Set<String> removeTagsSet = new HashSet<>(cacheTags);
//db中新加的tags灌进cache
for(int i = 0; i < cacheTags.size(); i++){
String tmp = cacheTags.get(i);
if(insertTagsSet.contains(tmp)){
insertTagsSet.remove(tmp);
}
}
for (String tag : insertTagsSet) {
SegmentBuffer buffer = new SegmentBuffer();
buffer.setKey(tag);
Segment segment = buffer.getCurrent();
segment.setValue(new AtomicLong(0));
segment.setMax(0);
segment.setStep(0);
cache.put(tag, buffer);
logger.info("Add tag {} from db to IdCache, SegmentBuffer {}", tag, buffer);
}
//cache中已失效的tags从cache删除
for(int i = 0; i < dbTags.size(); i++){
String tmp = dbTags.get(i);
if(removeTagsSet.contains(tmp)){
removeTagsSet.remove(tmp);
}
}
for (String tag : removeTagsSet) {
cache.remove(tag);
logger.info("Remove tag {} from IdCache", tag);
}
} catch (Exception e) {
logger.warn("update cache from db exception", e);
} finally {
sw.stop("updateCacheFromDb");
}
}
@Override
public Result get(final String key) {
if (!initOK) {
return new Result(EXCEPTION_ID_IDCACHE_INIT_FALSE, Status.EXCEPTION);
}
if (cache.containsKey(key)) {
SegmentBuffer buffer = cache.get(key);
if (!buffer.isInitOk()) {
synchronized (buffer) {
if (!buffer.isInitOk()) {
try {
updateSegmentFromDb(key, buffer.getCurrent());
logger.info("Init buffer. Update leafkey {} {} from db", key, buffer.getCurrent());
buffer.setInitOk(true);
} catch (Exception e) {
logger.warn("Init buffer {} exception", buffer.getCurrent(), e);
}
}
}
}
return getIdFromSegmentBuffer(cache.get(key));
}
return new Result(EXCEPTION_ID_KEY_NOT_EXISTS, Status.EXCEPTION);
}
public void updateSegmentFromDb(String key, Segment segment) {
StopWatch sw = new Slf4JStopWatch();
SegmentBuffer buffer = segment.getBuffer();
LeafAlloc leafAlloc;
if (!buffer.isInitOk()) {
leafAlloc = dao.updateMaxIdAndGetLeafAlloc(key);
buffer.setStep(leafAlloc.getStep());
buffer.setMinStep(leafAlloc.getStep());//leafAlloc中的step为DB中的step
} else if (buffer.getUpdateTimestamp() == 0) {
leafAlloc = dao.updateMaxIdAndGetLeafAlloc(key);
buffer.setUpdateTimestamp(System.currentTimeMillis());
buffer.setStep(leafAlloc.getStep());
buffer.setMinStep(leafAlloc.getStep());//leafAlloc中的step为DB中的step
} else {
long duration = System.currentTimeMillis() - buffer.getUpdateTimestamp();
int nextStep = buffer.getStep();
if (duration < SEGMENT_DURATION) {
if (nextStep * 2 > MAX_STEP) {
//do nothing
} else {
nextStep = nextStep * 2;
}
} else if (duration < SEGMENT_DURATION * 2) {
//do nothing with nextStep
} else {
nextStep = nextStep / 2 >= buffer.getMinStep() ? nextStep / 2 : nextStep;
}
logger.info("leafKey[{}], step[{}], duration[{}mins], nextStep[{}]", key, buffer.getStep(), String.format("%.2f",((double)duration / (1000 * 60))), nextStep);
LeafAlloc temp = new LeafAlloc();
temp.setKey(key);
temp.setStep(nextStep);
leafAlloc = dao.updateMaxIdByCustomStepAndGetLeafAlloc(temp);
buffer.setUpdateTimestamp(System.currentTimeMillis());
buffer.setStep(nextStep);
buffer.setMinStep(leafAlloc.getStep());//leafAlloc的step为DB中的step
}
// must set value before set max
long value = leafAlloc.getMaxId() - buffer.getStep();
segment.getValue().set(value);
segment.setMax(leafAlloc.getMaxId());
segment.setStep(buffer.getStep());
sw.stop("updateSegmentFromDb", key + " " + segment);
}
public Result getIdFromSegmentBuffer(final SegmentBuffer buffer) {
while (true) {
buffer.rLock().lock();
try {
final Segment segment = buffer.getCurrent();
/*当前号段已下发10%时,如果下一个号段未更新,则另启一个更新线程去更新下一个号段*/
if (!buffer.isNextReady() && (segment.getIdle() < 0.9 * segment.getStep()) && buffer.getThreadRunning().compareAndSet(false, true)) {
service.execute(new Runnable() {
@Override
public void run() {
Segment next = buffer.getSegments()[buffer.nextPos()];
boolean updateOk = false;
try {
updateSegmentFromDb(buffer.getKey(), next);
updateOk = true;
logger.info("update segment {} from db {}", buffer.getKey(), next);
} catch (Exception e) {
logger.warn(buffer.getKey() + " updateSegmentFromDb exception", e);
} finally {
if (updateOk) {
buffer.wLock().lock();
buffer.setNextReady(true);
buffer.getThreadRunning().set(false);
buffer.wLock().unlock();
} else {
buffer.getThreadRunning().set(false);
}
}
}
});
}
long value = segment.getValue().getAndIncrement();
if (value < segment.getMax()) {
return new Result(value, Status.SUCCESS);
}
} finally {
buffer.rLock().unlock();
}
waitAndSleep(buffer);
buffer.wLock().lock();
try {
final Segment segment = buffer.getCurrent();
long value = segment.getValue().getAndIncrement();
if (value < segment.getMax()) {
return new Result(value, Status.SUCCESS);
}
if (buffer.isNextReady()) {
buffer.switchPos();
buffer.setNextReady(false);
} else {
logger.error("Both two segments in {} are not ready!", buffer);
return new Result(EXCEPTION_ID_TWO_SEGMENTS_ARE_NULL, Status.EXCEPTION);
}
} finally {
buffer.wLock().unlock();
}
}
}
private void waitAndSleep(SegmentBuffer buffer) {
int roll = 0;
while (buffer.getThreadRunning().get()) {
roll += 1;
if(roll > 10000) {
try {
TimeUnit.MILLISECONDS.sleep(10);
break;
} catch (InterruptedException e) {
logger.warn("Thread {} Interrupted",Thread.currentThread().getName());
break;
}
}
}
}
public List<LeafAlloc> getAllLeafAllocs() {
return dao.getAllLeafAllocs();
}
public Map<String, SegmentBuffer> getCache() {
return cache;
}
public IDAllocDao getDao() {
return dao;
}
public void setDao(IDAllocDao dao) {
this.dao = dao;
}
}

View File

@@ -0,0 +1,12 @@
package com.tuling.leafcore.segment.dao;
import com.tuling.leafcore.segment.model.LeafAlloc;
import java.util.List;
public interface IDAllocDao {
List<LeafAlloc> getAllLeafAllocs();
LeafAlloc updateMaxIdAndGetLeafAlloc(String tag);
LeafAlloc updateMaxIdByCustomStepAndGetLeafAlloc(LeafAlloc leafAlloc);
List<String> getAllTags();
}

View File

@@ -0,0 +1,35 @@
package com.tuling.leafcore.segment.dao;
import com.tuling.leafcore.segment.model.LeafAlloc;
import org.apache.ibatis.annotations.*;
import java.util.List;
public interface IDAllocMapper {
@Select("SELECT biz_tag, max_id, step, update_time FROM leaf_alloc")
@Results(value = {
@Result(column = "biz_tag", property = "key"),
@Result(column = "max_id", property = "maxId"),
@Result(column = "step", property = "step"),
@Result(column = "update_time", property = "updateTime")
})
List<LeafAlloc> getAllLeafAllocs();
@Select("SELECT biz_tag, max_id, step FROM leaf_alloc WHERE biz_tag = #{tag}")
@Results(value = {
@Result(column = "biz_tag", property = "key"),
@Result(column = "max_id", property = "maxId"),
@Result(column = "step", property = "step")
})
LeafAlloc getLeafAlloc(@Param("tag") String tag);
@Update("UPDATE leaf_alloc SET max_id = max_id + step WHERE biz_tag = #{tag}")
void updateMaxId(@Param("tag") String tag);
@Update("UPDATE leaf_alloc SET max_id = max_id + #{step} WHERE biz_tag = #{key}")
void updateMaxIdByCustomStep(@Param("leafAlloc") LeafAlloc leafAlloc);
@Select("SELECT biz_tag FROM leaf_alloc")
List<String> getAllTags();
}

View File

@@ -0,0 +1,74 @@
package com.tuling.leafcore.segment.dao.impl;
import com.tuling.leafcore.segment.dao.IDAllocDao;
import com.tuling.leafcore.segment.dao.IDAllocMapper;
import com.tuling.leafcore.segment.model.LeafAlloc;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import javax.sql.DataSource;
import java.util.List;
public class IDAllocDaoImpl implements IDAllocDao {
SqlSessionFactory sqlSessionFactory;
public IDAllocDaoImpl(DataSource dataSource) {
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMapper(IDAllocMapper.class);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
}
@Override
public List<LeafAlloc> getAllLeafAllocs() {
SqlSession sqlSession = sqlSessionFactory.openSession(false);
try {
return sqlSession.selectList("com.tuling.leafcore.segment.dao.IDAllocMapper.getAllLeafAllocs");
} finally {
sqlSession.close();
}
}
@Override
public LeafAlloc updateMaxIdAndGetLeafAlloc(String tag) {
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
sqlSession.update("com.tuling.leafcore.segment.dao.IDAllocMapper.updateMaxId", tag);
LeafAlloc result = sqlSession.selectOne("com.tuling.leafcore.segment.dao.IDAllocMapper.getLeafAlloc", tag);
sqlSession.commit();
return result;
} finally {
sqlSession.close();
}
}
@Override
public LeafAlloc updateMaxIdByCustomStepAndGetLeafAlloc(LeafAlloc leafAlloc) {
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
sqlSession.update("com.tuling.leafcore.segment.dao.IDAllocMapper.updateMaxIdByCustomStep", leafAlloc);
LeafAlloc result = sqlSession.selectOne("com.tuling.leafcore.segment.dao.IDAllocMapper.getLeafAlloc", leafAlloc.getKey());
sqlSession.commit();
return result;
} finally {
sqlSession.close();
}
}
@Override
public List<String> getAllTags() {
SqlSession sqlSession = sqlSessionFactory.openSession(false);
try {
return sqlSession.selectList("com.tuling.leafcore.segment.dao.IDAllocMapper.getAllTags");
} finally {
sqlSession.close();
}
}
}

View File

@@ -0,0 +1,40 @@
package com.tuling.leafcore.segment.model;
public class LeafAlloc {
private String key;
private long maxId;
private int step;
private String updateTime;
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public long getMaxId() {
return maxId;
}
public void setMaxId(long maxId) {
this.maxId = maxId;
}
public int getStep() {
return step;
}
public void setStep(int step) {
this.step = step;
}
public String getUpdateTime() {
return updateTime;
}
public void setUpdateTime(String updateTime) {
this.updateTime = updateTime;
}
}

View File

@@ -0,0 +1,59 @@
package com.tuling.leafcore.segment.model;
import java.util.concurrent.atomic.AtomicLong;
public class Segment {
private AtomicLong value = new AtomicLong(0);
private volatile long max;
private volatile int step;
private SegmentBuffer buffer;
public Segment(SegmentBuffer buffer) {
this.buffer = buffer;
}
public AtomicLong getValue() {
return value;
}
public void setValue(AtomicLong value) {
this.value = value;
}
public long getMax() {
return max;
}
public void setMax(long max) {
this.max = max;
}
public int getStep() {
return step;
}
public void setStep(int step) {
this.step = step;
}
public SegmentBuffer getBuffer() {
return buffer;
}
public long getIdle() {
return this.getMax() - getValue().get();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("Segment(");
sb.append("value:");
sb.append(value);
sb.append(",max:");
sb.append(max);
sb.append(",step:");
sb.append(step);
sb.append(")");
return sb.toString();
}
}

View File

@@ -0,0 +1,129 @@
package com.tuling.leafcore.segment.model;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* 双buffer
*/
public class SegmentBuffer {
private String key;
private Segment[] segments; //双buffer
private volatile int currentPos; //当前的使用的segment的index
private volatile boolean nextReady; //下一个segment是否处于可切换状态
private volatile boolean initOk; //是否初始化完成
private final AtomicBoolean threadRunning; //线程是否在运行中
private final ReadWriteLock lock;
private volatile int step;
private volatile int minStep;
private volatile long updateTimestamp;
public SegmentBuffer() {
segments = new Segment[]{new Segment(this), new Segment(this)};
currentPos = 0;
nextReady = false;
initOk = false;
threadRunning = new AtomicBoolean(false);
lock = new ReentrantReadWriteLock();
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public Segment[] getSegments() {
return segments;
}
public Segment getCurrent() {
return segments[currentPos];
}
public int getCurrentPos() {
return currentPos;
}
public int nextPos() {
return (currentPos + 1) % 2;
}
public void switchPos() {
currentPos = nextPos();
}
public boolean isInitOk() {
return initOk;
}
public void setInitOk(boolean initOk) {
this.initOk = initOk;
}
public boolean isNextReady() {
return nextReady;
}
public void setNextReady(boolean nextReady) {
this.nextReady = nextReady;
}
public AtomicBoolean getThreadRunning() {
return threadRunning;
}
public Lock rLock() {
return lock.readLock();
}
public Lock wLock() {
return lock.writeLock();
}
public int getStep() {
return step;
}
public void setStep(int step) {
this.step = step;
}
public int getMinStep() {
return minStep;
}
public void setMinStep(int minStep) {
this.minStep = minStep;
}
public long getUpdateTimestamp() {
return updateTimestamp;
}
public void setUpdateTimestamp(long updateTimestamp) {
this.updateTimestamp = updateTimestamp;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("SegmentBuffer{");
sb.append("key='").append(key).append('\'');
sb.append(", segments=").append(Arrays.toString(segments));
sb.append(", currentPos=").append(currentPos);
sb.append(", nextReady=").append(nextReady);
sb.append(", initOk=").append(initOk);
sb.append(", threadRunning=").append(threadRunning);
sb.append(", step=").append(step);
sb.append(", minStep=").append(minStep);
sb.append(", updateTimestamp=").append(updateTimestamp);
sb.append('}');
return sb.toString();
}
}

View File

@@ -0,0 +1,9 @@
package com.tuling.tulingmall;
public class Constants {
public static final String LEAF_SEGMENT_ENABLE = "leaf.segment.enable";
public static final String LEAF_JDBC_URL = "leaf.jdbc.url";
public static final String LEAF_JDBC_USERNAME = "leaf.jdbc.username";
public static final String LEAF_JDBC_PASSWORD = "leaf.jdbc.password";
}

View File

@@ -0,0 +1,23 @@
package com.tuling.tulingmall.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
@Configuration
public class SystemConf {
private Logger logger = LoggerFactory.getLogger(SystemConf.class);
@Value("${spring.cloud.nacos.discovery.server-addr}")
private String nacosDiscoveryAddr;
@PostConstruct
public void showConf(){
logger.info("启动配置 nacosDiscoveryAddr:[{}]",nacosDiscoveryAddr);
}
}

View File

@@ -0,0 +1,66 @@
package com.tuling.tulingmall.controller;
import com.tuling.leafcore.common.Result;
import com.tuling.leafcore.common.Status;
import com.tuling.tulingmall.exception.LeafServerException;
import com.tuling.tulingmall.exception.NoKeyException;
import com.tuling.tulingmall.service.SegmentService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@RestController
public class LeafController {
private Logger logger = LoggerFactory.getLogger(LeafController.class);
@Autowired
private SegmentService segmentService;
@RequestMapping(value = "/api/segment/get/{key}")
public String getSegmentId(@PathVariable("key") String key) {
return get(key, segmentService.getId(key));
}
@RequestMapping(value = "/api/segment/getlist/{key}")
public List<String> getSegmentIdList(@PathVariable("key") String key,@RequestParam int keyNumber) {
if (keyNumber == 0 || keyNumber > 5000) keyNumber = 5000;
return getList(key, segmentService.getIds(key,keyNumber));
}
private String get(@PathVariable("key") String key, Result id) {
Result result;
if (key == null || key.isEmpty()) {
throw new NoKeyException();
}
result = id;
if (result.getStatus().equals(Status.EXCEPTION)) {
throw new LeafServerException(result.toString());
}
return String.valueOf(result.getId());
}
private List<String> getList(@PathVariable("key") String key, List<Result> idList) {
if (key == null || key.isEmpty()) {
throw new NoKeyException();
}
if(idList.isEmpty()){
throw new LeafServerException("获得id列表失败");
}
List<String> result = new ArrayList<>();
for(Result id : idList){
if (id.getStatus().equals(Status.EXCEPTION)) {
logger.error("获得id异常",new LeafServerException(id.toString()));
}else{
result.add(String.valueOf(id.getId()));
}
}
return result;
}
}

View File

@@ -0,0 +1,72 @@
package com.tuling.tulingmall.controller;
import com.tuling.leafcore.segment.SegmentIDGenImpl;
import com.tuling.leafcore.segment.model.LeafAlloc;
import com.tuling.leafcore.segment.model.SegmentBuffer;
import com.tuling.tulingmall.model.SegmentBufferView;
import com.tuling.tulingmall.service.SegmentService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
public class LeafMonitorController {
private Logger logger = LoggerFactory.getLogger(LeafMonitorController.class);
@Autowired
private SegmentService segmentService;
@RequestMapping(value = "cache")
public Map<String, SegmentBufferView> getCache() {
Map<String, SegmentBufferView> data = new HashMap<>();
SegmentIDGenImpl segmentIDGen = segmentService.getIdGen();
if (segmentIDGen == null) {
throw new IllegalArgumentException("You should config leaf.segment.enable=true first");
}
Map<String, SegmentBuffer> cache = segmentIDGen.getCache();
for (Map.Entry<String, SegmentBuffer> entry : cache.entrySet()) {
SegmentBufferView sv = new SegmentBufferView();
SegmentBuffer buffer = entry.getValue();
sv.setInitOk(buffer.isInitOk());
sv.setKey(buffer.getKey());
sv.setPos(buffer.getCurrentPos());
sv.setNextReady(buffer.isNextReady());
sv.setMax0(buffer.getSegments()[0].getMax());
sv.setValue0(buffer.getSegments()[0].getValue().get());
sv.setStep0(buffer.getSegments()[0].getStep());
sv.setMax1(buffer.getSegments()[1].getMax());
sv.setValue1(buffer.getSegments()[1].getValue().get());
sv.setStep1(buffer.getSegments()[1].getStep());
data.put(entry.getKey(), sv);
}
logger.info("Cache info {}", data);
//model.addAttribute("data", data);
return data;
}
@RequestMapping(value = "db")
public List<LeafAlloc> getDb() {
SegmentIDGenImpl segmentIDGen = segmentService.getIdGen();
if (segmentIDGen == null) {
throw new IllegalArgumentException("You should config leaf.segment.enable=true first");
}
List<LeafAlloc> items = segmentIDGen.getAllLeafAllocs();
logger.info("DB info {}", items);
return items;
}
}

View File

@@ -0,0 +1,7 @@
package com.tuling.tulingmall.exception;
public class InitException extends Exception{
public InitException(String msg) {
super(msg);
}
}

View File

@@ -0,0 +1,11 @@
package com.tuling.tulingmall.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(code=HttpStatus.INTERNAL_SERVER_ERROR)
public class LeafServerException extends RuntimeException {
public LeafServerException(String msg) {
super(msg);
}
}

View File

@@ -0,0 +1,8 @@
package com.tuling.tulingmall.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(code=HttpStatus.INTERNAL_SERVER_ERROR,reason="Key is none")
public class NoKeyException extends RuntimeException {
}

View File

@@ -0,0 +1,95 @@
package com.tuling.tulingmall.model;
public class SegmentBufferView {
private String key;
private long value0;
private int step0;
private long max0;
private long value1;
private int step1;
private long max1;
private int pos;
private boolean nextReady;
private boolean initOk;
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public long getValue1() {
return value1;
}
public void setValue1(long value1) {
this.value1 = value1;
}
public int getStep1() {
return step1;
}
public void setStep1(int step1) {
this.step1 = step1;
}
public long getMax1() {
return max1;
}
public void setMax1(long max1) {
this.max1 = max1;
}
public long getValue0() {
return value0;
}
public void setValue0(long value0) {
this.value0 = value0;
}
public int getStep0() {
return step0;
}
public void setStep0(int step0) {
this.step0 = step0;
}
public long getMax0() {
return max0;
}
public void setMax0(long max0) {
this.max0 = max0;
}
public int getPos() {
return pos;
}
public void setPos(int pos) {
this.pos = pos;
}
public boolean isNextReady() {
return nextReady;
}
public void setNextReady(boolean nextReady) {
this.nextReady = nextReady;
}
public boolean isInitOk() {
return initOk;
}
public void setInitOk(boolean initOk) {
this.initOk = initOk;
}
}

View File

@@ -0,0 +1,79 @@
package com.tuling.tulingmall.service;
import com.alibaba.druid.pool.DruidDataSource;
import com.tuling.leafcore.IDGen;
import com.tuling.leafcore.common.PropertyFactory;
import com.tuling.leafcore.common.Result;
import com.tuling.leafcore.common.ZeroIDGen;
import com.tuling.leafcore.segment.SegmentIDGenImpl;
import com.tuling.leafcore.segment.dao.IDAllocDao;
import com.tuling.leafcore.segment.dao.impl.IDAllocDaoImpl;
import com.tuling.tulingmall.Constants;
import com.tuling.tulingmall.exception.InitException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
@Service("SegmentService")
public class SegmentService {
private Logger logger = LoggerFactory.getLogger(SegmentService.class);
private IDGen idGen;
private DruidDataSource dataSource;
public SegmentService() throws SQLException, InitException {
Properties properties = PropertyFactory.getProperties();
boolean flag = Boolean.parseBoolean(properties.getProperty(Constants.LEAF_SEGMENT_ENABLE, "true"));
if (flag) {
// Config dataSource
dataSource = new DruidDataSource();
dataSource.setUrl(properties.getProperty(Constants.LEAF_JDBC_URL));
dataSource.setUsername(properties.getProperty(Constants.LEAF_JDBC_USERNAME));
dataSource.setPassword(properties.getProperty(Constants.LEAF_JDBC_PASSWORD));
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setValidationQuery("select 1");
dataSource.init();
logger.info("leaf.properties配置{}",properties);
// Config Dao
IDAllocDao dao = new IDAllocDaoImpl(dataSource);
// Config ID Gen
idGen = new SegmentIDGenImpl();
((SegmentIDGenImpl) idGen).setDao(dao);
if (idGen.init()) {
logger.info("Segment Service Init Successfully");
} else {
throw new InitException("Segment Service Init Fail");
}
} else {
idGen = new ZeroIDGen();
logger.info("Zero ID Gen Service Init Successfully");
}
}
public Result getId(String key) {
return idGen.get(key);
}
public List<Result> getIds(String key,int keyNumber) {
List<Result> results = new ArrayList<>();
for (int i = 1;i <= keyNumber; i++){
results.add(idGen.get(key));
}
return results;
}
public SegmentIDGenImpl getIdGen() {
if (idGen instanceof SegmentIDGenImpl) {
return (SegmentIDGenImpl) idGen;
}
return null;
}
}

View File

@@ -0,0 +1,17 @@
package com.tuling.tulingmall.service;
import java.util.UUID;
public class UUIDService {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
String rawUUID = UUID.randomUUID().toString();
System.out.println(rawUUID);
//去除"-"
String uuid = rawUUID.replaceAll("-", "");
System.out.println(uuid);
}
}
}

View File

@@ -0,0 +1,48 @@
package com.tuling.tulingmall.service.snowflake;
public class BitDemo {
public static void main(String[] args) {
//第一部分时间戳
Long a = System.currentTimeMillis();
System.out.println("当前时间:" + a);
String aa = Long.toBinaryString(a);
System.out.println(aa);
System.out.println("时间戳位数" + aa.length());
while (aa.length() < 64) {
aa = "0" + aa;
}
System.out.println("//时间戳------------------------------------//");
System.out.println(aa);
//第二部分机房区分
Long b = 5L;
String bb = Long.toBinaryString(b);
while (bb.length() < 64) {
bb = "0" + bb;
}
System.out.println("//数据标识------------------------------------//");
System.out.println(bb);
//机器区分
Long c = 6L;
String cc = Long.toBinaryString(c);
while (cc.length() < 64) {
cc = "0" + cc;
}
System.out.println("//机器标识------------------------------------//");
System.out.println(cc);
//第三部分递增数
Long d = 1L;
String dd = Long.toBinaryString(d);
while (dd.length() < 64) {
dd = "0" + dd;
}
System.out.println("//自增数------------------------------------//");
System.out.println(dd);
}
}

View File

@@ -0,0 +1,168 @@
package com.tuling.tulingmall.service.snowflake;
/**
* Twitter_Snowflake
* SnowFlake的结构如下(每部分用-分开):
* 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
* 1位标识由于long基本类型在Java中是带符号的最高位是符号位正数是0负数是1所以id一般是正数最高位是0
* 41位时间戳(毫秒级)注意41位时间戳不是存储当前时间的时间戳而是存储时间戳的差值当前时间戳 - 开始时间戳)
* 得到的值这里的的开始时间戳一般是我们的id生成器开始使用的时间由我们程序来指定的
* 如下面程序SnowflakeIdWorker类的startTime属性
* 41位的时间戳可以使用69年年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69
* 10位的数据机器位可以部署在1024个节点包括5位datacenterId和5位workerId
* 12位序列毫秒内的计数12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间戳)产生4096个ID序号
* 加起来刚好64位为一个Long型。
*/
public class SnowflakeIdWorker {
/**
* 机器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对应二进制 1111 1111 1111
*/
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;
//==============================Constructors=====================================
/**
* 构造函数
*
* @param workerId 工作ID (0~31)
* @param datacenterId 数据中心ID (0~31)
*/
public SnowflakeIdWorker(long workerId, long datacenterId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
}
if (datacenterId > maxDatacenterId || datacenterId < 0) {
throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
}
this.workerId = workerId;
this.datacenterId = datacenterId;
}
// ==============================Methods==========================================
/**
* 获得下一个ID (该方法是线程安全的)
*
* @return SnowflakeId
*/
public synchronized long nextId() {
long timestamp = System.currentTimeMillis();
//如果当前时间小于上一次ID生成的时间戳说明系统时钟回退过这个时候应当抛出异常
if (timestamp < lastTimestamp) {
throw new RuntimeException(
String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
}
//如果是同一时间生成的,则进行毫秒内序列
if (lastTimestamp == timestamp) {
//当sequence累加到4096也就是0001000000000000时和sequenceMask相与后sequence=0
sequence = (sequence + 1) & sequenceMask;
//毫秒内序列溢出
if (sequence == 0) {
//阻塞到下一个毫秒,获得新的时间戳
timestamp = tilNextMillis(lastTimestamp);
}
}
//时间戳改变,毫秒内序列重置
else {
sequence = 0L;
}
//上次生成ID的时间戳
lastTimestamp = timestamp;
//移位并通过或运算拼到一起组成64位的ID
return (timestamp << timestampLeftShift) // 时间戳左移22位
| (datacenterId << datacenterIdShift) //数据标识左移17位
| (workerId << workerIdShift) //机器id标识左移12位
| sequence;
}
/**
* 阻塞到下一个毫秒,直到获得新的时间戳
*
* @param lastTimestamp 上次生成ID的时间戳
* @return 当前时间戳
*/
protected long tilNextMillis(long lastTimestamp) {
long timestamp = System.currentTimeMillis();
while (timestamp <= lastTimestamp) {
timestamp = System.currentTimeMillis();
}
return timestamp;
}
/**
* 测试
*/
public static void main(String[] args) {
SnowflakeIdWorker idWorker = new SnowflakeIdWorker(1, 1);
for (int i = 0; i < 10; i++) {
long id = idWorker.nextId();
System.out.println(id);
//System.out.println(Long.toBinaryString(id));
}
}
}

View File

@@ -0,0 +1,28 @@
server:
port: 8833
spring:
application:
name: tulingmall-unqid
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
main:
allow-bean-definition-overriding: true
#feign:
# client:
# config:
# default:
# loggerLevel: full
# requestInterceptors:
# - com.tuling.tulingmall.feignapi.interceptor.HeaderInterceptor
# readTimeout: 3000
# connectTimeout: 3000
#logging:
# level:
# com:
# tuling:
# tulingmall:
# feignapi:
# ums:
# UmsMemberReceiveAddressFeignApi: debug

View File

@@ -0,0 +1,9 @@
leaf.name=com.tuling.tulingmall.unqid
leaf.segment.enable=true
leaf.jdbc.url=jdbc:mysql://127.0.0.1:3306/leaf?serverTimezone=GMT%2b8&autoReconnect=true&useUnicode=true&characterEncoding=utf-8&autocommit=false&useSSL=false
leaf.jdbc.username=root
leaf.jdbc.password=123456
leaf.snowflake.enable=false
#leaf.snowflake.zk.address=
#leaf.snowflake.port=

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
This is the JRebel configuration file. It maps the running application to your IDE workspace, enabling JRebel reloading for this project.
Refer to https://manuals.jrebel.com/jrebel/standalone/config.html for more information.
-->
<application generated-by="intellij" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.zeroturnaround.com" xsi:schemaLocation="http://www.zeroturnaround.com http://update.zeroturnaround.com/jrebel/rebel-2_3.xsd">
<id>tulingmall-unqid</id>
<classpath>
<dir name="D:/GitSource/tmallV5/tulingmall-unqid/target/classes">
</dir>
</classpath>
</application>