blob: 1357a60083f008b6d03357b7062cf28bfaccb3a8 [file] [log] [blame]
/*
* 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
*
* 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.
*/
use std::io::Write;
use std::process;
use std::time::SystemTime;
use byteorder::{BigEndian, WriteBytesExt};
use once_cell::sync::Lazy;
use parking_lot::Mutex;
use time::{Date, OffsetDateTime, PrimitiveDateTime, Time};
/**
* The codec for the message-id.
*
* <p>Codec here provides the following two functions:
* 1. Provide decoding function of message-id of all versions above v0.
* 2. Provide a generator of message-id of v1 version.
*
* <p>The message-id of versions above V1 consists of 17 bytes in total. The first two bytes represent the version
* number. For V1, these two bytes are 0x0001.
*
* <h3>V1 message id example</h3>
*
* <pre>
* ┌──┬────────────┬────┬────────┬────────┐
* │01│56F7E71C361B│21BC│024CCDBE│00000000│
* └──┴────────────┴────┴────────┴────────┘
* </pre>
*
* <h3>V1 version message id generation rules</h3>
*
* <pre>
* process id(lower 2bytes)
* ▲
* mac address(lower 6bytes) │ sequence number(big endian)
* ▲ │ ▲ (4bytes)
* │ │ │
* ┌─────┴─────┐ ┌┴┐ ┌───┐ ┌─┴─┐
* 0x01+ │ 6 │ │2│ │ 4 │ │ 4 │
* └───────────┘ └─┘ └─┬─┘ └───┘
* │
* ▼
* seconds since 2021-01-01 00:00:00(UTC+0)
* (lower 4bytes)
* </pre>
*/
// inspired by https://github.com/messense/rocketmq-rs
pub(crate) static UNIQ_ID_GENERATOR: Lazy<Mutex<UniqueIdGenerator>> = Lazy::new(|| {
let mut wtr = Vec::new();
wtr.write_u8(1).unwrap();
// mac
let x = mac_address::get_mac_address().unwrap();
let ma = match x {
Some(ma) => ma,
None => {
panic!("mac address is none")
}
};
wtr.write_all(&ma.bytes()).unwrap();
// process id
wtr.write_u16::<BigEndian>(process::id() as u16).unwrap();
let generator = UniqueIdGenerator {
counter: 0,
start_timestamp: 0,
next_timestamp: 0,
prefix: hex::encode_upper(wtr),
};
Mutex::new(generator)
});
pub struct UniqueIdGenerator {
counter: i16,
prefix: String,
start_timestamp: i64,
next_timestamp: i64,
}
impl UniqueIdGenerator {
pub fn next_id(&mut self) -> String {
if SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs() as i64
> self.next_timestamp
{
// update timestamp
let now = OffsetDateTime::now_utc();
let year = now.year();
let month = now.month();
self.start_timestamp = PrimitiveDateTime::new(
Date::from_calendar_date(year, month, 1).unwrap(),
Time::from_hms(0, 0, 0).unwrap(),
)
.assume_offset(now.offset())
.unix_timestamp();
self.next_timestamp = (PrimitiveDateTime::new(
Date::from_calendar_date(year, month, 1).unwrap(),
Time::from_hms(0, 0, 0).unwrap(),
)
.assume_offset(now.offset())
+ time::Duration::days(30))
.unix_timestamp();
}
self.counter = self.counter.wrapping_add(1);
let mut buf = Vec::new();
buf.write_i32::<BigEndian>(
((OffsetDateTime::now_utc().unix_timestamp() - self.start_timestamp) * 1000) as i32,
)
.unwrap();
buf.write_i16::<BigEndian>(self.counter).unwrap();
self.prefix.clone() + &hex::encode(buf)
}
}
#[cfg(test)]
mod test {
#[test]
fn text_generate_uniq_id() {
use super::UNIQ_ID_GENERATOR;
for i in 0..10 {
let uid = UNIQ_ID_GENERATOR.lock().next_id();
println!("i: {}, uid: {}", i, uid);
}
}
}