Java--Spring项目生成雪花算法数字(Twitter SnowFlake)

文章目录

        • 前言
        • 步骤
        • 查看结果

前言
  • 分布式系统常需要全局唯一的数字作为id,且该id要求有序,twitter的SnowFlake解决了这种需求,生成了符合条件的这种数字,本文将提供一个接口获取雪花算法数字。以下为代码。
步骤
  1. SnowFlakeUtils 雪花算法工具类。

    @Slf4j
    public class SnowFlakeUtils {
    
        private static final RedisOperation REDIS_OPERATION = ApplicationContextHelper.getBean(RedisOperation.class);
    
        private static final String LOCAL_IP = getLocalIp();
    
        private static volatile SnowFlakeUtils instance;
        /**
         * 该任务开始时间,必须手动设置(差值的唯一性)
         * 建议在生产部署时选择某一日的0时0分0秒0毫秒的时间戳,方便计算
         */
        private static final long START_TIME = 1588733692671L;
        /**
         * 各个位的位数,Timestamp为41L(无需定义)
         */
        private static final long DATA_CENTER_ID_BITS = 5L;
        private static final long WORKER_ID_BITS = 1L;
        private static final long SEQUENCE_BITS = 16L;
        /**
         * 各位的最大值
         */
        private static final long DATA_CENTER_ID_MAX = ~(-1 << DATA_CENTER_ID_BITS);
        private static final long WORKER_ID_MAX = ~(-1 << WORKER_ID_BITS);
        private static final long SEQUENCE_MAX = ~(-1 << SEQUENCE_BITS);
        /**
         * 各位应该向左移动位数
         */
        private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATA_CENTER_ID_BITS;
        private static final long DATA_CENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
        private static final long WORKER_ID_SHIFT = SEQUENCE_BITS;
        /**
         * 数据中心ID
         */
        private final long dataCenterId;
        private static final String DATA_CENTER_ID = "DATACENTERID";
        /**
         * 工作线程ID
         */
        private final long workerId;
        private static final String WORKER_ID = "WORKERID";
        /**
         * 序列号
         */
        private long sequence = 0L;
        /**
         * 上次时间(保证不回退)
         */
        private long lastTimestamp = -1L;
        /***
         * 是否在高并发下
         */
        private boolean isClock = false;
    
        public static SnowFlakeUtils getInstance() {
            if (instance == null) {
                synchronized (SnowFlakeUtils.class) {
                    if (instance == null) {
                        int dataCenterId = 0;
                        int workerId = 0;
                        while (true) {
                            // tryCatch保证即使redis等出现问题也可以保证当前线程阻塞,重启redis即可处理继续处理
                            try {
                                String replace = RedisKeyConstant.SNOW_FLAKE_KEY.
                                        replace(DATA_CENTER_ID, String.valueOf(dataCenterId)).
                                        replace(WORKER_ID, String.valueOf(workerId));
                                if (REDIS_OPERATION.setnx(replace, LOCAL_IP, 1, TimeUnit.MINUTES)) {
                                    instance = new SnowFlakeUtils(dataCenterId, workerId);
                                    break;
                                }
                                // 进行重新set直至成功,目前只运用dataCenterId
                                if (dataCenterId++ == DATA_CENTER_ID_MAX) {
                                    log.error("SnowFlake is getting CacheLock, please checkDATACENTERID_MAX={}", DATA_CENTER_ID_MAX);
                                    dataCenterId = 0;
                                }
                            } catch (Exception e) {
                                log.error("SnowFlakeUtils get CacheLock Error, errorMsg:", e);
                                try {
                                    Thread.sleep(MagicNum.THOUSAND);
                                } catch (InterruptedException ex) {
                                    log.error(ex.getMessage(), ex);
                                }
                            }
                        }
                    }
                }
            }
            return instance;
        }
    
        public SnowFlakeUtils(long dataCenterId, long workerId) {
            if (dataCenterId > DATA_CENTER_ID_MAX || dataCenterId < 0) {
                throw new IllegalArgumentException(String.format("data center id can't be greater than %d or less than 0", DATA_CENTER_ID_MAX));
            }
            if (workerId > WORKER_ID_MAX || workerId < 0) {
                throw new IllegalArgumentException(String.format("worker id can't be greater than %d or less than 0", WORKER_ID_MAX));
            }
            this.dataCenterId = dataCenterId;
            this.workerId = workerId;
            String key = RedisKeyConstant.SNOW_FLAKE_KEY.
                    replace(DATA_CENTER_ID, String.valueOf(dataCenterId)).
                    replace(WORKER_ID, String.valueOf(workerId));
            log.info("SnowFlakeUtils Cache Key={}", key);
            // 起线程保证workerId和dataCenter组合不重复
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    while (true) {
                        try {
                            log.debug("SnowFlakeUtils is keep geting CacheLock-{}", key);
                            String localIp = REDIS_OPERATION.get(key);
                            if (LOCAL_IP.equals(localIp)) {
                                REDIS_OPERATION.setex(key, LOCAL_IP, 1, TimeUnit.MINUTES);
                            } else if (!REDIS_OPERATION.setnx(key, LOCAL_IP, 1, TimeUnit.MINUTES)) {
                                throw new ProcessException(CommonConstants.ENUM_PROCESSING_EXCEPTION,"SnowFlakeUtils losed CacheLock-" + key + "." +
                                        "CacheLockKeeperThread broken!" +
                                        "Reday to retrieve CacheLock and Single Instance!");
                            }
                            Thread.sleep(MagicNum.FIFTY * MagicNum.THOUSAND);
                        } catch (Exception e) {
                            // 发生异常 将单例清除 并退出循环结束子线程
                            synchronized (SnowFlakeUtils.class) {
                                instance = null;
                            }
                            log.error(e.getMessage(),e);
                            break;
                        }
                    }
                }
            });
            thread.setName("SnowFlake-CacheLockKeeper-" + dataCenterId + "-" + workerId);
            thread.start();
        }
    
        public void setClock(boolean clock) {
            this.isClock = clock;
        }
    
        public synchronized long nextId() {
            long timestamp = this.getTime();
    
            if (timestamp < lastTimestamp) {
                long offset = lastTimestamp - timestamp;
                if (offset <= MagicNum.FIVE) {
                    try {
                        this.wait(offset << 1);
                        timestamp = this.getTime();
                        if (timestamp < lastTimestamp) {
                            throw new RuntimeException(String.format("Clock moved backwards, Refusing to generate id for %d milliseconds", offset));
                        }
                    } catch (InterruptedException e) {
                        log.error(e.getMessage(), e);
                    }
                } else {
                    throw new RuntimeException(String.format("Clock moved backwards, Refusing to generate id for %d milliseconds", offset));
                }
            }
    
            if (lastTimestamp == timestamp) {
                sequence = sequence + 1;
                if (sequence > SEQUENCE_MAX) {
                    timestamp = tilNextMillis(timestamp);
                    sequence = 0;
                }
            } else {
                sequence = 0;
            }
    
            lastTimestamp = timestamp;
    
            return ((timestamp - START_TIME) << TIMESTAMP_SHIFT) |
                    (dataCenterId << DATA_CENTER_ID_SHIFT) |
                    (workerId << WORKER_ID_SHIFT) |
                    sequence;
        }
    
        /**
         * 该毫秒达到上限,等待到下1毫秒
         */
        private long tilNextMillis(long timestamp) {
            while (getTime() <= timestamp) {
                log.debug("单毫秒主键生成达到上限");
            }
            return this.getTime();
        }
    
        private long getTime() {
            if (isClock) {
                return SystemClock.currentTimeMillis();
            } else {
                return System.currentTimeMillis();
            }
        }
    
        private static String getLocalIp() {
            String ip = "";
            try {
                InetAddress addr = InetAddress.getLocalHost();
                ip += addr.getHostAddress();
            } catch (Exception e) {
                ip += "127.0.0.1";
            }
            ip += "_" + System.currentTimeMillis() + "_" + Math.random();
            log.info("SnowFlakeUtils Cache Value={}", ip);
            return ip;
        }
    }
    
  2. SystemClock工具类。

    /**
     * 由于高并发,在同一毫秒中会多次获取currentTimeMillis,而每次使用System.currentTimeMillis都会占用CPU(native方法).
     * 于是自定义类(single)来获取currentTimeMillis,实现方法是在此类中定义时间并设置一个周期任务(定时线程)1毫秒更新类中的时间
     */
    public final class SystemClock {
    
      private static final SystemClock INSTANCE = new SystemClock(1);
    
      public static SystemClock getInstance() {
          return INSTANCE;
      }
    
      /**
       * 更新时间的时间间隔,默认为1毫秒
       */
      private final long period;
      /**
       * 当前时间
       */
      private final AtomicLong now;
    
      private SystemClock(long period) {
          this.period = period;
          this.now = new AtomicLong(System.currentTimeMillis());
          scheduleClockUpdate();
      }
    
      /**
       * 定时任务(设置为守护线程,1毫秒后开始更新)
       * scheduleAtFixedRate: 每次开始间隔为1毫秒
       * scheduleWithFixedDelay: 每次结束与开始为1毫秒
       */
      private void scheduleClockUpdate() {
          ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
              @Override
              public Thread newThread(Runnable r) {
                  Thread thread = new Thread(r, "System Clock");
                  thread.setDaemon(true);
                  return thread;
              }
          });
          executorService.scheduleAtFixedRate(new Runnable() {
              @Override
              public void run() {
                  now.set(System.currentTimeMillis());
              }
          }, period, period, TimeUnit.MILLISECONDS);
      }
    
      public static long currentTimeMillis() {
          return getInstance().now.get();
      }
    }
    
  3. ApplicationContextHelper Spring上下文工具类。

    @Slf4j
    @Component
    public class ApplicationContextHelper implements ApplicationContextAware {
    /**
    * Spring上下文
    */
    private static ApplicationContext applicationContext;
    
        /**
         * @return ApplicationContext
         */
        public static ApplicationContext getApplicationContext() {
            return applicationContext;
        }
    
        /**
         * 获取ApplicationContextAware
         *
         */
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) {
            ApplicationContextHelper.applicationContext = applicationContext;
        }
    
        /**
         * 根据Class获取对应实例
         *
         */
        public static <T> T getBean(Class<T> clz) {
            return applicationContext.getBean(clz);
        }
    
        /**
         * 根据beanName获取对应实例
         */
        public static <T> T getBean(String name, Class<T> requiredType) {
            return applicationContext.getBean(name, requiredType);
        }
    
    
        public static Object getBean(String name) {
            return applicationContext.getBean(name);
        }
    }
    
  4. RedisOperation获取 RedisOperation,Redis操作工具类。

  5. 在Controller里编写接口,测试结果。

    @RestController
    @RequestMapping("/part/util")
    public class UtilController {
         @ApiOperation("获取雪花数字")
         @GetMapping("/getSnowFlakeNo")
         public Result getSnowFlakeNo() {
             return Result.ok().data(String.valueOf(SnowFlakeUtils.getInstance().nextId()));
         }
    }
    
