基於redis實現分布式id


基於訂單生成規則,期望是以年與日+隨機串進行生成規則,因為分布式,所以對自增id場景需要,所以存在分布式自增id場景

直接上代碼

@Slf4j
@Service
public class CommonLocalCacheService {
    @Autowired
    private RedisTemplate redisTemplate;
    /**
     * 鎖對象
     */
    private static final Object LOCK = new Object();

    /**
     * 本地緩存redis自增序列,例如:key:value=20211104:【1-10000】
     */
    private static final Cache<String, Optional<RedisSequence>> REDIS_SEQUENCE_CACHE = CacheBuilder.newBuilder()
            .expireAfterWrite(1, TimeUnit.DAYS)
            .build();

    /**
     * 獲取redis自增Key,
     * 分布式id生成規則:
     * 根據日期 : 年+月+日 + 3位隨機字符串 + 7位redis自增key
     * 取redis key 為   20211109,每次取10000,本地緩存下來,后續如果達到最大值,網上遞增10000
     * 【1-10000】   20211109xxx0000001
     */
    @SneakyThrows
    public String generatorId() {
        String sequenceKey = DateUtils.parseTime(LocalDateTime.now(), DateUtils.TimeFormat.SHORT_DATE_PATTERN_NONE);
        StringBuilder orderId = new StringBuilder(sequenceKey);
        // 如果達到最大批次,換一批
        synchronized (LOCK) {
            //緩存時長為當前時間+1天
            Optional<RedisSequence> redisSequenceOptional = getLocalRedisSequence(sequenceKey, 10000, DateUtils.addDays(DateUtils.now(), 1));
            // 如果緩存為空,則redis掛了,隨機字段
            if (!redisSequenceOptional.isPresent()) {
                for (int i = 0; i < 10; i++) {
                    orderId.append(RandomUtils.nextInt(0, 10));
                }
                return orderId.toString();
            }
            RedisSequence redisSequence = redisSequenceOptional.get();
            if (redisSequence.getStartSequence().get() == redisSequence.getEndSequence().get()) {
                //如果值相等則失效,重新獲取
                log.info("----------------------------invalidate---------start:{}, end:{}", redisSequence.getStartSequence(), redisSequence.getEndSequence());
                REDIS_SEQUENCE_CACHE.invalidate(sequenceKey);
                return generatorId();
            }
            redisSequence.getStartSequence().getAndAdd(1L);
            REDIS_SEQUENCE_CACHE.put(sequenceKey, Optional.of(redisSequence));
            String sequence = String.format("%1$07d", redisSequence.getStartSequence().get());
            //中間3位隨機數
            for (int i = 0; i < 3; i++) {
                orderId.append(RandomUtils.nextInt(0, 10));
            }
            orderId.append(sequence);
        }
        log.info("orderId is {}", orderId.toString());
        return orderId.toString();
    }

    /**
     * 從本地緩存中取redisSequence
     *
     * @param sequenceKey
     * @return
     */
    private Optional<RedisSequence> getLocalRedisSequence(String sequenceKey, int increment, Date expireTime) {
        try {
            return REDIS_SEQUENCE_CACHE.get(sequenceKey, () -> {
                RedisAtomicLong counter = new RedisAtomicLong(RedisConstants.GOODS_GENERATOR_ORDER_ID.concat(sequenceKey), redisTemplate.getConnectionFactory());
                counter.expireAt(expireTime);
                long end = counter.addAndGet(increment);
                RedisSequence redisSequence = RedisSequence.builder().startSequence(new AtomicLong(end-increment)).endSequence(new AtomicLong(end)).build();
                log.info("----------------------------get---------start:{}, end:{}", redisSequence.getStartSequence(), redisSequence.getEndSequence());
                return Optional.of(redisSequence);
            });
        } catch (Exception e) {
            log.error("getLocalRedisSequence is errored, sequenceKey:{}, increment:{}", sequenceKey, increment, e);
        }
        return Optional.empty();
    }

}

 

//日期工具類
public class DateUtils extends org.apache.commons.lang3.time.DateUtils {

    private static final String YEAR_TO_SECOND_PATTERN = "yyyyMMddHHmmss";
    /**
     * 獲取默認時間格式: yyyy-MM-dd HH:mm:ss
     */
    private static final DateTimeFormatter DEFAULT_DATETIME_FORMATTER = TimeFormat.LONG_DATE_PATTERN_LINE.formatter;

