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

news2024/12/26 15:14:12

文章目录

        • 前言
        • 步骤
        • 查看结果

前言
  • 分布式系统常需要全局唯一的数字作为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的值即为雪花算法数字。
    在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一步步指南:从指定时长中提取需求的帧图片,高效剪辑视频

在现代多媒体时代&#xff0c;视频已经成生活中不可或缺的一部分。从视频中提取某一帧图片&#xff0c;或者对视频进行剪辑&#xff0c;都是常见的需求。下面一起来看云炫AI智剪如何从指定时长中提取需求的帧图片&#xff0c;如何高效地剪辑视频。 按指定时长提取视频某帧图片的…

航天民芯一级代理-技术支持-一站式配单 MT9284-28J LED驱动芯片

描述 MT9284是一个升压转换器&#xff0c;设计用于从单电池锂离子电池中驱动多达7个系列的白色led。MT9284使用电流模式&#xff0c;固定频率结构来调节LED电流&#xff0c;它通过外部电流感应电阻来测量。MT9284包括低电压锁定、限流和热过载保护&#xff0c;以防止在输出过载…

探秘Pytest插件pytest-xdist —— 提升测试效率,轻松并行运行测试

在软件开发中&#xff0c;测试是确保代码质量的重要一环。然而&#xff0c;随着项目规模的增大&#xff0c;测试用例的数量也随之增多&#xff0c;测试的执行时间可能成为一个瓶颈。为了解决这个问题&#xff0c;Pytest提供了丰富的插件生态系统&#xff0c;其中 pytest-xdist …

opencv拉流出现missing picture in access unit with size 4错误解决

0、应用场景问题 我们使用opencv作为拉流客户端&#xff0c;获取画面后进行图像处理并推流&#xff08;使用ffmpeg库&#xff09;。 opencv解码同样使用ffmpeg库。 我们要求opencv能根据业务不断进行拉流操作&#xff0c;等效的逻辑代码如下&#xff1a; while(1) {printf(&…

【LangChain学习之旅】—(7) 调用模型:使用OpenAI API还是微调开源Llama2/ChatGLM?

【LangChain学习之旅】—&#xff08;7&#xff09; 调用模型&#xff1a;使用OpenAI API还是微调开源Llama2/ChatGLM&#xff1f; 大语言模型发展史预训练 微调的模式用 HuggingFace 跑开源模型申请使用 Meta 的 Llama2 模型通过 HuggingFace 调用 LlamaLangChain 和 Hugging…

1panel中的sftpgo webadmin 更新修改docker容器文件的配置教程

本篇文章主要讲解1panel中的sftpgo webadmin 更新修改docker容器文件的配置教程&#xff0c;适合sftpgo webadmin和1panel系统用户配置时使用。 作者&#xff1a;任聪聪 rccblogs.com 日期&#xff1a;2024年1月8日 sftpgo是无法直接直接更改容器内部的网站目录的&#xff0c;但…

QT上位机开发(利用tcp/ip访问plc)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 plc是工控领域很重要的一个器件。简单的plc一般就是对io进行控制&#xff0c;但是复杂的plc&#xff0c;还可以控制电机、变频器&#xff0c;在工业…

Postman进行Soap webservice接口测试

许多人认为Postman是高级REST客户端&#xff0c;Postman是处理通过HTTP发送的请求的工具。其实Postman也可以测试与协议无关的SOAP webservice api接口。 要使用Postman发出SOAP请求&#xff0c;请执行以下操作&#xff1a; 1、提供SOAP端点作为URL&#xff0c;可以使用SOAP的W…

自动化测试和人工测试分别有什么优缺点?

自动化测试 优点 效率高&#xff1a;自动化测试可以快速执行大量测试用例&#xff0c;这对于大型项目或需要频繁进行回归测试的项目非常有用。 一致性强&#xff1a;自动化测试每次执行都会产生相同的结果&#xff0c;这有助于确保测试结果的可靠性和可重复性。 可重复性&am…

Flowable可视化流程设计器Flowable BPMN visualizer

1.安装IDEA插件Flowable BPMN visualizer IDEA插件市场中搜索Flowable BPMN visualizer安装该插件 2.创建流程图 2.1.创建流程图文件 在resources下创建myProcess.bpmn20.xml流程图。 创建时名称只用输入myProcess 2.2.创建流程图 选中myProcess.bpmn20.xml&#xff0c;右…

Openwrite帮我们实现一文多发

Openwrite 一文多发 当你想进入这个搞自媒体的圈子&#xff0c;学着人家一样去搞流量、做IP的时候&#xff0c;就会发现&#xff0c;卖铲子的和卖教程的都赚钱了。而对于商业一无所知的人&#xff0c;只能是接盘侠。可是&#xff0c;接盘侠又如何呢&#xff1f;高客单付不起&a…

【Web】forward 和 redirect 的区别

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;Web ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 Forward&#xff08;转发&#xff09;&#xff1a; Redirect&#xff08;重定向&#xff09;&#xff1a; 区别总结&#xff1a; …

多模态大模型

一、图文匹配 二、Stable-Diffusion(稳定扩散) 图片生成器 Stable-Diffusion(稳定扩散)组成模块: CrossAttention模块:将文本的语义信息与图像的语义信息进行Attention机制&#xff0c;增强输入文本Prompt对生成图片的控制。SelfAttention模块:SelfAttention模块的整体结构与C…

人工智能任务4-读懂YOLOv5模型的几个灵魂拷问问题,深度理解 YOLOv5模型架构

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下人工智能任务4-读懂YOLOv5模型的几个灵魂拷问问题&#xff0c;深度理解 YOLOv5模型架构。YOLOv5是一种高效且精确的目标检测模型&#xff0c;由ultralytics团队开发。它采用了轻量级的网络结构&#xff0c;能够在保…

给定n个字符串s[1...n], 求有多少个数对(i, j), 满足i < j 且 s[i] + s[j] == s[j] + s[i]?

题目 思路&#xff1a; 对于字符串a&#xff0c;b, (a.size() < b.size()), 考虑对字符串b满足什么条件&#xff1a; 由1、3可知a是b的前后缀&#xff0c;由2知b有一个周期是3&#xff0c;即a.size()&#xff0c;所以b是用多个a拼接而成的&#xff0c;有因为a是b的前后缀&…

【野火i.MX6NULL开发板】挂载 NFS 网络文件系统

0、前言 参考资料&#xff1a; &#xff08;误人子弟&#xff09;《野火 Linux 基础与应用开发实战指南基于 i.MX6ULL 系列》PDF 第22章 参考视频&#xff1a;&#xff08;成功&#xff09; https://www.bilibili.com/video/BV1JK4y1t7io?p26&vd_sourcefb8dcae0aee3f1aab…

C++ 类 对象

C 在 C 语言的基础上增加了面向对象编程&#xff0c;C 支持面向对象程序设计。类是 C 的核心特性&#xff0c;通常被称为用户定义的类型。 类用于指定对象的形式&#xff0c;是一种用户自定义的数据类型&#xff0c;它是一种封装了数据和函数的组合。类中的数据称为成员变量&a…

【信息安全】hydra爆破工具的使用方法

hydra简介 hydra又名九头蛇&#xff0c;与burp常规的爆破模块不同&#xff0c;hydra爆破的范围更加广泛&#xff0c;可以爆破远程桌面连接&#xff0c;数据库这类的密码。他在kali系统中自带。 参数说明 -l 指定用户名 -L 指定用户名字典文件 -p 指定密码 -P 指…

2024年前端面试中JavaScript的30个高频面试题之中级知识

基础知识 高级知识 13. 什么是闭包?闭包的用例有哪些? 闭包是一个功能,它允许函数捕获定义该函数的环境(或保留对作用域中变量的访问)即使在该作用域已经关闭后。 我们可以说闭包是函数和词法环境的组合,其中定义了该函数。 换句话说,闭包为函数提供了访问自己的作用域、…

真实可用,Xshell7 期待您的安装使用

xshell https://pan.baidu.com/s/1OKC1sQ1eYq6ZSC8Ez5s0Fg?pwd0531 1.鼠标右击【Xshell7.zip】压缩包&#xff08;win11及以上系统需先点击“显示更多选项”&#xff09; 2.双击Xshell-7.0.0065.exe 执行安装操作 3.选择【是】 4.点击【下一步】 5.选择【我接受...】 6.点击…