查看结果
  • 启动项目,有postman访问接口,查看结果如下,返回结果中data的值即为雪花算法数字。
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/316256.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

1Panel应用推荐:AList开源文件列表工具

1Panel&#xff08;github.com/1Panel-dev/1Panel&#xff09;是一款现代化、开源的Linux服务器运维管理面板&#xff0c;它致力于通过开源的方式&#xff0c;帮助用户简化建站与运维管理流程。为了方便广大用户快捷安装部署相关软件应用&#xff0c;1Panel特别开通应用商店&am…

C++力扣题目700--二叉搜索树中的搜索

给定二叉搜索树&#xff08;BST&#xff09;的根节点 root 和一个整数值 val。 你需要在 BST 中找到节点值等于 val 的节点。 返回以该节点为根的子树。 如果节点不存在&#xff0c;则返回 null 。 示例 1: 输入&#xff1a;root [4,2,7,1,3], val 2 输出&#xff1a;[2,1,…

131本!2023中科院分区晋升1区期刊名单出炉

2023年12月27日&#xff0c;中科院分区正式发布《2023年中国科学院文献情报中心期刊分区表》。今年期刊分区表包括SCIE、SSCI、A&HCI&#xff0c;以及ESCI中国期刊&#xff0c;共设置了包括自然科学、人文科学和社会科学在内的21个大类。 关注公众号“Unionpub学术”&…

