生成订单唯一编号由原来简单时间戳修改为idworker生产分布式唯一值

This commit is contained in:
hupeng
2019-12-29 17:30:52 +08:00
parent 0b22748afe
commit 9735253c01
21 changed files with 1409 additions and 15 deletions

View File

@ -0,0 +1,35 @@
package org.n3r.idworker;
import org.n3r.idworker.strategy.DefaultRandomCodeStrategy;
public class Code {
private static RandomCodeStrategy strategy;
static {
RandomCodeStrategy strategy = new DefaultRandomCodeStrategy();
strategy.init();
configure(strategy);
}
public static synchronized void configure(RandomCodeStrategy custom) {
if (strategy == custom) return;
if (strategy != null) strategy.release();
strategy = custom;
}
/**
* Next Unique code.
* The max length will be 1024-Integer.MAX-Integer.MAX(2147483647) which has 4+10+10+2*1=26 characters.
* The min length will be 0-0.
*
* @return unique string code.
*/
public static synchronized String next() {
long workerId = Id.getWorkerId();
int prefix = strategy.prefix();
int next = strategy.next();
return String.format("%d-%03d-%06d", workerId, prefix, next);
}
}

View File

@ -0,0 +1,19 @@
package org.n3r.idworker;
import org.n3r.idworker.strategy.DayPrefixRandomCodeStrategy;
public class DayCode {
static RandomCodeStrategy strategy;
static {
DayPrefixRandomCodeStrategy dayPrefixCodeStrategy = new DayPrefixRandomCodeStrategy("yyMM");
dayPrefixCodeStrategy.setMinRandomSize(7);
dayPrefixCodeStrategy.setMaxRandomSize(7);
strategy = dayPrefixCodeStrategy;
strategy.init();
}
public static synchronized String next() {
return String.format("%d-%04d-%07d", Id.getWorkerId(), strategy.prefix(), strategy.next());
}
}

View File

@ -0,0 +1,29 @@
package org.n3r.idworker;
import org.n3r.idworker.strategy.DefaultWorkerIdStrategy;
public class Id {
private static WorkerIdStrategy workerIdStrategy;
private static IdWorker idWorker;
static {
configure(DefaultWorkerIdStrategy.instance);
}
public static synchronized void configure(WorkerIdStrategy custom) {
if (workerIdStrategy == custom) return;
if (workerIdStrategy != null) workerIdStrategy.release();
workerIdStrategy = custom;
workerIdStrategy.initialize();
idWorker = new IdWorker(workerIdStrategy.availableWorkerId());
}
public static long next() {
return idWorker.nextId();
}
public static long getWorkerId() {
return idWorker.getWorkerId();
}
}

View File

@ -0,0 +1,91 @@
package org.n3r.idworker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.security.SecureRandom;
public class IdWorker {
protected long epoch = 1288834974657L;
// protected long epoch = 1387886498127L; // 2013-12-24 20:01:38.127
protected long workerIdBits = 10L;
protected long maxWorkerId = -1L ^ (-1L << workerIdBits);
protected long sequenceBits = 11L;
protected long workerIdShift = sequenceBits;
protected long timestampLeftShift = sequenceBits + workerIdBits;
protected long sequenceMask = -1L ^ (-1L << sequenceBits);
protected long lastMillis = -1L;
protected final long workerId;
protected long sequence = 0L;
protected Logger logger = LoggerFactory.getLogger(IdWorker.class);
public IdWorker(long workerId) {
this.workerId = checkWorkerId(workerId);
logger.debug("worker starting. timestamp left shift {}, worker id {}", timestampLeftShift, workerId);
}
public long getEpoch() {
return epoch;
}
private long checkWorkerId(long workerId) {
// sanity check for workerId
if (workerId > maxWorkerId || workerId < 0) {
int rand = new SecureRandom().nextInt((int) maxWorkerId + 1);
logger.warn("worker Id can't be greater than {} or less than 0, use a random {}", maxWorkerId, rand);
return rand;
}
return workerId;
}
public synchronized long nextId() {
long timestamp = millisGen();
if (timestamp < lastMillis) {
logger.error("clock is moving backwards. Rejecting requests until {}.", lastMillis);
throw new InvalidSystemClock(String.format(
"Clock moved backwards. Refusing to generate id for {} milliseconds", lastMillis - timestamp));
}
if (lastMillis == timestamp) {
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0)
timestamp = tilNextMillis(lastMillis);
} else {
sequence = 0;
}
lastMillis = timestamp;
long diff = timestamp - getEpoch();
return (diff << timestampLeftShift) |
(workerId << workerIdShift) |
sequence;
}
protected long tilNextMillis(long lastMillis) {
long millis = millisGen();
while (millis <= lastMillis)
millis = millisGen();
return millis;
}
protected long millisGen() {
return System.currentTimeMillis();
}
public long getLastMillis() {
return lastMillis;
}
public long getWorkerId() {
return workerId;
}
}

