2 changed files with 227 additions and 11 deletions
@ -0,0 +1,216 @@
@@ -0,0 +1,216 @@
|
||||
/* |
||||
* Copyright 1999-2019 Seata.io Group. |
||||
* |
||||
* 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 cc.niushuai.dididone.util; |
||||
|
||||
import java.net.NetworkInterface; |
||||
import java.util.Enumeration; |
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.Random; |
||||
import java.util.concurrent.atomic.AtomicLong; |
||||
import java.util.stream.Collectors; |
||||
|
||||
/** |
||||
* @author funkye |
||||
* @author selfishlover |
||||
*/ |
||||
public class IntSnowflake { |
||||
private static final IntSnowflake snowflake = new IntSnowflake(0L); |
||||
|
||||
public static int next_id() { |
||||
return (int) snowflake.nextId(); |
||||
} |
||||
|
||||
public static String next_id_str() { |
||||
return snowflake.nextId() + ""; |
||||
} |
||||
|
||||
/** |
||||
* Start time cut (2020-05-03) |
||||
*/ |
||||
private final long twepoch = 1588435200000L; |
||||
|
||||
/** |
||||
* The number of bits occupied by workerId |
||||
*/ |
||||
private final int workerIdBits = 8; |
||||
|
||||
/** |
||||
* The number of bits occupied by timestamp |
||||
*/ |
||||
private final int timestampBits = 12; |
||||
|
||||
/** |
||||
* The number of bits occupied by sequence |
||||
*/ |
||||
private final int sequenceBits = 12; |
||||
|
||||
/** |
||||
* Maximum supported machine id, the result is 1023 |
||||
*/ |
||||
private final int maxWorkerId = ~(-1 << workerIdBits); |
||||
/** |
||||
* mask that help to extract timestamp and sequence from a long |
||||
*/ |
||||
private final long timestampAndSequenceMask = ~(-1L << (timestampBits + sequenceBits)); |
||||
/** |
||||
* business meaning: machine ID (0 ~ 1023) |
||||
* actual layout in memory: |
||||
* highest 1 bit: 0 |
||||
* middle 10 bit: workerId |
||||
* lowest 53 bit: all 0 |
||||
*/ |
||||
private long workerId; |
||||
/** |
||||
* timestamp and sequence mix in one Long |
||||
* highest 11 bit: not used |
||||
* middle 41 bit: timestamp |
||||
* lowest 12 bit: sequence |
||||
*/ |
||||
private AtomicLong timestampAndSequence; |
||||
|
||||
/** |
||||
* instantiate an IdWorker using given workerId |
||||
* |
||||
* @param workerId if null, then will auto assign one |
||||
*/ |
||||
public IntSnowflake(Long workerId) { |
||||
initTimestampAndSequence(); |
||||
initWorkerId(workerId); |
||||
} |
||||
|
||||
/** |
||||
* init first timestamp and sequence immediately |
||||
*/ |
||||
protected void initTimestampAndSequence() { |
||||
long timestamp = getNewestTimestamp(); |
||||
long timestampWithSequence = timestamp << sequenceBits; |
||||
this.timestampAndSequence = new AtomicLong(timestampWithSequence); |
||||
} |
||||
|
||||
/** |
||||
* init workerId |
||||
* |
||||
* @param workerId if null, then auto generate one |
||||
*/ |
||||
protected void initWorkerId(Long workerId) { |
||||
if (workerId == null) { |
||||
workerId = generateWorkerId(); |
||||
} |
||||
if (workerId > maxWorkerId || workerId < 0) { |
||||
String message = String.format("worker Id can't be greater than %d or less than 0", maxWorkerId); |
||||
throw new IllegalArgumentException(message); |
||||
} |
||||
this.workerId = workerId << (timestampBits + sequenceBits); |
||||
} |
||||
|
||||
/** |
||||
* get next UUID(base on snowflake algorithm), which look like: |
||||
* highest 1 bit: always 0 |
||||
* next 10 bit: workerId |
||||
* next 41 bit: timestamp |
||||
* lowest 12 bit: sequence |
||||
* |
||||
* @return UUID |
||||
*/ |
||||
public long nextId() { |
||||
waitIfNecessary(); |
||||
long next = timestampAndSequence.incrementAndGet(); |
||||
long timestampWithSequence = next & timestampAndSequenceMask; |
||||
return workerId | timestampWithSequence; |
||||
} |
||||
|
||||
/** |
||||
* block current thread if the QPS of acquiring UUID is too high |
||||
* that current sequence space is exhausted |
||||
*/ |
||||
private void waitIfNecessary() { |
||||
long currentWithSequence = timestampAndSequence.get(); |
||||
long current = currentWithSequence >>> sequenceBits; |
||||
long newest = getNewestTimestamp(); |
||||
if (current >= newest) { |
||||
try { |
||||
Thread.sleep(5); |
||||
} catch (InterruptedException ignore) { |
||||
// don't care
|
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* get newest timestamp relative to twepoch |
||||
*/ |
||||
private long getNewestTimestamp() { |
||||
return System.currentTimeMillis() - twepoch; |
||||
} |
||||
|
||||
/** |
||||
* auto generate workerId, try using mac first, if failed, then randomly generate one |
||||
* |
||||
* @return workerId |
||||
*/ |
||||
private long generateWorkerId() { |
||||
try { |
||||
return generateWorkerIdBaseOnMac(); |
||||
} catch (Exception e) { |
||||
return generateRandomWorkerId(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* use lowest 10 bit of available MAC as workerId |
||||
* |
||||
* @return workerId |
||||
* @throws Exception when there is no available mac found |
||||
*/ |
||||
private long generateWorkerIdBaseOnMac() throws Exception { |
||||
Enumeration<NetworkInterface> all = NetworkInterface.getNetworkInterfaces(); |
||||
while (all.hasMoreElements()) { |
||||
NetworkInterface networkInterface = all.nextElement(); |
||||
boolean isLoopback = networkInterface.isLoopback(); |
||||
boolean isVirtual = networkInterface.isVirtual(); |
||||
if (isLoopback || isVirtual) { |
||||
continue; |
||||
} |
||||
byte[] mac = networkInterface.getHardwareAddress(); |
||||
return ((mac[4] & 0B11) << 8) | (mac[5] & 0xFF); |
||||
} |
||||
throw new RuntimeException("no available mac found"); |
||||
} |
||||
|
||||
/** |
||||
* randomly generate one as workerId |
||||
* |
||||
* @return workerId |
||||
*/ |
||||
private long generateRandomWorkerId() { |
||||
return new Random().nextInt(maxWorkerId + 1); |
||||
} |
||||
|
||||
|
||||
public static void main(String[] args) { |
||||
|
||||
Map<Integer, Integer> map = new HashMap<>(); |
||||
for (int i = 0; i < 100000000; i++) { |
||||
long id = next_id(); |
||||
map.put((int) id, (map.getOrDefault(id, 0) + 1)); |
||||
} |
||||
List<Integer> collect = map.values().stream().filter(item -> item > 0).collect(Collectors.toList()); |
||||
System.out.println(map.size()); |
||||
System.out.println(collect); |
||||
} |
||||
} |
Loading…
Reference in new issue