国际版WPS Office 18.6.1

【应用名称】&#xff1a;WPS Office 【适用平台】&#xff1a;#Android 【软件标签】&#xff1a;#WPS 【应用版本】&#xff1a;18.6.1 【应用大小】&#xff1a;160MB 【软件说明】&#xff1a;软件日常更新。WPS Office是使用人数最多的移动办公软件。独有手机阅读模式…

Vim一键配置指南,打造高效率C++开发环境

文章目录 前言安装与卸载功能演示gcc/g升级问题 前言 Vim作为当下最受欢迎的文本编译器之一&#xff0c;不仅具有强大的文本编辑功能&#xff0c;还提供了高度的可定制性。用户可以根据自己的喜好自定义配置&#xff0c;并且通过自己编写插件或者使用现有的插件来扩展Vim的功能…

【MATLAB】 多元变分模态分解MVMD信号分解算法

有意向获取代码&#xff0c;请转文末观看代码获取方式~ 1 基本定义 多元变分模态分解&#xff08;MVMD&#xff09;是一种信号分解方法&#xff0c;可以自适应地实现信号的频域剖分及各分量的有效分离。 MVMD算法的具体步骤如下&#xff1a; 假设原始信号S被分解为K个分量μ…

简易机器学习笔记(十一)opencv 简易使用-人脸识别、分类任务