    private DateUtils() {
        //工具類不應該有可用的構造方法
    }

    /**
     * 計算指定日期的生日
     *
     * @param credentialNo 身份證號
     * @param someDay      指定日期
     * @return 歲數
     */
    public static int getAge(String credentialNo, LocalDate someDay) {
        LocalDate birthDate = getBirthDay(credentialNo);
        return Period.between(birthDate, someDay).getYears();
    }

    /**
     * 計算當前年齡
     * @param credentialNo 身份證號
     * @return 歲數
     */
    public static int getAge(String credentialNo) {
        LocalDate birthDate = getBirthDay(credentialNo);
        return Period.between(birthDate, LocalDate.now()).getYears();
    }

    /**
     * 計算今天的生日
     *
     * @param credentialNo 身份證號
     * @return 歲數
     */
    public static int getAgeToday(String credentialNo) {
        LocalDate birthDate = getBirthDay(credentialNo);
        return Period.between(birthDate, LocalDate.now()).getYears();
    }

    /**
     * 計算明天的生日
     *
     * @param credentialNo 身份證號
     * @return 歲數
     */
    public static int getAgeTomorrow(String credentialNo) {
        LocalDate birthDate = getBirthDay(credentialNo);
        return Period.between(birthDate, LocalDate.now().plusDays(1)).getYears();
    }

    private static LocalDate getBirthDay(String credentialNo) {
        String birthdayStr;
        if (credentialNo.length() == 15) {
            birthdayStr = "19" + credentialNo.substring(6, 12);
        } else if (credentialNo.length() == 18) {
            birthdayStr = credentialNo.substring(6, 14);
        } else {
            throw new IllegalArgumentException("credentialNo's length is not 15 or 18!");
        }
        return LocalDate.parse(birthdayStr, DateTimeFormatter.ofPattern("yyyyMMdd"));
    }

    public static Date now() {
        return new Date();
    }

    public static Long stringToEpochSecond(String dateStr) {
        return parseDate(dateStr).toInstant().getEpochSecond();
    }

    /**
     * 格式化日期: 精確到秒
     */
    public static String getCurrentTimeStr() {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(YEAR_TO_SECOND_PATTERN);
        return LocalDateTime.now().format(formatter);
    }

    /**
     * 格式化日期: 精確到日
     */
    public static String formatDateStr(Date date) {
        return DateFormatUtils.format(date, "yyyyMMdd");
    }

