commit 28d4d40b0cec220f88e4c65e288f7f1f6ebc76ed
Author: review512jwy@163.com <“review512jwy@163.com”>
Date: Fri Oct 31 11:22:38 2025 +0800
同步代码
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..04115ad
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,37 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+logs
+
+### VS Code ###
+.vscode/
+
+### MAC ###
+.DS_Store
\ No newline at end of file
diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java
new file mode 100644
index 0000000..a45eb6b
--- /dev/null
+++ b/.mvn/wrapper/MavenWrapperDownloader.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2007-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.net.*;
+import java.io.*;
+import java.nio.channels.*;
+import java.util.Properties;
+
+public class MavenWrapperDownloader {
+
+ private static final String WRAPPER_VERSION = "0.5.6";
+ /**
+ * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
+ */
+ private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
+ + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
+
+ /**
+ * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
+ * use instead of the default one.
+ */
+ private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
+ ".mvn/wrapper/maven-wrapper.properties";
+
+ /**
+ * Path where the maven-wrapper.jar will be saved to.
+ */
+ private static final String MAVEN_WRAPPER_JAR_PATH =
+ ".mvn/wrapper/maven-wrapper.jar";
+
+ /**
+ * Name of the property which should be used to override the default download url for the wrapper.
+ */
+ private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
+
+ public static void main(String args[]) {
+ System.out.println("- Downloader started");
+ File baseDirectory = new File(args[0]);
+ System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
+
+ // If the maven-wrapper.properties exists, read it and check if it contains a custom
+ // wrapperUrl parameter.
+ File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
+ String url = DEFAULT_DOWNLOAD_URL;
+ if (mavenWrapperPropertyFile.exists()) {
+ FileInputStream mavenWrapperPropertyFileInputStream = null;
+ try {
+ mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
+ Properties mavenWrapperProperties = new Properties();
+ mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
+ url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
+ } catch (IOException e) {
+ System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
+ } finally {
+ try {
+ if (mavenWrapperPropertyFileInputStream != null) {
+ mavenWrapperPropertyFileInputStream.close();
+ }
+ } catch (IOException e) {
+ // Ignore ...
+ }
+ }
+ }
+ System.out.println("- Downloading from: " + url);
+
+ File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
+ if (!outputFile.getParentFile().exists()) {
+ if (!outputFile.getParentFile().mkdirs()) {
+ System.out.println(
+ "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
+ }
+ }
+ System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
+ try {
+ downloadFileFromURL(url, outputFile);
+ System.out.println("Done");
+ System.exit(0);
+ } catch (Throwable e) {
+ System.out.println("- Error downloading");
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+ private static void downloadFileFromURL(String urlString, File destination) throws Exception {
+ if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
+ String username = System.getenv("MVNW_USERNAME");
+ char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
+ Authenticator.setDefault(new Authenticator() {
+ @Override
+ protected PasswordAuthentication getPasswordAuthentication() {
+ return new PasswordAuthentication(username, password);
+ }
+ });
+ }
+ URL website = new URL(urlString);
+ ReadableByteChannel rbc;
+ rbc = Channels.newChannel(website.openStream());
+ FileOutputStream fos = new FileOutputStream(destination);
+ fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
+ fos.close();
+ rbc.close();
+ }
+
+}
diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar
new file mode 100644
index 0000000..2cc7d4a
Binary files /dev/null and b/.mvn/wrapper/maven-wrapper.jar differ
diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 0000000..ffdc10e
--- /dev/null
+++ b/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1,2 @@
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.1/apache-maven-3.8.1-bin.zip
+wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..0d216e7
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,11 @@
+FROM registry.ap-northeast-1.aliyuncs.com/southwave/jdk17-template:latest
+WORKDIR /app
+COPY target/data-center-sender.jar app.jar
+EXPOSE 8201
+
+
+# 使用UseCGroupMemoryLimitForHeap
+# ENV JAVA_OPTS="-Xms1g -Xmx2g -XX:+UseParallelGC -XX:ParallelGCThreads=2 -XX:MaxGCPauseMillis=200 -XX:GCTimeRatio=19 -XX:NewRatio=3 -XX:+AlwaysPreTouch -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/app/gc.log"
+# ENV JAVA_OPTS="-XX:+UnlockExperimentalVMOptions -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:G1NewSizePercent=20 -XX:G1MaxNewSizePercent=60 -XX:InitialHeapSize=2g -Xmx4096m -XX:MetaspaceSize=256m -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/app/gc.log"
+# 使用shell方式的ENTRYPOINT来确保环境变量被展开
+ENTRYPOINT java $JAVA_OPTS -jar app.jar --spring-profiles=$env
\ No newline at end of file
diff --git a/Documents/传感器资料/NBI API 资料/NBI SaaSプラットフォーム API説明書-ver 2.1.docx b/Documents/传感器资料/NBI API 资料/NBI SaaSプラットフォーム API説明書-ver 2.1.docx
new file mode 100644
index 0000000..84fc000
Binary files /dev/null and b/Documents/传感器资料/NBI API 资料/NBI SaaSプラットフォーム API説明書-ver 2.1.docx differ
diff --git a/Documents/传感器资料/NBI API 资料/NBI SaaSプラットフォーム API説明書-ver 2.1.pdf b/Documents/传感器资料/NBI API 资料/NBI SaaSプラットフォーム API説明書-ver 2.1.pdf
new file mode 100644
index 0000000..f5afcd9
Binary files /dev/null and b/Documents/传感器资料/NBI API 资料/NBI SaaSプラットフォーム API説明書-ver 2.1.pdf differ
diff --git a/Documents/传感器资料/NBI API 资料/NBIInterfaceTest.zip b/Documents/传感器资料/NBI API 资料/NBIInterfaceTest.zip
new file mode 100644
index 0000000..337470c
Binary files /dev/null and b/Documents/传感器资料/NBI API 资料/NBIInterfaceTest.zip differ
diff --git a/Documents/传感器资料/NBI API 资料/NBI字段匹配表.xlsx b/Documents/传感器资料/NBI API 资料/NBI字段匹配表.xlsx
new file mode 100644
index 0000000..a0cbd62
Binary files /dev/null and b/Documents/传感器资料/NBI API 资料/NBI字段匹配表.xlsx differ
diff --git a/Documents/传感器资料/NBI API 资料/NBI数据解析文档.docx b/Documents/传感器资料/NBI API 资料/NBI数据解析文档.docx
new file mode 100644
index 0000000..6e7efc9
Binary files /dev/null and b/Documents/传感器资料/NBI API 资料/NBI数据解析文档.docx differ
diff --git a/Documents/传感器资料/NBI API 资料/NBI数据解析文档.pdf b/Documents/传感器资料/NBI API 资料/NBI数据解析文档.pdf
new file mode 100644
index 0000000..9b920c6
Binary files /dev/null and b/Documents/传感器资料/NBI API 资料/NBI数据解析文档.pdf differ
diff --git a/Documents/传感器资料/ZETA传感器/41-干接点检测器-OCZ1ZT.docx b/Documents/传感器资料/ZETA传感器/41-干接点检测器-OCZ1ZT.docx
new file mode 100644
index 0000000..959f491
Binary files /dev/null and b/Documents/传感器资料/ZETA传感器/41-干接点检测器-OCZ1ZT.docx differ
diff --git a/Documents/传感器资料/ZETA传感器/57-多路干接点检测器-OC16ZT.docx b/Documents/传感器资料/ZETA传感器/57-多路干接点检测器-OC16ZT.docx
new file mode 100644
index 0000000..0261a9c
Binary files /dev/null and b/Documents/传感器资料/ZETA传感器/57-多路干接点检测器-OC16ZT.docx differ
diff --git a/Documents/传感器资料/ZETA传感器/59-485采集终端-D485ZT.docx b/Documents/传感器资料/ZETA传感器/59-485采集终端-D485ZT.docx
new file mode 100644
index 0000000..4504670
Binary files /dev/null and b/Documents/传感器资料/ZETA传感器/59-485采集终端-D485ZT.docx differ
diff --git a/Documents/传感器资料/ZETA传感器/BLE gateway/ZETA®LPWAN_BLEZETAGW_产品规格_3.0版.docx b/Documents/传感器资料/ZETA传感器/BLE gateway/ZETA®LPWAN_BLEZETAGW_产品规格_3.0版.docx
new file mode 100644
index 0000000..ad26ab9
Binary files /dev/null and b/Documents/传感器资料/ZETA传感器/BLE gateway/ZETA®LPWAN_BLEZETAGW_产品规格_3.0版.docx differ
diff --git a/Documents/传感器资料/ZETA传感器/JAZE CO2+温湿度.pdf b/Documents/传感器资料/ZETA传感器/JAZE CO2+温湿度.pdf
new file mode 100644
index 0000000..3a0e00e
Binary files /dev/null and b/Documents/传感器资料/ZETA传感器/JAZE CO2+温湿度.pdf differ
diff --git a/Documents/传感器资料/ZETA传感器/JAZE 超声波距离传感器使用手册v1.1.docx b/Documents/传感器资料/ZETA传感器/JAZE 超声波距离传感器使用手册v1.1.docx
new file mode 100644
index 0000000..09a842e
Binary files /dev/null and b/Documents/传感器资料/ZETA传感器/JAZE 超声波距离传感器使用手册v1.1.docx differ
diff --git a/Documents/传感器资料/ZETA传感器/JAZE 超音波距離センサ_JZUS91A1_仕様書_V1.1_20210622.pdf b/Documents/传感器资料/ZETA传感器/JAZE 超音波距離センサ_JZUS91A1_仕様書_V1.1_20210622.pdf
new file mode 100644
index 0000000..437fda7
Binary files /dev/null and b/Documents/传感器资料/ZETA传感器/JAZE 超音波距離センサ_JZUS91A1_仕様書_V1.1_20210622.pdf differ
diff --git a/Documents/传感器资料/ZETA传感器/Oviphone健康手环/产品/BG77製品/5G NBIOT体温定位健康手环.docx b/Documents/传感器资料/ZETA传感器/Oviphone健康手环/产品/BG77製品/5G NBIOT体温定位健康手环.docx
new file mode 100644
index 0000000..e9fc67a
Binary files /dev/null and b/Documents/传感器资料/ZETA传感器/Oviphone健康手环/产品/BG77製品/5G NBIOT体温定位健康手环.docx differ
diff --git a/Documents/传感器资料/ZETA传感器/Oviphone健康手环/产品/BG77製品/B2316 CAT-M & NBIOT Smart Band.docx b/Documents/传感器资料/ZETA传感器/Oviphone健康手环/产品/BG77製品/B2316 CAT-M & NBIOT Smart Band.docx
new file mode 100644
index 0000000..a3ef72f
Binary files /dev/null and b/Documents/传感器资料/ZETA传感器/Oviphone健康手环/产品/BG77製品/B2316 CAT-M & NBIOT Smart Band.docx differ
diff --git a/Documents/传感器资料/ZETA传感器/Oviphone健康手环/产品/BG77製品/B2316 CAT-M, NBIOT体温监测健康定位手环.docx b/Documents/传感器资料/ZETA传感器/Oviphone健康手环/产品/BG77製品/B2316 CAT-M, NBIOT体温监测健康定位手环.docx
new file mode 100644
index 0000000..0e6aa69
Binary files /dev/null and b/Documents/传感器资料/ZETA传感器/Oviphone健康手环/产品/BG77製品/B2316 CAT-M, NBIOT体温监测健康定位手环.docx differ
diff --git a/Documents/传感器资料/ZETA传感器/Oviphone健康手环/平台、数据格式/平台,APP和数据对接简介-20210127.docx b/Documents/传感器资料/ZETA传感器/Oviphone健康手环/平台、数据格式/平台,APP和数据对接简介-20210127.docx
new file mode 100644
index 0000000..e108fa5
Binary files /dev/null and b/Documents/传感器资料/ZETA传感器/Oviphone健康手环/平台、数据格式/平台,APP和数据对接简介-20210127.docx differ
diff --git a/Documents/传感器资料/ZETA传感器/Oviphone健康手环/平台、数据格式/欧孚通信设备通信协议json1127.docx b/Documents/传感器资料/ZETA传感器/Oviphone健康手环/平台、数据格式/欧孚通信设备通信协议json1127.docx
new file mode 100644
index 0000000..3b2498c
Binary files /dev/null and b/Documents/传感器资料/ZETA传感器/Oviphone健康手环/平台、数据格式/欧孚通信设备通信协议json1127.docx differ
diff --git a/Documents/传感器资料/ZETA传感器/skytech/SZC100(CO2+温湿度センサー)取扱い説明書_Rev00.pdf b/Documents/传感器资料/ZETA传感器/skytech/SZC100(CO2+温湿度センサー)取扱い説明書_Rev00.pdf
new file mode 100644
index 0000000..f2b2bd7
Binary files /dev/null and b/Documents/传感器资料/ZETA传感器/skytech/SZC100(CO2+温湿度センサー)取扱い説明書_Rev00.pdf differ
diff --git a/Documents/传感器资料/ZETA传感器/skytech/SZT200(高精度傾斜計)取扱い説明書_Rev02.pdf b/Documents/传感器资料/ZETA传感器/skytech/SZT200(高精度傾斜計)取扱い説明書_Rev02.pdf
new file mode 100644
index 0000000..44bab7e
Binary files /dev/null and b/Documents/传感器资料/ZETA传感器/skytech/SZT200(高精度傾斜計)取扱い説明書_Rev02.pdf differ
diff --git a/Documents/传感器资料/ZETA传感器/skytech/SZW100_センサーデータフォーマット_r05.pdf b/Documents/传感器资料/ZETA传感器/skytech/SZW100_センサーデータフォーマット_r05.pdf
new file mode 100644
index 0000000..b7f94e9
Binary files /dev/null and b/Documents/传感器资料/ZETA传感器/skytech/SZW100_センサーデータフォーマット_r05.pdf differ
diff --git a/Documents/传感器资料/设备匹配表/NESIC租户KTC项目设备列表_20210805.xlsx b/Documents/传感器资料/设备匹配表/NESIC租户KTC项目设备列表_20210805.xlsx
new file mode 100644
index 0000000..4be10e0
Binary files /dev/null and b/Documents/传感器资料/设备匹配表/NESIC租户KTC项目设备列表_20210805.xlsx differ
diff --git a/TECHSOR_OFFICIAL.pem b/TECHSOR_OFFICIAL.pem
new file mode 100644
index 0000000..d9e9076
--- /dev/null
+++ b/TECHSOR_OFFICIAL.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAoXT+WI0CFw06NjKINFhz5y9xs9CeQYHN0+G24fIePPEBPRhW
+Faxg36GuFuYKxk59W+Ey8w5Stz8R1wckbU//uaio/P7QhNbl+OpVIKbU93SVW8x6
+VH8roTSf+uYh6k6qO5ejAjqiicuEj8dZ8fQWyYSB4X/mLrXg5k5dxKyjhv5nM8RO
+vvFHy/uvcdFOAeBbYrq9pHOkSP3BLU+wjutBDmxEdM9YLJk9qOM9bulxr1+QMXNY
+4pjpWDlqfM1BpZZDhKzvUqLVjO+QwQ2E8mUgoK2PVY1umC5X8Jlcfy83A77+pKzl
+ZKYpLgfLHj7rV5I12uoBx4mvLO1jw6XrNlIb9QIDAQABAoIBAGe+pDxEBwbG4hO3
+LpvsBjWT38y6DSZsgNRX4cqXZ+Siu7gFLjNo+ypXWmSuVlgMUTK7pqBVIMNMjGsN
+1NNEpz4l6Mf/9/6Tk1v5Ps/nQ0rqJ5q/7g7jVCaWiQGP5FUJTQtTqVOiV5SRKFG2
+t83nmMjOEyLRqxdymNuDmW7pu33ebtDZxwm+QeN3nz6TwAbV7Geas5NC1UJLR0+C
+nLQh0M9QELv7fKUPiznr6wE9CxB+1fx9es6HJxYK6Q4W+4mjd8CVXXMPDiQhTVEt
+jnibk3UfxBQB7fkYP8PUukGq+Z4BgCszoAW0J8gFwiC8YMuzzQw3k83vPCiZZ+JQ
+u0hgUPUCgYEA1lQi4mhKNtYMfbVtKEURajvUy5pZw4rzIyo+6XFjE25c3Q/nt3ot
+d+vgh6dNCfRll95CB2RKOUoelj3nDeX+aM+QEwwQghLAtAdcRcIhG3DfIYBOJmiw
+ugjbk5AW1bc38eQsjtRD7aZMyJjjr2bI7D2D5/MrjqdMZ6sxYdFRmpMCgYEAwNlB
+h/Da5kY/z9qcXSnysr78ffU5BLGcxN2At+aXYGKyoR2YQqSME0y1jC9h0w+a7nAx
+jCtkz+ozhBkP7o0vRZ8eGbP/65CbPrsY8P9Kpvk7dr2IRMTUjMFihhptGQFrLP3a
+g+T2T+gQWS0v5HqiSot6znHZL+jND9elwx8nnFcCgYEAuemhmOL9/TMPArwtQ5El
+2hCsNTBeTNBqt0Yd7ED+wAwrYVY6mVzRtARXb1Qf71KgDWwtulu0Rp2Uip6Hnfaz
+CBeD0gHVD/9USNVZpOkP7s2pv1WcdJS7N6QXU5jZNekIDjruq7ZUdgCa+iYk2jE+
+eC2kDb9RORzFmedVnpQDRSECgYBH26xTXyfxzhNQ/ABvpoXMnOWweYN5gEUOBgtE
+eyPEwoIVDtYBXxbiyh6L0cv9vT7Zwex0cmbqIjZ37m7FUM5gft3UbgHaYNO4GDc+
+9aF3fj7uC8mO9ljM6fIwTgCA5MpuxVh69QHi3HHbCL9jv15hsH9eFYX8GB7w3EXj
+4uP7mQKBgQCFG7l/s1VDsLn9VNpkoUBjZMMdrLCyCWVrTEdeYtZ5LIx3etZxgbah
+/rvryIDgc/j7riQgEDnqYk19Ee/HVxK1duJO6d/ywDcSlnNMaChrS8khsMrbK6yI
+geqH+9jaaPUVacfeVe0MCIGLxnMiUucIUIyp3VV2OuJ2xx68xqw1wA==
+-----END RSA PRIVATE KEY-----
\ No newline at end of file
diff --git a/cmd b/cmd
new file mode 100644
index 0000000..a5abb79
--- /dev/null
+++ b/cmd
@@ -0,0 +1 @@
+docker run -d --name test-sender -e redisTimeout=30000 -e redisMaxActive=7000 -e iotcorePort=8883 -e secretkey=Plkid7RDnHc1gGbp2yAv/Scc+ukI0q8vzBuyEBN2 -e businessQueryPushInfoUrl=https://iothub-web-stg.ttkdatatechbuild.com/api/targetConfig/config/v1/queryAlertForwardConfigByAlarmTmplIds -e alarmEmailResultUrl=https://iothub-web-stg.ttkdatatechbuild.com/api/common/logEmailSentResult -e dynamicJdbcUrl=jdbc:mysql://rm-bp11k2zm2fr7864428o.mysql.rds.aliyuncs.com/%s -e defaultRedisCacheTTL=5000 -e JAVA_OPTS="-XX:+UseZGC -Xms1g -Xmx2g -XX:MetaspaceSize=256m -Xlog:gc*:file=/app/gc.log:time,level,tags" -e iotcoreClientId=tkbuild-demo -e jdbcUsername=zhc -e redisShutdownTimeout=30000 -e roidAuthorization="Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9..." -e apihost=https://api-sec.test-public-api.kanri-roid.app -e messageProtocol=kinesis -e redisLockExpire=500 -e logLevel=DEBUG -e awsaccesskey=AKIA5OFH5OOZHM3U3KX4 -e dataCenterSenderTargetUrl=http://43.163.243.201:8030/nesic/deviceId/ -e queryPushInfoUrl=https://iothub-web-stg.ttkdatatechbuild.com/api/targetConfig/config/v2/queryAlertForwardConfigByDeviceId -e redisMinIdle=0 -e iotcoreEndpoint=iotcore-mqtts-stg.ttkdatatechbuild.com -e redisMaxIdle=7000 -e redisHost=r-uf63x4g5p6ir5xao87pd.redis.rds.aliyuncs.com -e jdbcPassword=Youqu48bnb1 -e roid2Url="/api/public/v1/targets/{targetId}/monitoring-status" -e env=dev -e mqttNormalTopic=kinesis-to-lambda-stg -e redisPassword=B2BGn4gK4htgkEwP -e redisDatabase=0 -e redisMaxWait=10000 -e jdbcUrl=jdbc:mysql://rm-bp11k2zm2fr7864428o.mysql.rds.aliyuncs.com/data_center_aeon_admin -e roidAlarmCancelUrl="/api/public/v1/problem-reports/return-to-normal" -e redisPort=6379 -e roidAlarmUrl="/api/public/v1/problem-reports/alarm" -e roid2Authorization="Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9..." -e roidBaStatusUrl="/api/public/v1/targets/{targetId}/running-status" 923770123186.dkr.ecr.ap-northeast-1.amazonaws.com/tokyo-build-sender
\ No newline at end of file
diff --git a/deploy.bat b/deploy.bat
new file mode 100644
index 0000000..5f540fc
--- /dev/null
+++ b/deploy.bat
@@ -0,0 +1,2 @@
+scp -i ./TECHSOR_OFFICIAL.pem ./target/TECHSOR_dataCenter_sender-0.0.1-SNAPSHOT.jar root@8.209.255.206:~
+ssh -i ./TECHSOR_OFFICIAL.pem root@8.209.255.206
\ No newline at end of file
diff --git a/mvnw b/mvnw
new file mode 100644
index 0000000..a16b543
--- /dev/null
+++ b/mvnw
@@ -0,0 +1,310 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+# JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+# M2_HOME - location of maven2's installed home dir
+# MAVEN_OPTS - parameters passed to the Java VM when running Maven
+# e.g. to debug Maven itself, use
+# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+ if [ -f /etc/mavenrc ] ; then
+ . /etc/mavenrc
+ fi
+
+ if [ -f "$HOME/.mavenrc" ] ; then
+ . "$HOME/.mavenrc"
+ fi
+
+fi
+
+# OS specific support. $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+ CYGWIN*) cygwin=true ;;
+ MINGW*) mingw=true;;
+ Darwin*) darwin=true
+ # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+ # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+ if [ -z "$JAVA_HOME" ]; then
+ if [ -x "/usr/libexec/java_home" ]; then
+ export JAVA_HOME="`/usr/libexec/java_home`"
+ else
+ export JAVA_HOME="/Library/Java/Home"
+ fi
+ fi
+ ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+ if [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=`java-config --jre-home`
+ fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+ ## resolve links - $0 may be a link to maven's home
+ PRG="$0"
+
+ # need this for relative symlinks
+ while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG="`dirname "$PRG"`/$link"
+ fi
+ done
+
+ saveddir=`pwd`
+
+ M2_HOME=`dirname "$PRG"`/..
+
+ # make it fully qualified
+ M2_HOME=`cd "$M2_HOME" && pwd`
+
+ cd "$saveddir"
+ # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --unix "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME="`(cd "$M2_HOME"; pwd)`"
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ javaExecutable="`which javac`"
+ if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink=`which readlink`
+ if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+ if $darwin ; then
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+ else
+ javaExecutable="`readlink -f \"$javaExecutable\"`"
+ fi
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+ JAVA_HOME="$javaHome"
+ export JAVA_HOME
+ fi
+ fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+ if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="`which java`"
+ fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+ echo "Error: JAVA_HOME is not defined correctly." >&2
+ echo " We cannot execute $JAVACMD" >&2
+ exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+ echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+ if [ -z "$1" ]
+ then
+ echo "Path not specified to find_maven_basedir"
+ return 1
+ fi
+
+ basedir="$1"
+ wdir="$1"
+ while [ "$wdir" != '/' ] ; do
+ if [ -d "$wdir"/.mvn ] ; then
+ basedir=$wdir
+ break
+ fi
+ # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+ if [ -d "${wdir}" ]; then
+ wdir=`cd "$wdir/.."; pwd`
+ fi
+ # end of workaround
+ done
+ echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "$1" ]; then
+ echo "$(tr -s '\n' ' ' < "$1")"
+ fi
+}
+
+BASE_DIR=`find_maven_basedir "$(pwd)"`
+if [ -z "$BASE_DIR" ]; then
+ exit 1;
+fi
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found .mvn/wrapper/maven-wrapper.jar"
+ fi
+else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+ fi
+ if [ -n "$MVNW_REPOURL" ]; then
+ jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ else
+ jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ fi
+ while IFS="=" read key value; do
+ case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
+ esac
+ done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Downloading from: $jarUrl"
+ fi
+ wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+ if $cygwin; then
+ wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
+ fi
+
+ if command -v wget > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found wget ... using wget"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ wget "$jarUrl" -O "$wrapperJarPath"
+ else
+ wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
+ fi
+ elif command -v curl > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found curl ... using curl"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ curl -o "$wrapperJarPath" "$jarUrl" -f
+ else
+ curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
+ fi
+
+ else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Falling back to using Java to download"
+ fi
+ javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+ # For Cygwin, switch paths to Windows format before running javac
+ if $cygwin; then
+ javaClass=`cygpath --path --windows "$javaClass"`
+ fi
+ if [ -e "$javaClass" ]; then
+ if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Compiling MavenWrapperDownloader.java ..."
+ fi
+ # Compiling the Java class
+ ("$JAVA_HOME/bin/javac" "$javaClass")
+ fi
+ if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ # Running the downloader
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Running MavenWrapperDownloader.java ..."
+ fi
+ ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+ fi
+ fi
+ fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+if [ "$MVNW_VERBOSE" = true ]; then
+ echo $MAVEN_PROJECTBASEDIR
+fi
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --path --windows "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+ [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+ MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/mvnw.cmd b/mvnw.cmd
new file mode 100644
index 0000000..c8d4337
--- /dev/null
+++ b/mvnw.cmd
@@ -0,0 +1,182 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM https://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
+if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+
+FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+ IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Found %WRAPPER_JAR%
+ )
+) else (
+ if not "%MVNW_REPOURL%" == "" (
+ SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ )
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Couldn't find %WRAPPER_JAR%, downloading it ...
+ echo Downloading from: %DOWNLOAD_URL%
+ )
+
+ powershell -Command "&{"^
+ "$webclient = new-object System.Net.WebClient;"^
+ "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+ "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+ "}"^
+ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
+ "}"
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Finished downloading %WRAPPER_JAR%
+ )
+)
+@REM End of extension
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
+if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%" == "on" pause
+
+if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
+
+exit /B %ERROR_CODE%
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..4da5b5f
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,533 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.2.12
+
+
+ com.techsor
+ TECHSOR_dataCenter_sender
+ 0.0.1-SNAPSHOT
+ TECHSOR_dataCenter_sender
+ Demo project for Spring Boot
+
+ 17
+ 381659385655.dkr.ecr.ap-northeast-1.amazonaws.com
+ 923770123186.dkr.ecr.ap-northeast-1.amazonaws.com
+ tokyo-build-sender
+
+
+
+
+ software.amazon.awssdk
+ bom
+ 2.20.113
+ pom
+ import
+
+
+ software.amazon.awssdk
+ services
+ 2.20.113
+
+
+ software.amazon.kinesis
+ amazon-kinesis-client-pom
+ 3.1.0
+
+
+ io.netty
+ netty-common
+ 4.2.2.Final
+
+
+ io.netty
+ netty-buffer
+ 4.2.2.Final
+
+
+ io.netty
+ netty-resolver
+ 4.2.2.Final
+
+
+ io.netty
+ netty-transport
+ 4.2.2.Final
+
+
+ io.netty
+ netty-transport-native-unix-common
+ 4.2.2.Final
+
+
+ io.netty
+ netty-codec-base
+ 4.2.2.Final
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-core
+
+
+
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-core
+ 10.1.42
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.springframework.boot
+ spring-boot-starter-data-redis
+ 2.5.4
+
+
+ org.springframework.boot
+ spring-boot-starter-integration
+
+
+
+ org.springframework.integration
+ spring-integration-redis
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+
+ com.lmax
+ disruptor
+ 3.3.6
+
+
+ com.mysql
+ mysql-connector-j
+ 9.3.0
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+
+ com.google.code.gson
+ gson
+ 2.13.1
+
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+
+
+
+
+ org.apache.commons
+ commons-text
+ 1.13.1
+
+
+
+
+ com.alibaba.fastjson2
+ fastjson2
+ 2.0.57
+
+
+
+ godzilla
+ godzilla
+ 1.6.0
+ system
+ ${project.basedir}/src/libs/godzilla-sdk-1.6.0.jar
+
+
+
+ org.slf4j
+ slf4j-api
+ 2.0.17
+
+
+ org.slf4j
+ jul-to-slf4j
+ 1.7.7
+
+
+ org.slf4j
+ jcl-over-slf4j
+ 1.7.7
+
+
+ org.slf4j
+ log4j-over-slf4j
+ 1.7.7
+
+
+
+ ch.qos.logback
+ logback-classic
+ 1.5.18
+ compile
+
+
+ ch.qos.logback
+ logback-core
+ 1.5.18
+ compile
+
+
+ org.java-websocket
+ Java-WebSocket
+ 1.5.0
+
+
+
+
+ com.squareup.okhttp3
+ okhttp
+ 4.12.0
+
+
+
+ org.apache.commons
+ commons-pool2
+ 2.11.1
+
+
+ org.mockito
+ mockito-core
+ 4.11.0
+ test
+
+
+ cn.hutool
+ hutool-all
+ 5.8.38
+
+
+
+ com.jayway.jsonpath
+ json-path
+ 2.9.0
+
+
+
+
+ com.amazonaws
+ aws-java-sdk-dynamodb
+ 1.12.701
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ org.mvel
+ mvel2
+ 2.4.12.Final
+
+
+ org.junit.vintage
+ junit-vintage-engine
+ 5.8.2
+ test
+
+
+ net.bytebuddy
+ byte-buddy
+ 1.8.16
+
+
+ net.bytebuddy
+ byte-buddy-agent
+ 1.8.16
+ test
+
+
+
+ com.google.guava
+ guava
+ 33.4.8-jre
+
+
+ org.eclipse.paho
+ org.eclipse.paho.client.mqttv3
+ 1.2.5
+
+
+
+ org.bouncycastle
+ bcprov-jdk18on
+ 1.81
+
+
+
+ commons-io
+ commons-io
+ 2.19.0
+
+
+
+
+
+
+
+
+
+
+
+ javax.mail
+ mail
+ 1.4.7
+
+
+
+ com.amazonaws
+ amazon-kinesis-producer
+ 0.15.10
+
+
+ org.apache.kafka
+ kafka-clients
+
+
+
+
+
+ org.apache.kafka
+ kafka-clients
+ 4.0.0
+
+
+ software.amazon.awssdk
+ kinesis
+
+
+
+
+ software.amazon.kinesis
+ amazon-kinesis-client
+ 3.0.3
+
+
+ com.amazonaws
+ aws-java-sdk-kinesis
+ 1.12.681
+
+
+
+ com.amazonaws
+ aws-java-sdk-core
+ 1.12.681
+
+
+
+
+ org.yaml
+ snakeyaml
+ 2.4
+
+
+
+
+ org.json
+ json
+ 20250517
+
+
+
+
+ net.bytebuddy
+ byte-buddy
+ 1.17.5
+
+
+
+
+ org.apache.logging.log4j
+ log4j-core
+ 2.24.3
+
+
+ org.apache.logging.log4j
+ log4j-api
+ 2.24.3
+
+
+
+
+ net.minidev
+ json-smart
+ 2.5.2
+
+
+
+
+ com.google.protobuf
+ protobuf-java
+ 4.31.1
+
+
+
+
+ org.apache.commons
+ commons-compress
+ 1.27.1
+
+
+
+
+ com.squareup.wire
+ wire-compiler
+ 5.2.1
+ runtime
+
+
+
+
+ com.squareup.wire
+ wire-schema
+ 5.2.1
+
+
+
+
+ org.apache.avro
+ avro
+ 1.12.0
+
+
+
+
+ io.netty
+ netty-handler
+ 4.2.2.Final
+
+
+
+
+ org.springframework
+ spring-context
+ 6.1.21
+
+
+
+
+ org.springframework
+ spring-context-support
+ 6.1.21
+
+
+
+
+ com.github.seratch
+ jslack
+ 3.4.2
+
+
+
+
+
+ data-center-sender
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ pl.project13.maven
+ git-commit-id-plugin
+ 4.9.10
+
+
+
+ revision
+
+
+
+
+ true
+ yyyy-MM-dd'T'HH:mm:ssZ
+ true
+ ${project.build.outputDirectory}/git.properties
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ io.fabric8
+ docker-maven-plugin
+ 0.38.1
+
+
+ AKIA5OFH5OOZPCXZIRUQ
+ TMIN27+OxamT1FmBQSVKfUIWpOVldhxQx2Stxwix
+
+
+
+ ${aws.ecr.registryTest}/${aws.ecr.repository}:latest
+ ${aws.ecr.registry}
+
+ ${project.basedir}/Dockerfile
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/readme.md b/readme.md
new file mode 100644
index 0000000..9bf2f5b
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,57 @@
+# Techsor数据转发系统接收部分
+# Version V0.2.1
+
+# DBM
+mqtt订阅已经完成
+
+# OviPhone
+测试接口:
+http://IP:8200/api/v1/oviphone/raw
+功能:接收数据并存储进restful_history.
+
+# RoadMap
+
++ 代码优化
++ 功能实现
++ 测试代码
+
+
+### docker 环境配置
+#### 环境启动问题
+aws上应该可以设置环境变量 `env`
+
+`env`=dev
+
+或者
+`env`=prd
+
+#### 接收服务器
+
+
+
+
+
+## ????
+```properties
+spring.datasource.url=${jdbcUrl} //jdbcUrl
+spring.datasource.username=${jdbcUsername} //jdbcUsername
+spring.datasource.password=${jdbcPassword} //jdbcPassword
+spring.datasource.driver-class-name=com.mysql.jdbc.Driver
+spring.datasource.hikari.driver-class-name=com.mysql.jdbc.Driver
+spring.datasource.hikari.schema=data_center
+
+spring.redis.host=${redisHost}
+spring.redis.password=${redisPassword}
+spring.redis.port=${redisPort}
+spring.redis.database=${redisDatabase}
+spring.redis.timeout=${redisTimeout}
+spring.redis.lettuce.pool.max-active=${redisMaxActive}
+spring.redis.lettuce.pool.min-idle=${redisMinIdle}
+spring.redis.lettuce.pool.max-idle=${redisMaxIdle}
+spring.redis.lettuce.pool.max-wait=${redisMaxWait}
+spring.redis.lettuce.shutdown-timeout=${redisShutdownTimeout}
+
+
+data.center.sender.url=${dataCenterSenderTargetUrl}
+```
+
diff --git a/shell-build.sh b/shell-build.sh
new file mode 100644
index 0000000..4e46985
--- /dev/null
+++ b/shell-build.sh
@@ -0,0 +1,3 @@
+git pull
+mvn clean
+mvn package -DskipTests=true docker:build
diff --git a/src/libs/godzilla-sdk-1.6.0.jar b/src/libs/godzilla-sdk-1.6.0.jar
new file mode 100644
index 0000000..2fdc73f
Binary files /dev/null and b/src/libs/godzilla-sdk-1.6.0.jar differ
diff --git a/src/main/docker/Dockerfile b/src/main/docker/Dockerfile
new file mode 100644
index 0000000..a9fb694
--- /dev/null
+++ b/src/main/docker/Dockerfile
@@ -0,0 +1,11 @@
+FROM openjdk:8u131-jdk
+WORKDIR /app
+COPY data-center-sender.jar app.jar
+EXPOSE 8201
+
+
+# 使用UseCGroupMemoryLimitForHeap
+ENV JAVA_OPTS="-Xms2g -Xmx2g -XX:+UseParallelGC -XX:ParallelGCThreads=2 -XX:MaxGCPauseMillis=200 -XX:GCTimeRatio=19 -XX:NewRatio=3 -XX:+AlwaysPreTouch -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/app/gc.log"
+
+# 使用shell方式的ENTRYPOINT来确保环境变量被展开
+ENTRYPOINT java $JAVA_OPTS -jar app.jar --spring-profiles=$env
\ No newline at end of file
diff --git a/src/main/java/com/baidu/fsg/uid/BitsAllocator.java b/src/main/java/com/baidu/fsg/uid/BitsAllocator.java
new file mode 100644
index 0000000..dc4327c
--- /dev/null
+++ b/src/main/java/com/baidu/fsg/uid/BitsAllocator.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2017 Baidu, Inc. All Rights Reserve.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.baidu.fsg.uid;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import org.springframework.util.Assert;
+
+/**
+ * Allocate 64 bits for the UID(long)
+ * sign (fixed 1bit) -> deltaSecond -> workerId -> sequence(within the same second)
+ *
+ * @author yutianbao
+ */
+public class BitsAllocator {
+ /**
+ * Total 64 bits
+ */
+ public static final int TOTAL_BITS = 1 << 6;
+
+ /**
+ * Bits for [sign-> second-> workId-> sequence]
+ */
+ private int signBits = 1;
+ private final int timestampBits;
+ private final int workerIdBits;
+ private final int sequenceBits;
+
+ /**
+ * Max value for workId & sequence
+ */
+ private final long maxDeltaSeconds;
+ private final long maxWorkerId;
+ private final long maxSequence;
+
+ /**
+ * Shift for timestamp & workerId
+ */
+ private final int timestampShift;
+ private final int workerIdShift;
+
+ /**
+ * Constructor with timestampBits, workerIdBits, sequenceBits
+ * The highest bit used for sign, so 63 bits for timestampBits, workerIdBits, sequenceBits
+ */
+ public BitsAllocator(int timestampBits, int workerIdBits, int sequenceBits) {
+ // make sure allocated 64 bits
+ int allocateTotalBits = signBits + timestampBits + workerIdBits + sequenceBits;
+ Assert.isTrue(allocateTotalBits == TOTAL_BITS, "allocate not enough 64 bits");
+
+ // initialize bits
+ this.timestampBits = timestampBits;
+ this.workerIdBits = workerIdBits;
+ this.sequenceBits = sequenceBits;
+
+ // initialize max value
+ this.maxDeltaSeconds = ~(-1L << timestampBits);
+ this.maxWorkerId = ~(-1L << workerIdBits);
+ this.maxSequence = ~(-1L << sequenceBits);
+
+ // initialize shift
+ this.timestampShift = workerIdBits + sequenceBits;
+ this.workerIdShift = sequenceBits;
+ }
+
+ /**
+ * Allocate bits for UID according to delta seconds & workerId & sequence
+ * Note that: The highest bit will always be 0 for sign
+ *
+ * @param deltaSeconds
+ * @param workerId
+ * @param sequence
+ * @return
+ */
+ public long allocate(long deltaSeconds, long workerId, long sequence) {
+ return (deltaSeconds << timestampShift) | (workerId << workerIdShift) | sequence;
+ }
+
+ /**
+ * Getters
+ */
+ public int getSignBits() {
+ return signBits;
+ }
+
+ public int getTimestampBits() {
+ return timestampBits;
+ }
+
+ public int getWorkerIdBits() {
+ return workerIdBits;
+ }
+
+ public int getSequenceBits() {
+ return sequenceBits;
+ }
+
+ public long getMaxDeltaSeconds() {
+ return maxDeltaSeconds;
+ }
+
+ public long getMaxWorkerId() {
+ return maxWorkerId;
+ }
+
+ public long getMaxSequence() {
+ return maxSequence;
+ }
+
+ public int getTimestampShift() {
+ return timestampShift;
+ }
+
+ public int getWorkerIdShift() {
+ return workerIdShift;
+ }
+
+ @Override
+ public String toString() {
+ return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/baidu/fsg/uid/UidGenerator.java b/src/main/java/com/baidu/fsg/uid/UidGenerator.java
new file mode 100644
index 0000000..a0fe2ea
--- /dev/null
+++ b/src/main/java/com/baidu/fsg/uid/UidGenerator.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2017 Baidu, Inc. All Rights Reserve.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.baidu.fsg.uid;
+
+
+import com.baidu.fsg.uid.exception.UidGenerateException;
+
+/**
+ * Represents a unique id generator.
+ *
+ * @author yutianbao
+ */
+public interface UidGenerator {
+
+ /**
+ * Get a unique ID
+ *
+ * @return UID
+ * @throws UidGenerateException
+ */
+ long getUID() throws UidGenerateException;
+
+ /**
+ * Parse the UID into elements which are used to generate the UID.
+ * Such as timestamp & workerId & sequence...
+ *
+ * @param uid
+ * @return Parsed info
+ */
+ String parseUID(long uid);
+
+}
diff --git a/src/main/java/com/baidu/fsg/uid/buffer/BufferPaddingExecutor.java b/src/main/java/com/baidu/fsg/uid/buffer/BufferPaddingExecutor.java
new file mode 100644
index 0000000..8632285
--- /dev/null
+++ b/src/main/java/com/baidu/fsg/uid/buffer/BufferPaddingExecutor.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2017 Baidu, Inc. All Rights Reserve.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.baidu.fsg.uid.buffer;
+
+
+import com.baidu.fsg.uid.utils.NamingThreadFactory;
+import com.baidu.fsg.uid.utils.PaddedAtomicLong;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.Assert;
+
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Represents an executor for padding {@link RingBuffer}
+ * There are two kinds of executors: one for scheduled padding, the other for padding immediately.
+ *
+ * @author yutianbao
+ */
+public class BufferPaddingExecutor {
+ private static final Logger LOGGER = LoggerFactory.getLogger(RingBuffer.class);
+
+ /** Constants */
+ private static final String WORKER_NAME = "RingBuffer-Padding-Worker";
+ private static final String SCHEDULE_NAME = "RingBuffer-Padding-Schedule";
+ private static final long DEFAULT_SCHEDULE_INTERVAL = 5 * 60L; // 5 minutes
+
+ /** Whether buffer padding is running */
+ private final AtomicBoolean running;
+
+ /** We can borrow UIDs from the future, here store the last second we have consumed */
+ private final PaddedAtomicLong lastSecond;
+
+ /** RingBuffer & BufferUidProvider */
+ private final RingBuffer ringBuffer;
+ private final BufferedUidProvider uidProvider;
+
+ /** Padding immediately by the thread pool */
+ private final ExecutorService bufferPadExecutors;
+ /** Padding schedule thread */
+ private final ScheduledExecutorService bufferPadSchedule;
+
+ /** Schedule interval Unit as seconds */
+ private long scheduleInterval = DEFAULT_SCHEDULE_INTERVAL;
+
+ /**
+ * Constructor with {@link RingBuffer} and {@link BufferedUidProvider}, default use schedule
+ *
+ * @param ringBuffer {@link RingBuffer}
+ * @param uidProvider {@link BufferedUidProvider}
+ */
+ public BufferPaddingExecutor(RingBuffer ringBuffer, BufferedUidProvider uidProvider) {
+ this(ringBuffer, uidProvider, true);
+ }
+
+ /**
+ * Constructor with {@link RingBuffer}, {@link BufferedUidProvider}, and whether use schedule padding
+ *
+ * @param ringBuffer {@link RingBuffer}
+ * @param uidProvider {@link BufferedUidProvider}
+ * @param usingSchedule
+ */
+ public BufferPaddingExecutor(RingBuffer ringBuffer, BufferedUidProvider uidProvider, boolean usingSchedule) {
+ this.running = new AtomicBoolean(false);
+ this.lastSecond = new PaddedAtomicLong(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()));
+ this.ringBuffer = ringBuffer;
+ this.uidProvider = uidProvider;
+
+ // initialize thread pool
+ int cores = Runtime.getRuntime().availableProcessors();
+ bufferPadExecutors = Executors.newFixedThreadPool(cores * 2, new NamingThreadFactory(WORKER_NAME));
+
+ // initialize schedule thread
+ if (usingSchedule) {
+ bufferPadSchedule = Executors.newSingleThreadScheduledExecutor(new NamingThreadFactory(SCHEDULE_NAME));
+ } else {
+ bufferPadSchedule = null;
+ }
+ }
+
+ /**
+ * Start executors such as schedule
+ */
+ public void start() {
+ if (bufferPadSchedule != null) {
+ bufferPadSchedule.scheduleWithFixedDelay(() -> paddingBuffer(), scheduleInterval, scheduleInterval, TimeUnit.SECONDS);
+ }
+ }
+
+ /**
+ * Shutdown executors
+ */
+ public void shutdown() {
+ if (!bufferPadExecutors.isShutdown()) {
+ bufferPadExecutors.shutdownNow();
+ }
+
+ if (bufferPadSchedule != null && !bufferPadSchedule.isShutdown()) {
+ bufferPadSchedule.shutdownNow();
+ }
+ }
+
+ /**
+ * Whether is padding
+ *
+ * @return
+ */
+ public boolean isRunning() {
+ return running.get();
+ }
+
+ /**
+ * Padding buffer in the thread pool
+ */
+ public void asyncPadding() {
+ bufferPadExecutors.submit(this::paddingBuffer);
+ }
+
+ /**
+ * Padding buffer fill the slots until to catch the cursor
+ */
+ public void paddingBuffer() {
+ LOGGER.info("Ready to padding buffer lastSecond:{}. {}", lastSecond.get(), ringBuffer);
+
+ // is still running
+ if (!running.compareAndSet(false, true)) {
+ LOGGER.info("Padding buffer is still running. {}", ringBuffer);
+ return;
+ }
+
+ // fill the rest slots until to catch the cursor
+ boolean isFullRingBuffer = false;
+ while (!isFullRingBuffer) {
+ List uidList = uidProvider.provide(lastSecond.incrementAndGet());
+ for (Long uid : uidList) {
+ isFullRingBuffer = !ringBuffer.put(uid);
+ if (isFullRingBuffer) {
+ break;
+ }
+ }
+ }
+
+ // not running now
+ running.compareAndSet(true, false);
+ LOGGER.info("End to padding buffer lastSecond:{}. {}", lastSecond.get(), ringBuffer);
+ }
+
+ /**
+ * Setters
+ */
+ public void setScheduleInterval(long scheduleInterval) {
+ Assert.isTrue(scheduleInterval > 0, "Schedule interval must positive!");
+ this.scheduleInterval = scheduleInterval;
+ }
+
+}
diff --git a/src/main/java/com/baidu/fsg/uid/buffer/BufferedUidProvider.java b/src/main/java/com/baidu/fsg/uid/buffer/BufferedUidProvider.java
new file mode 100644
index 0000000..6b332cf
--- /dev/null
+++ b/src/main/java/com/baidu/fsg/uid/buffer/BufferedUidProvider.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2017 Baidu, Inc. All Rights Reserve.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.baidu.fsg.uid.buffer;
+
+import java.util.List;
+
+/**
+ * Buffered UID provider(Lambda supported), which provides UID in the same one second
+ *
+ * @author yutianbao
+ */
+@FunctionalInterface
+public interface BufferedUidProvider {
+
+ /**
+ * Provides UID in one second
+ *
+ * @param momentInSecond
+ * @return
+ */
+ List provide(long momentInSecond);
+}
diff --git a/src/main/java/com/baidu/fsg/uid/buffer/RejectedPutBufferHandler.java b/src/main/java/com/baidu/fsg/uid/buffer/RejectedPutBufferHandler.java
new file mode 100644
index 0000000..c9f9d34
--- /dev/null
+++ b/src/main/java/com/baidu/fsg/uid/buffer/RejectedPutBufferHandler.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2017 Baidu, Inc. All Rights Reserve.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.baidu.fsg.uid.buffer;
+
+/**
+ * If tail catches the cursor it means that the ring buffer is full, any more buffer put request will be rejected.
+ * Specify the policy to handle the reject. This is a Lambda supported interface
+ *
+ * @author yutianbao
+ */
+@FunctionalInterface
+public interface RejectedPutBufferHandler {
+
+ /**
+ * Reject put buffer request
+ *
+ * @param ringBuffer
+ * @param uid
+ */
+ void rejectPutBuffer(RingBuffer ringBuffer, long uid);
+}
diff --git a/src/main/java/com/baidu/fsg/uid/buffer/RejectedTakeBufferHandler.java b/src/main/java/com/baidu/fsg/uid/buffer/RejectedTakeBufferHandler.java
new file mode 100644
index 0000000..0afe679
--- /dev/null
+++ b/src/main/java/com/baidu/fsg/uid/buffer/RejectedTakeBufferHandler.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2017 Baidu, Inc. All Rights Reserve.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.baidu.fsg.uid.buffer;
+
+/**
+ * If cursor catches the tail it means that the ring buffer is empty, any more buffer take request will be rejected.
+ * Specify the policy to handle the reject. This is a Lambda supported interface
+ *
+ * @author yutianbao
+ */
+@FunctionalInterface
+public interface RejectedTakeBufferHandler {
+
+ /**
+ * Reject take buffer request
+ *
+ * @param ringBuffer
+ */
+ void rejectTakeBuffer(RingBuffer ringBuffer);
+}
diff --git a/src/main/java/com/baidu/fsg/uid/buffer/RingBuffer.java b/src/main/java/com/baidu/fsg/uid/buffer/RingBuffer.java
new file mode 100644
index 0000000..4e46433
--- /dev/null
+++ b/src/main/java/com/baidu/fsg/uid/buffer/RingBuffer.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 2017 Baidu, Inc. All Rights Reserve.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.baidu.fsg.uid.buffer;
+
+
+import com.baidu.fsg.uid.utils.PaddedAtomicLong;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.Assert;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Represents a ring buffer based on array.
+ * Using array could improve read element performance due to the CUP cache line. To prevent
+ * the side effect of False Sharing, {@link PaddedAtomicLong} is using on 'tail' and 'cursor'
+ *
+ * A ring buffer is consisted of:
+ *
slots: each element of the array is a slot, which is be set with a UID
+ *
flags: flag array corresponding the same index with the slots, indicates whether can take or put slot
+ *
tail: a sequence of the max slot position to produce
+ *
cursor: a sequence of the min slot position to consume
+ *
+ * @author yutianbao
+ */
+public class RingBuffer {
+ private static final Logger LOGGER = LoggerFactory.getLogger(RingBuffer.class);
+
+ /** Constants */
+ private static final int START_POINT = -1;
+ private static final long CAN_PUT_FLAG = 0L;
+ private static final long CAN_TAKE_FLAG = 1L;
+ public static final int DEFAULT_PADDING_PERCENT = 50;
+
+ /** The size of RingBuffer's slots, each slot hold a UID */
+ private final int bufferSize;
+ private final long indexMask;
+ private final long[] slots;
+ private final PaddedAtomicLong[] flags;
+
+ /** Tail: last position sequence to produce */
+ private final AtomicLong tail = new PaddedAtomicLong(START_POINT);
+
+ /** Cursor: current position sequence to consume */
+ private final AtomicLong cursor = new PaddedAtomicLong(START_POINT);
+
+ /** Threshold for trigger padding buffer*/
+ private final int paddingThreshold;
+
+ /** Reject put/take buffer handle policy */
+ private RejectedPutBufferHandler rejectedPutHandler = this::discardPutBuffer;
+ private RejectedTakeBufferHandler rejectedTakeHandler = this::exceptionRejectedTakeBuffer;
+
+ /** Executor of padding buffer */
+ private BufferPaddingExecutor bufferPaddingExecutor;
+
+ /**
+ * Constructor with buffer size, paddingFactor default as {@value #DEFAULT_PADDING_PERCENT}
+ *
+ * @param bufferSize must be positive & a power of 2
+ */
+ public RingBuffer(int bufferSize) {
+ this(bufferSize, DEFAULT_PADDING_PERCENT);
+ }
+
+ /**
+ * Constructor with buffer size & padding factor
+ *
+ * @param bufferSize must be positive & a power of 2
+ * @param paddingFactor percent in (0 - 100). When the count of rest available UIDs reach the threshold, it will trigger padding buffer
+ * Sample: paddingFactor=20, bufferSize=1000 -> threshold=1000 * 20 /100,
+ * padding buffer will be triggered when tail-cursor 0L, "RingBuffer size must be positive");
+ Assert.isTrue(Integer.bitCount(bufferSize) == 1, "RingBuffer size must be a power of 2");
+ Assert.isTrue(paddingFactor > 0 && paddingFactor < 100, "RingBuffer size must be positive");
+
+ this.bufferSize = bufferSize;
+ this.indexMask = bufferSize - 1;
+ this.slots = new long[bufferSize];
+ this.flags = initFlags(bufferSize);
+
+ this.paddingThreshold = bufferSize * paddingFactor / 100;
+ }
+
+ /**
+ * Put an UID in the ring & tail moved
+ * We use 'synchronized' to guarantee the UID fill in slot & publish new tail sequence as atomic operations
+ *
+ * Note that: It is recommended to put UID in a serialize way, cause we once batch generate a series UIDs and put
+ * the one by one into the buffer, so it is unnecessary put in multi-threads
+ *
+ * @param uid
+ * @return false means that the buffer is full, apply {@link RejectedPutBufferHandler}
+ */
+ public synchronized boolean put(long uid) {
+ long currentTail = tail.get();
+ long currentCursor = cursor.get();
+
+ // tail catches the cursor, means that you can't put any cause of RingBuffer is full
+ long distance = currentTail - (currentCursor == START_POINT ? 0 : currentCursor);
+ if (distance == bufferSize - 1) {
+ rejectedPutHandler.rejectPutBuffer(this, uid);
+ return false;
+ }
+
+ // 1. pre-check whether the flag is CAN_PUT_FLAG
+ int nextTailIndex = calSlotIndex(currentTail + 1);
+ if (flags[nextTailIndex].get() != CAN_PUT_FLAG) {
+ rejectedPutHandler.rejectPutBuffer(this, uid);
+ return false;
+ }
+
+ // 2. put UID in the next slot
+ // 3. update next slot' flag to CAN_TAKE_FLAG
+ // 4. publish tail with sequence increase by one
+ slots[nextTailIndex] = uid;
+ flags[nextTailIndex].set(CAN_TAKE_FLAG);
+ tail.incrementAndGet();
+
+ // The atomicity of operations above, guarantees by 'synchronized'. In another word,
+ // the take operation can't consume the UID we just put, until the tail is published(tail.incrementAndGet())
+ return true;
+ }
+
+ /**
+ * Take an UID of the ring at the next cursor, this is a lock free operation by using atomic cursor
+ *
+ * Before getting the UID, we also check whether reach the padding threshold,
+ * the padding buffer operation will be triggered in another thread
+ * If there is no more available UID to be taken, the specified {@link RejectedTakeBufferHandler} will be applied
+ *
+ * @return UID
+ * @throws IllegalStateException if the cursor moved back
+ */
+ public long take() {
+ // spin get next available cursor
+ long currentCursor = cursor.get();
+ long nextCursor = cursor.updateAndGet(old -> old == tail.get() ? old : old + 1);
+
+ // check for safety consideration, it never occurs
+ Assert.isTrue(nextCursor >= currentCursor, "Curosr can't move back");
+
+ // trigger padding in an async-mode if reach the threshold
+ long currentTail = tail.get();
+ if (currentTail - nextCursor < paddingThreshold) {
+ LOGGER.info("Reach the padding threshold:{}. tail:{}, cursor:{}, rest:{}", paddingThreshold, currentTail,
+ nextCursor, currentTail - nextCursor);
+ bufferPaddingExecutor.asyncPadding();
+ }
+
+ // cursor catch the tail, means that there is no more available UID to take
+ if (nextCursor == currentCursor) {
+ rejectedTakeHandler.rejectTakeBuffer(this);
+ }
+
+ // 1. check next slot flag is CAN_TAKE_FLAG
+ int nextCursorIndex = calSlotIndex(nextCursor);
+ Assert.isTrue(flags[nextCursorIndex].get() == CAN_TAKE_FLAG, "Curosr not in can take status");
+
+ // 2. get UID from next slot
+ // 3. set next slot flag as CAN_PUT_FLAG.
+ long uid = slots[nextCursorIndex];
+ flags[nextCursorIndex].set(CAN_PUT_FLAG);
+
+ // Note that: Step 2,3 can not swap. If we set flag before get value of slot, the producer may overwrite the
+ // slot with a new UID, and this may cause the consumer take the UID twice after walk a round the ring
+ return uid;
+ }
+
+ /**
+ * Calculate slot index with the slot sequence (sequence % bufferSize)
+ */
+ protected int calSlotIndex(long sequence) {
+ return (int) (sequence & indexMask);
+ }
+
+ /**
+ * Discard policy for {@link RejectedPutBufferHandler}, we just do logging
+ */
+ protected void discardPutBuffer(RingBuffer ringBuffer, long uid) {
+ LOGGER.warn("Rejected putting buffer for uid:{}. {}", uid, ringBuffer);
+ }
+
+ /**
+ * Policy for {@link RejectedTakeBufferHandler}, throws {@link RuntimeException} after logging
+ */
+ protected void exceptionRejectedTakeBuffer(RingBuffer ringBuffer) {
+ LOGGER.warn("Rejected take buffer. {}", ringBuffer);
+ throw new RuntimeException("Rejected take buffer. " + ringBuffer);
+ }
+
+ /**
+ * Initialize flags as CAN_PUT_FLAG
+ */
+ private PaddedAtomicLong[] initFlags(int bufferSize) {
+ PaddedAtomicLong[] flags = new PaddedAtomicLong[bufferSize];
+ for (int i = 0; i < bufferSize; i++) {
+ flags[i] = new PaddedAtomicLong(CAN_PUT_FLAG);
+ }
+
+ return flags;
+ }
+
+ /**
+ * Getters
+ */
+ public long getTail() {
+ return tail.get();
+ }
+
+ public long getCursor() {
+ return cursor.get();
+ }
+
+ public int getBufferSize() {
+ return bufferSize;
+ }
+
+ /**
+ * Setters
+ */
+ public void setBufferPaddingExecutor(BufferPaddingExecutor bufferPaddingExecutor) {
+ this.bufferPaddingExecutor = bufferPaddingExecutor;
+ }
+
+ public void setRejectedPutHandler(RejectedPutBufferHandler rejectedPutHandler) {
+ this.rejectedPutHandler = rejectedPutHandler;
+ }
+
+ public void setRejectedTakeHandler(RejectedTakeBufferHandler rejectedTakeHandler) {
+ this.rejectedTakeHandler = rejectedTakeHandler;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("RingBuffer [bufferSize=").append(bufferSize)
+ .append(", tail=").append(tail)
+ .append(", cursor=").append(cursor)
+ .append(", paddingThreshold=").append(paddingThreshold).append("]");
+
+ return builder.toString();
+ }
+
+}
diff --git a/src/main/java/com/baidu/fsg/uid/exception/UidGenerateException.java b/src/main/java/com/baidu/fsg/uid/exception/UidGenerateException.java
new file mode 100644
index 0000000..ae7aaff
--- /dev/null
+++ b/src/main/java/com/baidu/fsg/uid/exception/UidGenerateException.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2017 Baidu, Inc. All Rights Reserve.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.baidu.fsg.uid.exception;
+
+/**
+ * UidGenerateException
+ *
+ * @author yutianbao
+ */
+public class UidGenerateException extends RuntimeException {
+
+ /**
+ * Serial Version UID
+ */
+ private static final long serialVersionUID = -27048199131316992L;
+
+ /**
+ * Default constructor
+ */
+ public UidGenerateException() {
+ super();
+ }
+
+ /**
+ * Constructor with message & cause
+ *
+ * @param message
+ * @param cause
+ */
+ public UidGenerateException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * Constructor with message
+ *
+ * @param message
+ */
+ public UidGenerateException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructor with message format
+ *
+ * @param msgFormat
+ * @param args
+ */
+ public UidGenerateException(String msgFormat, Object... args) {
+ super(String.format(msgFormat, args));
+ }
+
+ /**
+ * Constructor with cause
+ *
+ * @param cause
+ */
+ public UidGenerateException(Throwable cause) {
+ super(cause);
+ }
+
+}
diff --git a/src/main/java/com/baidu/fsg/uid/impl/CachedUidGenerator.java b/src/main/java/com/baidu/fsg/uid/impl/CachedUidGenerator.java
new file mode 100644
index 0000000..2364e4c
--- /dev/null
+++ b/src/main/java/com/baidu/fsg/uid/impl/CachedUidGenerator.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2017 Baidu, Inc. All Rights Reserve.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.baidu.fsg.uid.impl;
+
+
+import com.baidu.fsg.uid.buffer.BufferPaddingExecutor;
+import com.baidu.fsg.uid.buffer.RejectedPutBufferHandler;
+import com.baidu.fsg.uid.buffer.RejectedTakeBufferHandler;
+import com.baidu.fsg.uid.buffer.RingBuffer;
+import com.baidu.fsg.uid.exception.UidGenerateException;
+import com.baidu.fsg.uid.UidGenerator;
+import com.baidu.fsg.uid.BitsAllocator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.util.Assert;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a cached implementation of {@link UidGenerator} extends
+ * from {@link DefaultUidGenerator}, based on a lock free {@link RingBuffer}
+ *
+ * The spring properties you can specified as below:
+ *
boostPower: RingBuffer size boost for a power of 2, Sample: boostPower is 3, it means the buffer size
+ * will be ({@link BitsAllocator#getMaxSequence()} + 1) <<
+ * {@link #boostPower}, Default as {@value #DEFAULT_BOOST_POWER}
+ *
paddingFactor: Represents a percent value of (0 - 100). When the count of rest available UIDs reach the
+ * threshold, it will trigger padding buffer. Default as{@link RingBuffer#DEFAULT_PADDING_PERCENT}
+ * Sample: paddingFactor=20, bufferSize=1000 -> threshold=1000 * 20 /100, padding buffer will be triggered when tail-cursorscheduleInterval: Padding buffer in a schedule, specify padding buffer interval, Unit as second
+ *
rejectedPutBufferHandler: Policy for rejected put buffer. Default as discard put request, just do logging
+ *
rejectedTakeBufferHandler: Policy for rejected take buffer. Default as throwing up an exception
+ *
+ * @author yutianbao
+ */
+public class CachedUidGenerator extends DefaultUidGenerator implements DisposableBean {
+ private static final Logger LOGGER = LoggerFactory.getLogger(CachedUidGenerator.class);
+ private static final int DEFAULT_BOOST_POWER = 3;
+
+ /** Spring properties */
+ private int boostPower = DEFAULT_BOOST_POWER;
+ private int paddingFactor = RingBuffer.DEFAULT_PADDING_PERCENT;
+ private Long scheduleInterval;
+
+ private RejectedPutBufferHandler rejectedPutBufferHandler;
+ private RejectedTakeBufferHandler rejectedTakeBufferHandler;
+
+ /** RingBuffer */
+ private RingBuffer ringBuffer;
+ private BufferPaddingExecutor bufferPaddingExecutor;
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ // initialize workerId & bitsAllocator
+ super.afterPropertiesSet();
+
+ // initialize RingBuffer & RingBufferPaddingExecutor
+ this.initRingBuffer();
+ LOGGER.info("Initialized RingBuffer successfully.");
+ }
+
+ @Override
+ public long getUID() {
+ try {
+ System.out.println("我是cache的uid");
+ return ringBuffer.take();
+ } catch (Exception e) {
+ LOGGER.error("Generate unique id exception. ", e);
+ throw new UidGenerateException(e);
+ }
+ }
+
+ @Override
+ public String parseUID(long uid) {
+ return super.parseUID(uid);
+ }
+
+ @Override
+ public void destroy() throws Exception {
+ bufferPaddingExecutor.shutdown();
+ }
+
+ /**
+ * Get the UIDs in the same specified second under the max sequence
+ *
+ * @param currentSecond
+ * @return UID list, size of {@link BitsAllocator#getMaxSequence()} + 1
+ */
+ protected List nextIdsForOneSecond(long currentSecond) {
+ // Initialize result list size of (max sequence + 1)
+ int listSize = (int) bitsAllocator.getMaxSequence() + 1;
+ List uidList = new ArrayList<>(listSize);
+
+ // Allocate the first sequence of the second, the others can be calculated with the offset
+ long firstSeqUid = bitsAllocator.allocate(currentSecond - epochSeconds, workerId, 0L);
+ for (int offset = 0; offset < listSize; offset++) {
+ uidList.add(firstSeqUid + offset);
+ }
+
+ return uidList;
+ }
+
+ /**
+ * Initialize RingBuffer & RingBufferPaddingExecutor
+ */
+ private void initRingBuffer() {
+ // initialize RingBuffer
+ int bufferSize = ((int) bitsAllocator.getMaxSequence() + 1) << boostPower;
+ this.ringBuffer = new RingBuffer(bufferSize, paddingFactor);
+ LOGGER.info("Initialized ring buffer size:{}, paddingFactor:{}", bufferSize, paddingFactor);
+
+ // initialize RingBufferPaddingExecutor
+ boolean usingSchedule = (scheduleInterval != null);
+ this.bufferPaddingExecutor = new BufferPaddingExecutor(ringBuffer, this::nextIdsForOneSecond, usingSchedule);
+ if (usingSchedule) {
+ bufferPaddingExecutor.setScheduleInterval(scheduleInterval);
+ }
+
+ LOGGER.info("Initialized BufferPaddingExecutor. Using schdule:{}, interval:{}", usingSchedule, scheduleInterval);
+
+ // set rejected put/take handle policy
+ this.ringBuffer.setBufferPaddingExecutor(bufferPaddingExecutor);
+ if (rejectedPutBufferHandler != null) {
+ this.ringBuffer.setRejectedPutHandler(rejectedPutBufferHandler);
+ }
+ if (rejectedTakeBufferHandler != null) {
+ this.ringBuffer.setRejectedTakeHandler(rejectedTakeBufferHandler);
+ }
+
+ // fill in all slots of the RingBuffer
+ bufferPaddingExecutor.paddingBuffer();
+
+ // start buffer padding threads
+ bufferPaddingExecutor.start();
+ }
+
+ /**
+ * Setters for spring property
+ */
+ public void setBoostPower(int boostPower) {
+ Assert.isTrue(boostPower > 0, "Boost power must be positive!");
+ this.boostPower = boostPower;
+ }
+
+ public void setRejectedPutBufferHandler(RejectedPutBufferHandler rejectedPutBufferHandler) {
+ Assert.notNull(rejectedPutBufferHandler, "RejectedPutBufferHandler can't be null!");
+ this.rejectedPutBufferHandler = rejectedPutBufferHandler;
+ }
+
+ public void setRejectedTakeBufferHandler(RejectedTakeBufferHandler rejectedTakeBufferHandler) {
+ Assert.notNull(rejectedTakeBufferHandler, "RejectedTakeBufferHandler can't be null!");
+ this.rejectedTakeBufferHandler = rejectedTakeBufferHandler;
+ }
+
+ public void setScheduleInterval(long scheduleInterval) {
+ Assert.isTrue(scheduleInterval > 0, "Schedule interval must positive!");
+ this.scheduleInterval = scheduleInterval;
+ }
+
+}
diff --git a/src/main/java/com/baidu/fsg/uid/impl/DefaultUidGenerator.java b/src/main/java/com/baidu/fsg/uid/impl/DefaultUidGenerator.java
new file mode 100644
index 0000000..77cd117
--- /dev/null
+++ b/src/main/java/com/baidu/fsg/uid/impl/DefaultUidGenerator.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2017 Baidu, Inc. All Rights Reserve.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.baidu.fsg.uid.impl;
+
+import com.baidu.fsg.uid.exception.UidGenerateException;
+import com.baidu.fsg.uid.UidGenerator;
+import com.baidu.fsg.uid.BitsAllocator;
+import com.baidu.fsg.uid.utils.DateUtils;
+import com.baidu.fsg.uid.worker.WorkerIdAssigner;
+
+import com.techsor.datacenter.sender.config.UidProperties;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Represents an implementation of {@link UidGenerator}
+ *
+ * The unique id has 64bits (long), default allocated as blow:
+ *
sign: The highest bit is 0
+ *
delta seconds: The next 28 bits, represents delta seconds since a customer epoch(2016-05-20 00:00:00.000).
+ * Supports about 8.7 years until to 2024-11-20 21:24:16
+ *
worker id: The next 22 bits, represents the worker's id which assigns based on database, max id is about 420W
+ *
sequence: The next 13 bits, represents a sequence within the same second, max for 8192/s
+ *
+ * The {@link DefaultUidGenerator#parseUID(long)} is a tool method to parse the bits
+ *
+ *
+ *
+ * You can also specified the bits by Spring property setting.
+ *
timeBits: default as 28
+ *
workerBits: default as 22
+ *
seqBits: default as 13
+ *
epochStr: Epoch date string format 'yyyy-MM-dd'. Default as '2016-05-20'
+ *
+ * Note that: The total bits must be 64 -1
+ *
+ * @author yutianbao
+ */
+@Component
+public class DefaultUidGenerator implements UidGenerator, InitializingBean {
+ private static final Logger LOGGER = LoggerFactory.getLogger(DefaultUidGenerator.class);
+ @Autowired
+ UidProperties uidProperties;
+ /** Bits allocate */
+ /**
+ * timeBits = 31 68年
+ * workerBits = 17 每天重启5次可用70年
+ * seqBits = 15 每秒32768个并发
+ */
+
+
+ /** Customer epoch, unit as second. For example 2016-05-20 (ms: 1463673600000)*/
+ /**
+ * 默认时间更改为2020-10-28(毫秒值:1603814400000)
+ */
+ protected String epochStr = "2023-11-01";
+ protected long epochSeconds = TimeUnit.MILLISECONDS.toSeconds(1698768000000L);
+
+ /** Stable fields after spring bean initializing */
+ protected BitsAllocator bitsAllocator;
+ protected long workerId;
+
+ /** Volatile fields caused by nextId() */
+ protected long sequence = 0L;
+ protected long lastSecond = -1L;
+
+ /** Spring property */
+ @Autowired
+ protected WorkerIdAssigner workerIdAssigner;
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ // initialize bits allocator
+ bitsAllocator = new BitsAllocator(uidProperties.getTimeBits(), uidProperties.getWorkerBits(), uidProperties.getSeqBits());
+
+ // initialize worker id
+ workerId = workerIdAssigner.assignWorkerId();
+ if (workerId > bitsAllocator.getMaxWorkerId()) {
+ throw new RuntimeException("Worker id " + workerId + " exceeds the max " + bitsAllocator.getMaxWorkerId());
+ }
+
+ LOGGER.info("Initialized bits(1, {}, {}, {}) for workerID:{}", uidProperties.getTimeBits(), uidProperties.getWorkerBits(), uidProperties.getSeqBits(), workerId);
+ }
+
+ @Override
+ public long getUID() throws UidGenerateException {
+ try {
+ return nextId();
+ } catch (Exception e) {
+ LOGGER.error("Generate unique id exception. ", e);
+ throw new UidGenerateException(e);
+ }
+ }
+
+ @Override
+ public String parseUID(long uid) {
+ long totalBits = BitsAllocator.TOTAL_BITS;
+ long signBits = bitsAllocator.getSignBits();
+ long timestampBits = bitsAllocator.getTimestampBits();
+ long workerIdBits = bitsAllocator.getWorkerIdBits();
+ long sequenceBits = bitsAllocator.getSequenceBits();
+
+ // parse UID
+ long sequence = (uid << (totalBits - sequenceBits)) >>> (totalBits - sequenceBits);
+ long workerId = (uid << (timestampBits + signBits)) >>> (totalBits - workerIdBits);
+ long deltaSeconds = uid >>> (workerIdBits + sequenceBits);
+
+ Date thatTime = new Date(TimeUnit.SECONDS.toMillis(epochSeconds + deltaSeconds));
+ String thatTimeStr = DateUtils.formatByDateTimePattern(thatTime);
+
+ // format as string
+ return String.format("{\"UID\":\"%d\",\"timestamp\":\"%s\",\"workerId\":\"%d\",\"sequence\":\"%d\"}",
+ uid, thatTimeStr, workerId, sequence);
+ }
+
+ /**
+ * Get UID
+ *
+ * @return UID
+ * @throws UidGenerateException in the case: Clock moved backwards; Exceeds the max timestamp
+ */
+ protected synchronized long nextId() {
+ long currentSecond = getCurrentSecond();
+
+ // Clock moved backwards, refuse to generate uid
+ if (currentSecond < lastSecond) {
+ long refusedSeconds = lastSecond - currentSecond;
+ throw new UidGenerateException("Clock moved backwards. Refusing for %d seconds", refusedSeconds);
+ }
+
+ // At the same second, increase sequence
+ if (currentSecond == lastSecond) {
+ sequence = (sequence + 1) & bitsAllocator.getMaxSequence();
+ // Exceed the max sequence, we wait the next second to generate uid
+ if (sequence == 0) {
+ currentSecond = getNextSecond(lastSecond);
+ }
+
+ // At the different second, sequence restart from zero
+ } else {
+ sequence = 0L;
+ }
+
+ lastSecond = currentSecond;
+
+ // Allocate bits for UID
+ return bitsAllocator.allocate(currentSecond - epochSeconds, workerId, sequence);
+ }
+
+ /**
+ * Get next millisecond
+ */
+ private long getNextSecond(long lastTimestamp) {
+ long timestamp = getCurrentSecond();
+ while (timestamp <= lastTimestamp) {
+ timestamp = getCurrentSecond();
+ }
+
+ return timestamp;
+ }
+
+ /**
+ * Get current second
+ */
+ private long getCurrentSecond() {
+ long currentSecond = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis());
+ if (currentSecond - epochSeconds > bitsAllocator.getMaxDeltaSeconds()) {
+ throw new UidGenerateException("Timestamp bits is exhausted. Refusing UID generate. Now: " + currentSecond);
+ }
+
+ return currentSecond;
+ }
+
+ /**
+ * Setters for spring property
+ */
+ public void setWorkerIdAssigner(WorkerIdAssigner workerIdAssigner) {
+ this.workerIdAssigner = workerIdAssigner;
+ }
+
+
+
+ public void setEpochStr(String epochStr) {
+ if (StringUtils.isNotBlank(epochStr)) {
+ this.epochStr = epochStr;
+ this.epochSeconds = TimeUnit.MILLISECONDS.toSeconds(DateUtils.parseByDayPattern(epochStr).getTime());
+ }
+ }
+}
diff --git a/src/main/java/com/baidu/fsg/uid/utils/DateUtils.java b/src/main/java/com/baidu/fsg/uid/utils/DateUtils.java
new file mode 100644
index 0000000..b8d11b4
--- /dev/null
+++ b/src/main/java/com/baidu/fsg/uid/utils/DateUtils.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2017 Baidu, Inc. All Rights Reserve.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.baidu.fsg.uid.utils;
+
+
+import org.apache.commons.lang3.time.DateFormatUtils;
+
+import java.text.ParseException;
+import java.util.Calendar;
+import java.util.Date;
+
+/**
+ * DateUtils provides date formatting, parsing
+ *
+ * @author yutianbao
+ */
+public abstract class DateUtils extends org.apache.commons.lang3.time.DateUtils {
+ /**
+ * Patterns
+ */
+ public static final String DAY_PATTERN = "yyyy-MM-dd";
+ public static final String DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
+ public static final String DATETIME_MS_PATTERN = "yyyy-MM-dd HH:mm:ss.SSS";
+
+ public static final Date DEFAULT_DATE = DateUtils.parseByDayPattern("1970-01-01");
+
+ /**
+ * Parse date by 'yyyy-MM-dd' pattern
+ *
+ * @param str
+ * @return
+ */
+ public static Date parseByDayPattern(String str) {
+ return parseDate(str, DAY_PATTERN);
+ }
+
+ /**
+ * Parse date by 'yyyy-MM-dd HH:mm:ss' pattern
+ *
+ * @param str
+ * @return
+ */
+ public static Date parseByDateTimePattern(String str) {
+ return parseDate(str, DATETIME_PATTERN);
+ }
+
+ /**
+ * Parse date without Checked exception
+ *
+ * @param str
+ * @param pattern
+ * @return
+ * @throws RuntimeException when ParseException occurred
+ */
+ public static Date parseDate(String str, String pattern) {
+ try {
+ return parseDate(str, new String[]{pattern});
+ } catch (ParseException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Format date into string
+ *
+ * @param date
+ * @param pattern
+ * @return
+ */
+ public static String formatDate(Date date, String pattern) {
+ return DateFormatUtils.format(date, pattern);
+ }
+
+ /**
+ * Format date by 'yyyy-MM-dd' pattern
+ *
+ * @param date
+ * @return
+ */
+ public static String formatByDayPattern(Date date) {
+ if (date != null) {
+ return DateFormatUtils.format(date, DAY_PATTERN);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Format date by 'yyyy-MM-dd HH:mm:ss' pattern
+ *
+ * @param date
+ * @return
+ */
+ public static String formatByDateTimePattern(Date date) {
+ return DateFormatUtils.format(date, DATETIME_PATTERN);
+ }
+
+ /**
+ * Get current day using format date by 'yyyy-MM-dd HH:mm:ss' pattern
+ *
+ * @return
+ * @author yebo
+ */
+ public static String getCurrentDayByDayPattern() {
+ Calendar cal = Calendar.getInstance();
+ return formatByDayPattern(cal.getTime());
+ }
+
+}
diff --git a/src/main/java/com/baidu/fsg/uid/utils/DockerUtils.java b/src/main/java/com/baidu/fsg/uid/utils/DockerUtils.java
new file mode 100644
index 0000000..a7950db
--- /dev/null
+++ b/src/main/java/com/baidu/fsg/uid/utils/DockerUtils.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2017 Baidu, Inc. All Rights Reserve.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.baidu.fsg.uid.utils;
+
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * DockerUtils
+ *
+ * @author yutianbao
+ */
+public abstract class DockerUtils {
+ private static final Logger LOGGER = LoggerFactory.getLogger(DockerUtils.class);
+
+ /** Environment param keys */
+ private static final String ENV_KEY_HOST = "JPAAS_HOST";
+ private static final String ENV_KEY_PORT = "JPAAS_HTTP_PORT";
+ private static final String ENV_KEY_PORT_ORIGINAL = "JPAAS_HOST_PORT_8080";
+
+ /** Docker host & port */
+ private static String DOCKER_HOST = "";
+ private static String DOCKER_PORT = "";
+
+ /** Whether is docker */
+ private static boolean IS_DOCKER;
+
+ static {
+ retrieveFromEnv();
+ }
+
+ /**
+ * Retrieve docker host
+ *
+ * @return empty string if not a docker
+ */
+ public static String getDockerHost() {
+ return DOCKER_HOST;
+ }
+
+ /**
+ * Retrieve docker port
+ *
+ * @return empty string if not a docker
+ */
+ public static String getDockerPort() {
+ return DOCKER_PORT;
+ }
+
+ /**
+ * Whether a docker
+ *
+ * @return
+ */
+ public static boolean isDocker() {
+ return IS_DOCKER;
+ }
+
+ /**
+ * Retrieve host & port from environment
+ */
+ private static void retrieveFromEnv() {
+ // retrieve host & port from environment
+ DOCKER_HOST = System.getenv(ENV_KEY_HOST);
+ DOCKER_PORT = System.getenv(ENV_KEY_PORT);
+
+ // not found from 'JPAAS_HTTP_PORT', then try to find from 'JPAAS_HOST_PORT_8080'
+ if (StringUtils.isBlank(DOCKER_PORT)) {
+ DOCKER_PORT = System.getenv(ENV_KEY_PORT_ORIGINAL);
+ }
+
+ boolean hasEnvHost = StringUtils.isNotBlank(DOCKER_HOST);
+ boolean hasEnvPort = StringUtils.isNotBlank(DOCKER_PORT);
+
+ // docker can find both host & port from environment
+ if (hasEnvHost && hasEnvPort) {
+ IS_DOCKER = true;
+
+ // found nothing means not a docker, maybe an actual machine
+ } else if (!hasEnvHost && !hasEnvPort) {
+ IS_DOCKER = false;
+
+ } else {
+ LOGGER.error("Missing host or port from env for Docker. host:{}, port:{}", DOCKER_HOST, DOCKER_PORT);
+ throw new RuntimeException(
+ "Missing host or port from env for Docker. host:" + DOCKER_HOST + ", port:" + DOCKER_PORT);
+ }
+ }
+
+}
diff --git a/src/main/java/com/baidu/fsg/uid/utils/EnumUtils.java b/src/main/java/com/baidu/fsg/uid/utils/EnumUtils.java
new file mode 100644
index 0000000..3261539
--- /dev/null
+++ b/src/main/java/com/baidu/fsg/uid/utils/EnumUtils.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2017 Baidu, Inc. All Rights Reserve.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.baidu.fsg.uid.utils;
+
+import org.springframework.util.Assert;
+
+/**
+ * EnumUtils provides the operations for {@link ValuedEnum} such as Parse, value of...
+ *
+ * @author yutianbao
+ */
+public abstract class EnumUtils {
+
+ /**
+ * Parse the bounded value into ValuedEnum
+ *
+ * @param clz
+ * @param value
+ * @return
+ */
+ public static , V> T parse(Class clz, V value) {
+ Assert.notNull(clz, "clz can not be null");
+ if (value == null) {
+ return null;
+ }
+
+ for (T t : clz.getEnumConstants()) {
+ if (value.equals(t.value())) {
+ return t;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Null-safe valueOf function
+ *
+ * @param
+ * @param enumType
+ * @param name
+ * @return
+ */
+ public static > T valueOf(Class enumType, String name) {
+ if (name == null) {
+ return null;
+ }
+
+ return Enum.valueOf(enumType, name);
+ }
+
+}
diff --git a/src/main/java/com/baidu/fsg/uid/utils/NamingThreadFactory.java b/src/main/java/com/baidu/fsg/uid/utils/NamingThreadFactory.java
new file mode 100644
index 0000000..58263a0
--- /dev/null
+++ b/src/main/java/com/baidu/fsg/uid/utils/NamingThreadFactory.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2017 Baidu, Inc. All Rights Reserve.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.baidu.fsg.uid.utils;
+
+import org.apache.commons.lang3.ClassUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.Thread.UncaughtExceptionHandler;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Named thread in ThreadFactory. If there is no specified name for thread, it
+ * will auto detect using the invoker classname instead.
+ *
+ * @author yutianbao
+ */
+public class NamingThreadFactory implements ThreadFactory {
+ private static final Logger LOGGER = LoggerFactory.getLogger(NamingThreadFactory.class);
+
+ /**
+ * Thread name pre
+ */
+ private String name;
+ /**
+ * Is daemon thread
+ */
+ private boolean daemon;
+ /**
+ * UncaughtExceptionHandler
+ */
+ private UncaughtExceptionHandler uncaughtExceptionHandler;
+ /**
+ * Sequences for multi thread name prefix
+ */
+ private final ConcurrentHashMap sequences;
+
+ /**
+ * Constructors
+ */
+ public NamingThreadFactory() {
+ this(null, false, null);
+ }
+
+ public NamingThreadFactory(String name) {
+ this(name, false, null);
+ }
+
+ public NamingThreadFactory(String name, boolean daemon) {
+ this(name, daemon, null);
+ }
+
+ public NamingThreadFactory(String name, boolean daemon, UncaughtExceptionHandler handler) {
+ this.name = name;
+ this.daemon = daemon;
+ this.uncaughtExceptionHandler = handler;
+ this.sequences = new ConcurrentHashMap();
+ }
+
+ @Override
+ public Thread newThread(Runnable r) {
+ Thread thread = new Thread(r);
+ thread.setDaemon(this.daemon);
+
+ // If there is no specified name for thread, it will auto detect using the invoker classname instead.
+ // Notice that auto detect may cause some performance overhead
+ String prefix = this.name;
+ if (StringUtils.isBlank(prefix)) {
+ prefix = getInvoker(2);
+ }
+ thread.setName(prefix + "-" + getSequence(prefix));
+
+ // no specified uncaughtExceptionHandler, just do logging.
+ if (this.uncaughtExceptionHandler != null) {
+ thread.setUncaughtExceptionHandler(this.uncaughtExceptionHandler);
+ } else {
+ thread.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
+ @Override
+ public void uncaughtException(Thread t, Throwable e) {
+ LOGGER.error("unhandled exception in thread: " + t.getId() + ":" + t.getName(), e);
+ }
+ });
+ }
+
+ return thread;
+ }
+
+ /**
+ * Get the method invoker's class name
+ *
+ * @param depth
+ * @return
+ */
+ private String getInvoker(int depth) {
+ Exception e = new Exception();
+ StackTraceElement[] stes = e.getStackTrace();
+ if (stes.length > depth) {
+ return ClassUtils.getShortClassName(stes[depth].getClassName());
+ }
+ return getClass().getSimpleName();
+ }
+
+ /**
+ * Get sequence for different naming prefix
+ *
+ * @param invoker
+ * @return
+ */
+ private long getSequence(String invoker) {
+ AtomicLong r = this.sequences.get(invoker);
+ if (r == null) {
+ r = new AtomicLong(0);
+ AtomicLong previous = this.sequences.putIfAbsent(invoker, r);
+ if (previous != null) {
+ r = previous;
+ }
+ }
+
+ return r.incrementAndGet();
+ }
+
+ /**
+ * Getters & Setters
+ */
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public boolean isDaemon() {
+ return daemon;
+ }
+
+ public void setDaemon(boolean daemon) {
+ this.daemon = daemon;
+ }
+
+ public UncaughtExceptionHandler getUncaughtExceptionHandler() {
+ return uncaughtExceptionHandler;
+ }
+
+ public void setUncaughtExceptionHandler(UncaughtExceptionHandler handler) {
+ this.uncaughtExceptionHandler = handler;
+ }
+
+}
diff --git a/src/main/java/com/baidu/fsg/uid/utils/NetUtils.java b/src/main/java/com/baidu/fsg/uid/utils/NetUtils.java
new file mode 100644
index 0000000..4f0f17f
--- /dev/null
+++ b/src/main/java/com/baidu/fsg/uid/utils/NetUtils.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2017 Baidu, Inc. All Rights Reserve.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.baidu.fsg.uid.utils;
+
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.util.Enumeration;
+
+/**
+ * NetUtils
+ *
+ * @author yutianbao
+ */
+public abstract class NetUtils {
+
+ /**
+ * Pre-loaded local address
+ */
+ public static InetAddress localAddress;
+
+ static {
+ try {
+ localAddress = getLocalInetAddress();
+ } catch (SocketException e) {
+ throw new RuntimeException("fail to get local ip.");
+ }
+ }
+
+ /**
+ * Retrieve the first validated local ip address(the Public and LAN ip addresses are validated).
+ *
+ * @return the local address
+ * @throws SocketException the socket exception
+ */
+ public static InetAddress getLocalInetAddress() throws SocketException {
+ // enumerates all network interfaces
+ Enumeration enu = NetworkInterface.getNetworkInterfaces();
+
+ while (enu.hasMoreElements()) {
+ NetworkInterface ni = enu.nextElement();
+ if (ni.isLoopback()) {
+ continue;
+ }
+
+ Enumeration addressEnumeration = ni.getInetAddresses();
+ while (addressEnumeration.hasMoreElements()) {
+ InetAddress address = addressEnumeration.nextElement();
+
+ // ignores all invalidated addresses
+ if (address.isLinkLocalAddress() || address.isLoopbackAddress() || address.isAnyLocalAddress()) {
+ continue;
+ }
+
+ return address;
+ }
+ }
+
+ throw new RuntimeException("No validated local address!");
+ }
+
+ /**
+ * Retrieve local address
+ *
+ * @return the string local address
+ */
+ public static String getLocalAddress() {
+ return localAddress.getHostAddress();
+ }
+
+}
diff --git a/src/main/java/com/baidu/fsg/uid/utils/PaddedAtomicLong.java b/src/main/java/com/baidu/fsg/uid/utils/PaddedAtomicLong.java
new file mode 100644
index 0000000..c338312
--- /dev/null
+++ b/src/main/java/com/baidu/fsg/uid/utils/PaddedAtomicLong.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2017 Baidu, Inc. All Rights Reserve.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.baidu.fsg.uid.utils;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Represents a padded {@link AtomicLong} to prevent the FalseSharing problem
+ *
+ * The CPU cache line commonly be 64 bytes, here is a sample of cache line after padding:
+ * 64 bytes = 8 bytes (object reference) + 6 * 8 bytes (padded long) + 8 bytes (a long value)
+ *
+ * @author yutianbao
+ */
+public class PaddedAtomicLong extends AtomicLong {
+ private static final long serialVersionUID = -3415778863941386253L;
+
+ /** Padded 6 long (48 bytes) */
+ public volatile long p1, p2, p3, p4, p5, p6 = 7L;
+
+ /**
+ * Constructors from {@link AtomicLong}
+ */
+ public PaddedAtomicLong() {
+ super();
+ }
+
+ public PaddedAtomicLong(long initialValue) {
+ super(initialValue);
+ }
+
+ /**
+ * To prevent GC optimizations for cleaning unused padded references
+ */
+ public long sumPaddingToPreventOptimization() {
+ return p1 + p2 + p3 + p4 + p5 + p6;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/baidu/fsg/uid/utils/ValuedEnum.java b/src/main/java/com/baidu/fsg/uid/utils/ValuedEnum.java
new file mode 100644
index 0000000..22a4964
--- /dev/null
+++ b/src/main/java/com/baidu/fsg/uid/utils/ValuedEnum.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2017 Baidu, Inc. All Rights Reserve.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.baidu.fsg.uid.utils;
+
+/**
+ * {@code ValuedEnum} defines an enumeration which is bounded to a value, you
+ * may implements this interface when you defines such kind of enumeration, that
+ * you can use {@link EnumUtils} to simplify parse and valueOf operation.
+ *
+ * @author yutianbao
+ */
+public interface ValuedEnum {
+ T value();
+}
diff --git a/src/main/java/com/baidu/fsg/uid/worker/DisposableWorkerIdAssigner.java b/src/main/java/com/baidu/fsg/uid/worker/DisposableWorkerIdAssigner.java
new file mode 100644
index 0000000..bf98d73
--- /dev/null
+++ b/src/main/java/com/baidu/fsg/uid/worker/DisposableWorkerIdAssigner.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2017 Baidu, Inc. All Rights Reserve.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.baidu.fsg.uid.worker;
+
+
+import com.baidu.fsg.uid.worker.service.WorkerNodeEntityService;
+import com.baidu.fsg.uid.utils.DockerUtils;
+import com.baidu.fsg.uid.utils.NetUtils;
+import org.apache.commons.lang3.RandomUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.transaction.annotation.Transactional;
+
+import jakarta.annotation.Resource;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import com.baidu.fsg.uid.worker.entity.WorkerNodeEntity;
+/**
+ * Represents an implementation of {@link WorkerIdAssigner},
+ * the worker id will be discarded after assigned to the UidGenerator
+ *
+ * @author yutianbao
+ */
+public class DisposableWorkerIdAssigner implements WorkerIdAssigner {
+ private static final Logger LOGGER = LoggerFactory.getLogger(DisposableWorkerIdAssigner.class);
+
+ @Resource
+ private WorkerNodeEntityService workerNodeEntityService;
+ /**
+ * Assign worker id base on database.
+ * If there is host name & port in the environment, we considered that the node runs in Docker container
+ * Otherwise, the node runs on an actual machine.
+ *
+ * @return assigned worker id
+ */
+ @Override
+ @Transactional
+ public long assignWorkerId() {
+ // build worker node entity
+ WorkerNodeEntity workerNodeEntity = buildWorkerNode();
+
+ // add worker node for new (ignore the same IP + PORT)
+ Long id = workerNodeEntityService.save(workerNodeEntity);
+ LOGGER.info("Add worker node:" + workerNodeEntity);
+ return id;
+ }
+
+ /**
+ * Build worker node entity by IP and PORT
+ */
+ private WorkerNodeEntity buildWorkerNode() {
+ WorkerNodeEntity workerNodeEntity = new WorkerNodeEntity();
+ if (DockerUtils.isDocker()) {
+ workerNodeEntity.setType(WorkerNodeType.CONTAINER.value());
+ workerNodeEntity.setHostName(DockerUtils.getDockerHost());
+ workerNodeEntity.setPort(DockerUtils.getDockerPort());
+
+ } else {
+ workerNodeEntity.setType(WorkerNodeType.ACTUAL.value());
+ workerNodeEntity.setHostName(NetUtils.getLocalAddress());
+ workerNodeEntity.setPort(System.currentTimeMillis() + "-" + RandomUtils.nextInt(0,100000));
+ }
+ workerNodeEntity.setLaunchDate(LocalDate.now());
+ workerNodeEntity.setCreated(LocalDateTime.now());
+ workerNodeEntity.setModified(LocalDateTime.now());
+
+ return workerNodeEntity;
+ }
+
+}
diff --git a/src/main/java/com/baidu/fsg/uid/worker/WorkerIdAssigner.java b/src/main/java/com/baidu/fsg/uid/worker/WorkerIdAssigner.java
new file mode 100644
index 0000000..7119f20
--- /dev/null
+++ b/src/main/java/com/baidu/fsg/uid/worker/WorkerIdAssigner.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2017 Baidu, Inc. All Rights Reserve.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.baidu.fsg.uid.worker;
+import com.baidu.fsg.uid.impl.DefaultUidGenerator;
+
+
+
+/**
+ * Represents a worker id assigner for {@link DefaultUidGenerator}
+ *
+ * @author yutianbao
+ */
+public interface WorkerIdAssigner {
+
+ /**
+ * Assign worker id for {@link DefaultUidGenerator}
+ *
+ * @return assigned worker id
+ */
+ long assignWorkerId();
+
+}
diff --git a/src/main/java/com/baidu/fsg/uid/worker/WorkerNodeType.java b/src/main/java/com/baidu/fsg/uid/worker/WorkerNodeType.java
new file mode 100644
index 0000000..3d86c72
--- /dev/null
+++ b/src/main/java/com/baidu/fsg/uid/worker/WorkerNodeType.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2017 Baidu, Inc. All Rights Reserve.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.baidu.fsg.uid.worker;
+
+
+import com.baidu.fsg.uid.utils.ValuedEnum;
+
+/**
+ * WorkerNodeType
+ *