前言 前段时间摸了下机器学习&#xff0c;然后我发现其实openCV还是一个很浩瀚的库的&#xff0c;现在也正在写一篇有关yolo的博客&#xff0c;不过感觉理论偏多&#xff0c;所以在学yolo之前先摸一下opencv&#xff0c;简单先写个项目感受感受opencv。 流程 openCV实际上已…

【机器学习】常见算法详解第2篇:K近邻算法各种距离度量(已分享,附代码)

本系列文章md笔记&#xff08;已分享&#xff09;主要讨论机器学习算法相关知识。机器学习算法文章笔记以算法、案例为驱动的学习&#xff0c;伴随浅显易懂的数学知识&#xff0c;让大家掌握机器学习常见算法原理&#xff0c;应用Scikit-learn实现机器学习算法的应用&#xff0…

个人的感悟观点,即将毕业的应届生的对自己未来方向的思考和认识

目录 复习历程思考 为什么我选择了考研 考完后我的状态 考完后我的做法 我对方向的看法&#xff08;拙见&#xff09; 复习历程思考 自我决定考研复习一刻开始。停更半年之久&#xff0c;甚至更长。没有分享自己的学习。在时常半年多的考研复习的过程中。我决定它带给我希…

什么是云服务器?云服务器的工作原理是介绍

阿里云服务器ECS英文全程Elastic Compute Service&#xff0c;云服务器ECS是一种安全可靠、弹性可伸缩的云计算服务&#xff0c;阿里云提供多种云服务器ECS实例规格&#xff0c;如经济型e实例、通用算力型u1、ECS计算型c7、通用型g7、GPU实例等&#xff0c;阿里云百科aliyunbai…

旺店通·企业版和金蝶云星空接口打通对接实战

旺店通企业版和金蝶云星空接口打通对接实战 对接源平台:旺店通企业版 慧策&#xff08;原旺店通&#xff09;是一家技术驱动型智能零售服务商&#xff0c;基于云计算PaaS、SaaS模式&#xff0c;以一体化智能零售解决方案&#xff0c;帮助零售企业数字化智能化升级&#xff0c;实…

运用AI搭建中间服务层(五)

其他文件的修改 ValuesControllers.cs 注意Post的参数从[FromBody]变成了[FromForm]&#xff0c;以便接收上传的图片流数据 using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; using CognitiveMi…

大模型实战营Day3 基于 InternLM 和 LangChain 搭建你的知识库

LLM的局限性&#xff1a; 知识时效性&#xff0c;专业能力&#xff0c;定制化成本 两种大模型开发范式&#xff1a; RAG FineTune RAG: 检索问答生成 外挂知识库 成本低 FineTune&#xff1a; 更新成本高 GPU算力要求高 RAG开发框图&#xff1a; 用户输入 文本向量化 匹配相似…

20240114三角形边长周长和面积

代码 class Triangle(object):"""三角形类"""def __init__(self, a, b, c):"""初始化方法"""self.a aself.b bself.c cstaticmethoddef is_valid(a, b, c):"""判断三条边长能否构成三角形(静态…

【MySQL】:掌握SQL中DDL的数据库定义与操作

&#x1f3a5; 屿小夏 &#xff1a; 个人主页 &#x1f525;个人专栏 &#xff1a; MySQL从入门到进阶 &#x1f304; 莫道桑榆晚&#xff0c;为霞尚满天&#xff01; 文章目录 &#x1f4d1;前言一. SQL的分类二. DDL数据库操作2.1 查询所有数据库2.2 查询当前数据库2.3 创建数…

文件操作(一)

目录 一.什么是文件 1.程序文件和数据文件 2.文件名 3&#xff0c;文本文件和二进制文件 二.文件的打开和关闭 1.流和标准流 2.文件指针 3.文件的打开与关闭 三.结尾 一.什么是文件 在我们学习文件操作之前我们先了解一下什么是文件&#xff1f;以及文件为什么使用文件…

【面试合集】1.说说你对微信小程序的理解?优缺点?

面试官&#xff1a;说说你对微信小程序的理解&#xff1f;优缺点&#xff1f; 一、是什么 2017年&#xff0c;微信正式推出了小程序&#xff0c;允许外部开发者在微信内部运行自己的代码&#xff0c;开展业务 截至目前&#xff0c;小程序已经成为国内前端的一个重要业务&…

java求链表中倒数第k个结点

下面我用两种方法求解&#xff1a; 第一种方法&#xff1a;通常我们做这种题就是求出链表的长度length&#xff0c;然后呢length-k的值就是我们要从链表头部走几步就可以了&#xff0c;代码解释&#xff1a; public class Solution {public class ListNode {int val;ListNode…

分享一个使用python FastApi创建服务的简易模版,与使用http/python请求

这个博客分享一个fastapi的模版&#xff0c;并提供使用http/python访问的示例程序 文章目录 示例程序FastApi应用程序HTTP请求Python请求 示例程序 FastApi应用程序 下面是一个示例&#xff1a; 默认开启一个可以使用Get请求访问的URL&#xff1a;/example_connect这个请求有…

微信商家转账到零钱,既能单笔又能批量,支持多商户管理

大家好&#xff0c;我是小悟 微信商家转账到零钱的功能大家应该都熟悉吧&#xff0c;为了满足商家向用户微信零钱转账的需求&#xff0c;微信支付推出【商家转账到零钱】服务&#xff0c;方便商户可以一次向单个或多个用户的微信零钱转账。 商家转账到零钱为商户提供了简便、…