View File

@ -0,0 +1,7 @@
package org.n3r.idworker;
public class InvalidSystemClock extends RuntimeException {
public InvalidSystemClock(String message) {
super(message);
}
}

View File

@ -0,0 +1,11 @@
package org.n3r.idworker;
public interface RandomCodeStrategy {
void init();
int prefix();
int next();
void release();
}

View File

@ -0,0 +1,62 @@
package org.n3r.idworker;
import org.n3r.idworker.strategy.DefaultWorkerIdStrategy;
import org.n3r.idworker.utils.Utils;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.Date;
@Component
public class Sid {
private static WorkerIdStrategy workerIdStrategy;
private static IdWorker idWorker;
static {
configure(DefaultWorkerIdStrategy.instance);
}
public static synchronized void configure(WorkerIdStrategy custom) {
if (workerIdStrategy != null) workerIdStrategy.release();
workerIdStrategy = custom;
idWorker = new IdWorker(workerIdStrategy.availableWorkerId()) {
@Override
public long getEpoch() {
return Utils.midnightMillis();
}
};
}
/**
* 一天最大毫秒86400000最大占用27比特
* 27+10+11=48位 最大值281474976710655(15字)YK0XXHZ827(10字)
* 6位(YYMMDD)+15位共21位
*
* @return 固定21位数字字符串
*/
public static String next() {
long id = idWorker.nextId();
String yyMMdd = new SimpleDateFormat("yyMMdd").format(new Date());
return yyMMdd + String.format("%014d", id);
}
/**
* 返回固定16位的字母数字混编的字符串。
*/
public String nextShort() {
long id = idWorker.nextId();
String yyMMdd = new SimpleDateFormat("yyMMdd").format(new Date());
return yyMMdd + Utils.padLeft(Utils.encode(id), 10, '0');
}
// public static void main(String[] args) {
// String aa = new Sid().nextShort();
// String bb = new Sid().next();
//
// System.out.println(aa);
// System.out.println(bb);
// }
}

View File

@ -0,0 +1,12 @@
package org.n3r.idworker;
public class Test {
public static void main(String[] args) {
for (int i = 0 ; i < 1000 ; i ++) {
// System.out.println(Sid.nextShort());
}
}
}

View File

@ -0,0 +1,9 @@
package org.n3r.idworker;
public interface WorkerIdStrategy {
void initialize();
long availableWorkerId();
void release();
}

View File

@ -0,0 +1,41 @@
package org.n3r.idworker.strategy;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DayPrefixRandomCodeStrategy extends DefaultRandomCodeStrategy {
private final String dayFormat;
private String lastDay;
public DayPrefixRandomCodeStrategy(String dayFormat) {
this.dayFormat = dayFormat;
}
@Override
public void init() {
String day = createDate();
if (day.equals(lastDay))
throw new RuntimeException("init failed for day unrolled");
lastDay = day;
availableCodes.clear();
release();
prefixIndex = Integer.parseInt(lastDay);
if (tryUsePrefix()) return;
throw new RuntimeException("prefix is not available " + prefixIndex);
}
private String createDate() {
return new SimpleDateFormat(dayFormat).format(new Date());
}
@Override
public int next() {
if (!lastDay.equals(createDate())) init();
return super.next();
}
}