    /**
     * 解析日期yyyyMMddHHmmss為LocalDateTime
     */
    public static LocalDateTime parseLocalDateTime(String dateStr) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(YEAR_TO_SECOND_PATTERN);
        return LocalDateTime.parse(dateStr, formatter);
    }

    /**
     * 解析日期yyyyMMddHHmmss為Date
     */
    @SneakyThrows
    public static Date parseDate(String dateStr) {
        return org.apache.commons.lang3.time.DateUtils.parseDate(dateStr, YEAR_TO_SECOND_PATTERN);
    }

    /**
     * 格式化為db格式: 20170911000000 -> 2017-09-11 00:00:00
     */
    @SneakyThrows
    public static String formatDbDateStr(String dateTime) {
        Date date = org.apache.commons.lang3.time.DateUtils.parseDate(dateTime, YEAR_TO_SECOND_PATTERN);
        return DateFormatUtils.format(date, "yyyy-MM-dd HH:mm:ss");
    }

    public static String getYearAndMonth(Date date) {
        return DateFormatUtils.format(date, "yyyyMM");
    }

    @SneakyThrows
    public static String formatChineseDateStr(String dateTime) {
        Date date = parseDate(dateTime);
        return DateFormatUtils.format(date, "yyyy年MM月dd日");
    }

    public static Long utcFromDate(String dateTime, String... dateFormat) {
        String defaultDateFormat = YEAR_TO_SECOND_PATTERN;
        if (dateFormat != null && dateFormat.length > 0) {
            defaultDateFormat = dateFormat[0];
        }
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(defaultDateFormat);
        return LocalDateTime.parse(dateTime, formatter).atZone(ZoneId.systemDefault()).toEpochSecond();
    }

    /**
     * String 轉時間
     *
     * @param timeStr
     * @return
     */
    public static LocalDateTime parseTime(String timeStr) {
        return LocalDateTime.parse(timeStr, DEFAULT_DATETIME_FORMATTER);
    }

    /**
     * String 轉時間
     *
     * @param timeStr
     * @param format  時間格式
     * @return
     */
    public static LocalDateTime parseTime(String timeStr, TimeFormat format) {
        return LocalDateTime.parse(timeStr, format.formatter);
    }

    /**
     * 判斷日期是否今天
     * @param date
     * @return
     */
    public static boolean isToday(Date date){
        return dateToLocalDateTime(date).isAfter(dateToLocalDateTime(localDateToDate(LocalDate.now())));
    }



    /**
     * 時間轉 String
     *
     * @param time
     * @return
     */
    public static String parseTime(LocalDateTime time) {
        return DEFAULT_DATETIME_FORMATTER.format(time);
    }

    /**
     * 時間轉 String
     *
     * @param time
     * @param format 時間格式
     * @return
     */
    public static String parseTime(LocalDateTime time, TimeFormat format) {
        return format.formatter.format(time);
    }

    /**
     * 獲取當前時間
     *
     * @return
     */
    public static String getCurrentDateTime() {
        return DEFAULT_DATETIME_FORMATTER.format(LocalDateTime.now());
    }

    /**
     * 獲取當前時間
     *
     * @param format 時間格式
     * @return
     */
    public static String getCurrentDateTime(TimeFormat format) {
        return format.formatter.format(LocalDateTime.now());
    }


    /**
     * 當天時間起始點.
     *
     * @param date the date
     * @return the start date
     */
    public static LocalDateTime getStartDate(LocalDateTime date) {
        return string2LocalDateTime(date2String(date, TimeFormat.LONG_DATE_BEGIN_PATTERN_LINE));
    }

    /**
     * 當天時間終點.
     *
     * @param date the date
     * @return the end date
     */
    public static LocalDateTime getEndDate(LocalDateTime date) {
        return string2LocalDateTime(date2String(date, TimeFormat.LONG_DATE_END_PATTERN_LINE));
    }

    /**
     * String 轉時間
     * @param timeStr
     * @return
     */
    public static LocalDateTime string2LocalDateTime(String timeStr) {
        return LocalDateTime.parse(timeStr, DEFAULT_DATETIME_FORMATTER);
    }

    /**
     * String 轉時間
     * @param timeStr
     * @return
     */
    public static LocalDate string2LocalDate(String timeStr, TimeFormat timeFormat) {
        return LocalDate.parse(timeStr, timeFormat.formatter);
    }

    /**
     * String 轉時間
     *
     * @param timeStr
     * @param format  時間格式
     * @return
     */
    public static LocalDateTime string2LocalDateTime(String timeStr, TimeFormat format) {
        return LocalDateTime.parse(timeStr, format.formatter);
    }

    // 01. java.util.Date --> java.time.LocalDateTime
    public static LocalDateTime dateToLocalDateTime(Date date) {
        Instant instant = date.toInstant();
        ZoneId zone = ZoneId.systemDefault();
        return LocalDateTime.ofInstant(instant, zone);
    }


    // 02. java.util.Date --> java.time.LocalDate
    public static LocalDate dateToLocalDate(Date date) {
        Instant instant = date.toInstant();
        ZoneId zone = ZoneId.systemDefault();
        LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
        return localDateTime.toLocalDate();
    }

    // 03. java.util.Date --> java.time.LocalTime
    public static LocalTime dateToLocalTime(Date date) {
        Instant instant = date.toInstant();
        ZoneId zone = ZoneId.systemDefault();
        LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
        return localDateTime.toLocalTime();
    }


    // 04. java.time.LocalDateTime --> java.util.Date
    public static Date localDateTimeToDate(LocalDateTime localDateTime) {
        ZoneId zone = ZoneId.systemDefault();
        Instant instant = localDateTime.atZone(zone).toInstant();
        return Date.from(instant);
    }


    // 05. java.time.LocalDate --> java.util.Date
    public static Date localDateToDate(LocalDate localDate) {
        ZoneId zone = ZoneId.systemDefault();
        Instant instant = localDate.atStartOfDay().atZone(zone).toInstant();
        return Date.from(instant);
    }

    // 06. java.time.LocalTime --> java.util.Date
    public static Date LocalTimeToDate(LocalDate localDate, LocalTime localTime) {
        LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime);
        ZoneId zone = ZoneId.systemDefault();
        Instant instant = localDateTime.atZone(zone).toInstant();
        return Date.from(instant);
    }

    /**
     * 時間轉 String
     *
     * @param time
     * @return
     */
    public static String date2String(LocalDateTime time) {
        return DEFAULT_DATETIME_FORMATTER.format(time);
    }

    /**
     * 時間轉 String
     *
     * @param time
     * @param format 時間格式
     * @return
     */
    public static String date2String(LocalDateTime time, TimeFormat format) {
        return format.formatter.format(time);
    }


    /**
     * 時間格式
     */
    public enum TimeFormat {

        /**
         * 短時間格式
         */
        SHORT_DATE_PATTERN_CHINA("yyyy年MM月dd日"),
        SHORT_DATE_PATTERN_LINE("yyyy-MM-dd"),
        SHORT_DATE_PATTERN_SLASH("yyyy/MM/dd"),
        SHORT_DATE_PATTERN_DOUBLE_SLASH("yyyy\\MM\\dd"),
        SHORT_DATE_PATTERN_NONE("yyyyMMdd"),

        /**
         * 長時間格式
         */
        LONG_DATE_BEGIN_PATTERN_LINE("yyyy-MM-dd 00:00:00"),
        LONG_DATE_END_PATTERN_LINE("yyyy-MM-dd 23:59:59"),
        LONG_DATE_PATTERN_LINE("yyyy-MM-dd HH:mm:ss"),
        LONG_DATE_PATTERN_SLASH("yyyy/MM/dd HH:mm:ss"),
        LONG_DATE_PATTERN_DOUBLE_SLASH("yyyy\\MM\\dd HH:mm:ss"),
        LONG_DATE_PATTERN_NONE("yyyyMMdd HH:mm:ss"),
        LONG_DATE_PATTERN_ALL_NONE("yyyyMMddHHmmss"),
        LONG_DATE_PATTERN_CHINA("yyyy年MM月dd日 HH:mm:ss"),
        LONG_DATE_BEGIN_PATTERN_CHINA("yyyy年MM月dd日 00:00:00"),

        /**
         * 長時間格式 帶毫秒
         */
        LONG_DATE_PATTERN_WITH_MILSEC_LINE("yyyy-MM-dd HH:mm:ss.SSS"),
        LONG_DATE_PATTERN_WITH_MILSEC_SLASH("yyyy/MM/dd HH:mm:ss.SSS"),
        LONG_DATE_PATTERN_WITH_MILSEC_DOUBLE_SLASH("yyyy\\MM\\dd HH:mm:ss.SSS"),
        LONG_DATE_PATTERN_WITH_MILSEC_NONE("yyyyMMdd HH:mm:ss.SSS");

        public transient DateTimeFormatter formatter;

        TimeFormat(String pattern) {
            formatter = DateTimeFormatter.ofPattern(pattern);
        }
    }

    /**
     * 獲取指定date的后一天作為保單生效時間
     * @param date 一般是投保日
     * @return
     */
    @SneakyThrows
    public static Date getNextDateAsEffectiveTime(Date date){
        return org.apache.commons.lang3.time.DateUtils.truncate(org.apache.commons.lang3.time.DateUtils.addDays(date, 1), Calendar.DATE);
    }


    /**
     * 獲取下一年的日期作為保單失效時間
     * @param date 一般是保單生效日 20190528000000
     * @return 20200527235959
     */
    @SneakyThrows
    public static Date getNextYearAsExpiredTime(Date date){
        date = addYears(date, 1);
        date = truncate(date, Calendar.DATE);
        return addSeconds(date, -1);
    }

    /**
     * 獲取指定date的作為保單生效時間
     * @param date 格式化date的時分秒為00:00:00
     * @return
     */
    @SneakyThrows
    public static Date getEffectiveTimeByDate(Date date){
        return org.apache.commons.lang3.time.DateUtils.truncate(date, Calendar.DATE);
    }

    /**
     * 判斷當前時間是否處於兩個時間中間,不判斷beginTime和endTime邏輯大小
     *
     * @param beginTime 開始時間
     * @param endTime   結束時間
     * @return true or false
     */
    public static boolean isBetween(Date beginTime, Date endTime) {
        Date now = new Date();
        if (now.after(beginTime) && now.before(endTime)) {
            return true;
        }
        return false;
    }
}

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM