2 changed files with 227 additions and 11 deletions
@ -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