View File

@ -0,0 +1,197 @@
package org.n3r.idworker.strategy;
import org.n3r.idworker.Id;
import org.n3r.idworker.RandomCodeStrategy;
import org.n3r.idworker.utils.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.ArrayDeque;
import java.util.BitSet;
import java.util.Queue;
public class DefaultRandomCodeStrategy implements RandomCodeStrategy {
public static final int MAX_BITS = 1000000;
Logger log = LoggerFactory.getLogger(DefaultRandomCodeStrategy.class);
File idWorkerHome = Utils.createIdWorkerHome();
volatile FileLock fileLock;
BitSet codesFilter;
int prefixIndex = -1;
File codePrefixIndex;
int minRandomSize = 6;
int maxRandomSize = 6;
public DefaultRandomCodeStrategy() {
destroyFileLockWhenShutdown();
}
@Override
public void init() {
release();
while (++prefixIndex < 1000) {
if (tryUsePrefix()) return;
}
throw new RuntimeException("all prefixes are used up, the world maybe ends!");
}
public DefaultRandomCodeStrategy setMinRandomSize(int minRandomSize) {
this.minRandomSize = minRandomSize;
return this;
}
public DefaultRandomCodeStrategy setMaxRandomSize(int maxRandomSize) {
this.maxRandomSize = maxRandomSize;
return this;
}
protected boolean tryUsePrefix() {
codePrefixIndex = new File(idWorkerHome, Id.getWorkerId() + ".code.prefix." + prefixIndex);
if (!createPrefixIndexFile()) return false;
if (!createFileLock()) return false;
if (!createBloomFilter()) return false;
log.info("get available prefix index file {}", codePrefixIndex);
return true;
}
private boolean createFileLock() {
if (fileLock != null) fileLock.destroy();
fileLock = new FileLock(codePrefixIndex);
return fileLock.tryLock();
}
private boolean createBloomFilter() {
codesFilter = fileLock.readObject();
if (codesFilter == null) {
log.info("create new bloom filter");
codesFilter = new BitSet(MAX_BITS); // 2^24
} else {
int size = codesFilter.cardinality();
if (size >= MAX_BITS) {
log.warn("bloom filter with prefix file {} is already full", codePrefixIndex);
return false;
}
log.info("recreate bloom filter with cardinality {}", size);
}
return true;
}
private void destroyFileLockWhenShutdown() {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
release();
}
});
}
private boolean createPrefixIndexFile() {
try {
codePrefixIndex.createNewFile();
return codePrefixIndex.exists();
} catch (IOException e) {
e.printStackTrace();
log.warn("create file {} error {}", codePrefixIndex, e.getMessage());
}
return false;
}
@Override
public int prefix() {
return prefixIndex;
}
static final int CACHE_CODES_NUM = 1000;
SecureRandom secureRandom = new SecureRandom();
Queue<Integer> availableCodes = new ArrayDeque<Integer>(CACHE_CODES_NUM);
@Override
public int next() {
if (availableCodes.isEmpty()) generate();
return availableCodes.poll();
}
@Override
public synchronized void release() {
if (fileLock != null) {
fileLock.writeObject(codesFilter);
fileLock.destroy();
fileLock = null;
}
}
private void generate() {
for (int i = 0; i < CACHE_CODES_NUM; ++i)
availableCodes.add(generateOne());
fileLock.writeObject(codesFilter);
}
private int generateOne() {
while (true) {
int code = secureRandom.nextInt(max(maxRandomSize));
boolean existed = contains(code);
code = !existed ? add(code) : tryFindAvailableCode(code);
if (code >= 0) return code;
init();
}
}
private int tryFindAvailableCode(int code) {
int next = codesFilter.nextClearBit(code);
if (next != -1 && next < max(maxRandomSize)) return add(next);
next = codesFilter.previousClearBit(code);
if (next != -1) return add(next);
return -1;
}
private int add(int code) {
codesFilter.set(code);
return code;
}
private boolean contains(int code) {
return codesFilter.get(code);
}
private int max(int size) {
switch (size) {
case 1: // fall through
case 2: // fall through
case 3: // fall through
case 4:
return 10000;
case 5:
return 100000;
case 6:
return 1000000;
case 7:
return 10000000;
case 8:
return 100000000;
case 9:
return 1000000000;
default:
return Integer.MAX_VALUE;
}
}
}

View File

@ -0,0 +1,205 @@
package org.n3r.idworker.strategy;
import org.n3r.idworker.WorkerIdStrategy;
import org.n3r.idworker.utils.HttpReq;
import org.n3r.idworker.utils.Ip;
import org.n3r.idworker.utils.Props;
import org.n3r.idworker.utils.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.Properties;
import java.util.Random;
public class DefaultWorkerIdStrategy implements WorkerIdStrategy {
static long workerIdBits = 10L;
static long maxWorkerId = -1L ^ (-1L << workerIdBits);
static Random random = new SecureRandom();
public static final WorkerIdStrategy instance = new DefaultWorkerIdStrategy();
private final Properties props =
Props.tryProperties("idworker-client.properties", Utils.DOT_IDWORKERS);
private final String idWorkerServerUrl =
props.getProperty("server.address", "http://id.worker.server:18001");
String userName = System.getProperty("user.name");
String ipDotUsername = Ip.ip + "." + userName;
String ipudotlock = ipDotUsername + ".lock.";
int workerIdIndex = ipudotlock.length();
long workerId;
FileLock fileLock;
Logger logger = LoggerFactory.getLogger(DefaultWorkerIdStrategy.class);
private boolean inited;
private void init() {
workerId = findAvailWorkerId();
if (workerId >= 0) {
destroyFileLockWhenShutdown();
startSyncThread();
} else {
syncWithWorkerIdServer();
workerId = findAvailWorkerId();
if (workerId < 0) workerId = increaseWithWorkerIdServer();
}
if (workerId < 0) workerId = tryToCreateOnIp();
if (workerId < 0) {
logger.warn("DANGEROUS!!! Try to use random worker id.");
workerId = tryToRandomOnIp(); // Try avoiding! it could cause duplicated
}
if (workerId < 0) {
logger.warn("the world may be ended!");
throw new RuntimeException("the world may be ended");
}
}
private void destroyFileLockWhenShutdown() {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
fileLock.destroy();
}
});
}
private void startSyncThread() {
new Thread() {
@Override
public void run() {
syncWithWorkerIdServer();
}
}.start();
}
private long increaseWithWorkerIdServer() {
String incId = HttpReq.get(idWorkerServerUrl)
.req("/inc")
.param("ipu", ipDotUsername)
.exec();
if (incId == null || incId.trim().isEmpty()) return -1L;
long lid = Long.parseLong(incId);
return checkAvail(lid);
}
private long tryToCreateOnIp() {
long wid = Ip.lip & maxWorkerId;
return checkAvail(wid);
}
private long tryToRandomOnIp() {
long avaiWorkerId = -1L;
long tryTimes = -1;
while (avaiWorkerId < 0 && ++tryTimes < maxWorkerId) {
long wid = Ip.lip & random.nextInt((int) maxWorkerId);
avaiWorkerId = checkAvail(wid);
}
return avaiWorkerId;
}
private long checkAvail(long wid) {
long availWorkerId = -1L;
try {
File idWorkerHome = Utils.createIdWorkerHome();
new File(idWorkerHome, ipudotlock + String.format("%04d", wid)).createNewFile();
availWorkerId = findAvailWorkerId();
} catch (IOException e) {
logger.warn("checkAvail error", e);
}
return availWorkerId;
}
private void syncWithWorkerIdServer() {
String syncIds = HttpReq.get(idWorkerServerUrl).req("/sync")
.param("ipu", ipDotUsername).param("ids", buildWorkerIdsOfCurrentIp())
.exec();
if (syncIds == null || syncIds.trim().isEmpty()) return;
String[] syncIdsArr = syncIds.split(",");
File idWorkerHome = Utils.createIdWorkerHome();
for (String syncId : syncIdsArr) {
try {
new File(idWorkerHome, ipudotlock + syncId).createNewFile();
} catch (IOException e) {
logger.warn("create workerid lock file error", e);
}
}
}
private String buildWorkerIdsOfCurrentIp() {
StringBuilder sb = new StringBuilder();
File idWorkerHome = Utils.createIdWorkerHome();
for (File lockFile : idWorkerHome.listFiles()) {
// check the format like 10.142.1.151.lock.0001
if (!lockFile.getName().startsWith(ipudotlock)) continue;
String workerId = lockFile.getName().substring(workerIdIndex);
if (!workerId.matches("\\d\\d\\d\\d")) continue;
if (sb.length() > 0) sb.append(',');
sb.append(workerId);
}
return sb.toString();
}
/**
* Find the local available worker id.
*
* @return -1 when N/A
*/
private long findAvailWorkerId() {
File idWorkerHome = Utils.createIdWorkerHome();
for (File lockFile : idWorkerHome.listFiles()) {
// check the format like 10.142.1.151.lock.0001
if (!lockFile.getName().startsWith(ipudotlock)) continue;
String workerId = lockFile.getName().substring(workerIdIndex);
if (!workerId.matches("\\d\\d\\d\\d")) continue;
FileLock fileLock = new FileLock(lockFile);
if (!fileLock.tryLock()) {
fileLock.destroy();
continue;
}
this.fileLock = fileLock;
return Long.parseLong(workerId);
}
return -1;
}
@Override
public void initialize() {
if (inited) return;
init();
this.inited = true;
}
@Override
public long availableWorkerId() {
return workerId;
}
@Override
public void release() {
if (fileLock != null) fileLock.destroy();
inited = false;
}
}

View File

@ -0,0 +1,132 @@
package org.n3r.idworker.strategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.nio.channels.Channels;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.FileChannel;
import java.nio.channels.OverlappingFileLockException;
/**
* A file lock a la flock/funlock
* <p/>
* The given path will be created and opened if it doesn't exist.
*/
public class FileLock {
private final File file;
private FileChannel channel;
private java.nio.channels.FileLock flock = null;
Logger logger = LoggerFactory.getLogger(FileLock.class);
public FileLock(File file) {
this.file = file;
try {
file.createNewFile(); // create the file if it doesn't exist
channel = new RandomAccessFile(file, "rw").getChannel();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* Lock the file or throw an exception if the lock is already held
*/
public void lock() {
try {
synchronized (this) {
logger.trace("Acquiring lock on {}", file.getAbsolutePath());
flock = channel.lock();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* Try to lock the file and return true if the locking succeeds
*/
public boolean tryLock() {
synchronized (this) {
logger.trace("Acquiring lock on {}", file.getAbsolutePath());
try {
// weirdly this method will return null if the lock is held by another
// process, but will throw an exception if the lock is held by this process
// so we have to handle both cases
flock = channel.tryLock();
return flock != null;
} catch (OverlappingFileLockException e) {
return false;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
/**
* Unlock the lock if it is held
*/
public void unlock() {
synchronized (this) {
logger.trace("Releasing lock on {}", file.getAbsolutePath());
if (flock == null) return;
try {
flock.release();
} catch (ClosedChannelException e) {
// Ignore
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
/**
* Destroy this lock, closing the associated FileChannel
*/
public void destroy() {
synchronized (this) {
unlock();
if (!channel.isOpen()) return;
try {
channel.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@SuppressWarnings("unchecked")
public <T> T readObject() {
try {
InputStream is = Channels.newInputStream(channel);
ObjectInputStream objectReader = new ObjectInputStream(is);
return (T) objectReader.readObject();
} catch (EOFException e) {
} catch (Exception e) {
throw new RuntimeException(e);
}
return null;
}
public synchronized boolean writeObject(Object object) {
if (!channel.isOpen()) return false;
try {
channel.position(0);
OutputStream out = Channels.newOutputStream(channel);
ObjectOutputStream objectOutput = new ObjectOutputStream(out);
objectOutput.writeObject(object);
return true;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@ -0,0 +1,113 @@
package org.n3r.idworker.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.*;
public class HttpReq {
private final String baseUrl;
private String req;
private StringBuilder params = new StringBuilder();
Logger logger = LoggerFactory.getLogger(HttpReq.class);
public HttpReq(String baseUrl) {
this.baseUrl = baseUrl;
}
public static HttpReq get(String baseUrl) {
return new HttpReq(baseUrl);
}
public HttpReq req(String req) {
this.req = req;
return this;
}
public HttpReq param(String name, String value) {
if (params.length() > 0) params.append('&');
try {
params.append(name).append('=').append(URLEncoder.encode(value, "UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
return this;
}
public String exec() {
HttpURLConnection http = null;
try {
http = (HttpURLConnection) new URL(baseUrl
+ (req == null ? "" : req)
+ (params.length() > 0 ? ("?" + params) : "")).openConnection();
http.setRequestProperty("Accept-Charset", "UTF-8");
HttpURLConnection.setFollowRedirects(false);
http.setConnectTimeout(5 * 1000);
http.setReadTimeout(5 * 1000);
http.connect();
int status = http.getResponseCode();
String charset = getCharset(http.getHeaderField("Content-Type"));
if (status == 200) {
return readResponseBody(http, charset);
} else {
logger.warn("non 200 respoonse :" + readErrorResponseBody(http, status, charset));
return null;
}
} catch (Exception e) {
logger.error("exec error {}", e.getMessage());
return null;
} finally {
if (http != null) http.disconnect();
}
}
private static String readErrorResponseBody(HttpURLConnection http, int status, String charset) throws IOException {
InputStream errorStream = http.getErrorStream();
if (errorStream != null) {
String error = toString(charset, errorStream);
return ("STATUS CODE =" + status + "\n\n" + error);
} else {
return ("STATUS CODE =" + status);
}
}
private static String readResponseBody(HttpURLConnection http, String charset) throws IOException {
InputStream inputStream = http.getInputStream();
return toString(charset, inputStream);
}
private static String toString(String charset, InputStream inputStream) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) != -1) {
baos.write(buffer, 0, length);
}
return new String(baos.toByteArray(), charset);
}
private static String getCharset(String contentType) {
if (contentType == null) return "UTF-8";
String charset = null;
for (String param : contentType.replace(" ", "").split(";")) {
if (param.startsWith("charset=")) {
charset = param.split("=", 2)[1];
break;
}
}
return charset == null ? "UTF-8" : charset;
}
}

View File

@ -0,0 +1,60 @@
package org.n3r.idworker.utils;
/**
* This utility provides methods to either convert an IPv4 address to its long format or a 32bit dotted format.
*
* @author Aion
* Created on 22/11/12
*/
public class IPv4Utils {
/**
* Returns the long format of the provided IP address.
*
* @param ipAddress the IP address
* @return the long format of <code>ipAddress</code>
* @throws IllegalArgumentException if <code>ipAddress</code> is invalid
*/
public static long toLong(String ipAddress) {
if (ipAddress == null || ipAddress.isEmpty()) {
throw new IllegalArgumentException("ip address cannot be null or empty");
}
String[] octets = ipAddress.split(java.util.regex.Pattern.quote("."));
if (octets.length != 4) {
throw new IllegalArgumentException("invalid ip address");
}
long ip = 0;
for (int i = 3; i >= 0; i--) {
long octet = Long.parseLong(octets[3 - i]);
if (octet > 255 || octet < 0) {
throw new IllegalArgumentException("invalid ip address");
}
ip |= octet << (i * 8);
}
return ip;
}
/**
* Returns the 32bit dotted format of the provided long ip.
*
* @param ip the long ip
* @return the 32bit dotted format of <code>ip</code>
* @throws IllegalArgumentException if <code>ip</code> is invalid
*/
public static String toString(long ip) {
// if ip is bigger than 255.255.255.255 or smaller than 0.0.0.0
if (ip > 4294967295l || ip < 0) {
throw new IllegalArgumentException("invalid ip");
}
StringBuilder ipAddress = new StringBuilder();
for (int i = 3; i >= 0; i--) {
int shift = i * 8;
ipAddress.append((ip & (0xff << shift)) >> shift);
if (i > 0) {
ipAddress.append(".");
}
}
return ipAddress.toString();
}
}

View File

@ -0,0 +1,50 @@
package org.n3r.idworker.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
public class Ip {
static Logger logger = LoggerFactory.getLogger(Ip.class);
public static String ip;
public static long lip;
static {
try {
InetAddress localHostLANAddress = getFirstNonLoopbackAddress();
ip = localHostLANAddress.getHostAddress();
byte[] address = localHostLANAddress.getAddress();
lip = ((address [0] & 0xFFL) << (3*8)) +
((address [1] & 0xFFL) << (2*8)) +
((address [2] & 0xFFL) << (1*8)) +
(address [3] & 0xFFL);
} catch (Exception e) {
logger.error("get ipv4 failed ", e);
}
}
private static InetAddress getFirstNonLoopbackAddress() throws SocketException {
Enumeration en = NetworkInterface.getNetworkInterfaces();
while (en.hasMoreElements()) {
NetworkInterface i = (NetworkInterface) en.nextElement();
for (Enumeration en2 = i.getInetAddresses(); en2.hasMoreElements(); ) {
InetAddress addr = (InetAddress) en2.nextElement();
if (addr.isLoopbackAddress()) continue;
if (addr instanceof Inet4Address) {
return addr;
}
}
}
return null;
}
}

View File

@ -0,0 +1,70 @@
package org.n3r.idworker.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.util.Properties;
import static java.io.File.separator;
import static org.n3r.idworker.utils.Serializes.closeQuietly;
public class Props {
static Logger log = LoggerFactory.getLogger(Props.class);
public static Properties tryProperties(String propertiesFileName, String userHomeBasePath) {
Properties properties = new Properties();
InputStream is = null;
try {
is = Props.tryResource(propertiesFileName, userHomeBasePath, Silent.ON);
if (is != null) properties.load(is);
} catch (IOException e) {
log.error("load properties error: {}", e.getMessage());
} finally {
closeQuietly(is);
}
return properties;
}
enum Silent {ON, OFF}
public static InputStream tryResource(String propertiesFileName, String userHomeBasePath, Silent silent) {
InputStream is = currentDirResource(new File(propertiesFileName));
if (is != null) return is;
is = userHomeResource(propertiesFileName, userHomeBasePath);
if (is != null) return is;
is = classpathResource(propertiesFileName);
if (is != null || silent == Silent.ON) return is;
throw new RuntimeException("fail to find " + propertiesFileName + " in current dir or classpath");
}
private static InputStream userHomeResource(String pathname, String appHome) {
String filePath = System.getProperty("user.home") + separator + appHome;
File dir = new File(filePath);
if (!dir.exists()) return null;
return currentDirResource(new File(dir, pathname));
}
private static InputStream currentDirResource(File file) {
if (!file.exists()) return null;
try {
return new FileInputStream(file);
} catch (FileNotFoundException e) {
// This should not happened
log.error("read file {} error", file, e);
return null;
}
}
public static InputStream classpathResource(String resourceName) {
return Props.class.getClassLoader().getResourceAsStream(resourceName);
}
}

View File

@ -0,0 +1,118 @@
package org.n3r.idworker.utils;
import java.io.*;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.List;
public class Serializes {
@SuppressWarnings("unchecked")
public static <T> List<T> readObjects(File file) {
ArrayList<T> objects = new ArrayList<T>();
ObjectInputStream objectReader = null;
FileInputStream fis = null;
try {
fis = new FileInputStream(file);
objectReader = new ObjectInputStream(fis);
while (true)
objects.add((T) objectReader.readObject());
} catch (EOFException e) {
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
closeQuietly(objectReader);
closeQuietly(fis);
}
return objects;
}
@SuppressWarnings("unchecked")
public static <T> T readObject(File file) {
ObjectInputStream objectReader = null;
FileInputStream fis = null;
try {
fis = new FileInputStream(file);
objectReader = new ObjectInputStream(fis);
return (T) objectReader.readObject();
} catch (EOFException e) {
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
closeQuietly(objectReader);
closeQuietly(fis);
}
return null;
}
public static void writeObject(File file, Object object) {
ObjectOutputStream objectOutput = null;
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file);
objectOutput = new ObjectOutputStream(fos);
objectOutput.writeObject(object);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
closeQuietly(objectOutput);
closeQuietly(fos);
}
}
public static void writeObject(FileOutputStream fos, Object object) {
FileChannel channel = fos.getChannel();
if (!channel.isOpen()) throw new RuntimeException("channel is closed");
try {
channel.position(0);
ObjectOutputStream objectOutput = new ObjectOutputStream(fos);
objectOutput.writeObject(object);
fos.flush();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
}
}
public static void writeObjects(File file, Object... objects) {
ObjectOutputStream objectOutput = null;
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file);
objectOutput = new ObjectOutputStream(fos);
for (Object object : objects)
objectOutput.writeObject(object);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
closeQuietly(objectOutput);
closeQuietly(fos);
}
}
public static void closeQuietly(OutputStream os) {
if (os != null) try {
os.close();
} catch (IOException e) {
// ignore
}
}
public static void closeQuietly(InputStream is) {
if (is != null) try {
is.close();
} catch (IOException e) {
// ignore
}
}
}

View File

@ -0,0 +1,114 @@
package org.n3r.idworker.utils;
import java.io.*;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Calendar;
public class Utils {
public static final String DOT_IDWORKERS = ".idworkers";
public static ClassLoader getClassLoader() {
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
return contextClassLoader != null ? contextClassLoader : Utils.class.getClassLoader();
}
public static InputStream classResourceToStream(String resourceName) {
return getClassLoader().getResourceAsStream(resourceName);
}
public static String firstLine(String classResourceName) {
InputStream inputStream = null;
try {
inputStream = classResourceToStream(classResourceName);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
return bufferedReader.readLine();
} catch (IOException e) {
return null;
} finally {
if (inputStream != null) try {
inputStream.close();
} catch (IOException e) {
// ignore
}
}
}
public static String checkNotEmpty(String param, String name) {
if (param == null || param.isEmpty())
throw new IllegalArgumentException(name + " is empty");
return param;
}
public static long midnightMillis() {
// today
Calendar date = Calendar.getInstance();
// reset hour, minutes, seconds and millis
date.set(Calendar.HOUR_OF_DAY, 0);
date.set(Calendar.MINUTE, 0);
date.set(Calendar.SECOND, 0);
date.set(Calendar.MILLISECOND, 0);
return date.getTimeInMillis();
}
public static void main(String[] args) {
// 2013-12-25 00:00:00.000
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Timestamp(midnightMillis())));
System.out.println(encode(281474976710655L));
}
public static long decode(String s, String symbols) {
final int B = symbols.length();
long num = 0;
for (char ch : s.toCharArray()) {
num *= B;
num += symbols.indexOf(ch);
}
return num;
}
public static String encode(long num) {
return encode(num, defaultRange);
}
public static String encode(long num, String symbols) {
final int B = symbols.length();
StringBuilder sb = new StringBuilder();
while (num != 0) {
sb.append(symbols.charAt((int) (num % B)));
num /= B;
}
return sb.reverse().toString();
}
// all un-clearly-recognized letters are skiped.
static String defaultRange = "0123456789ABCDFGHKMNPRSTWXYZ";
public static String padLeft(String str, int size, char padChar) {
if (str.length() >= size) return str;
StringBuilder s = new StringBuilder();
for (int i = size - str.length(); i > 0; --i) {
s.append(padChar);
}
s.append(str);
return s.toString();
}
public static File createIdWorkerHome() {
String userHome = System.getProperty("user.home");
File idWorkerHome = new File(userHome + File.separator + DOT_IDWORKERS);
idWorkerHome.mkdirs();
if (idWorkerHome.isDirectory()) return idWorkerHome;
throw new RuntimeException("failed to create .idworkers at user home");
}
}