分布式ID-Leaf

news2024/11/18 19:38:04

目录

  • 一,背景
  • 二,ID生成方案
    • 1,UUID
    • 2,类snowflake方案
    • 3,号段模式
    • 4,基于Redis模式
    • 5,数据库自增ID
  • 三,Leaf Segment
    • 1,拉取源码
    • 2,修改配置并创建号段表
    • 3,项目启动并测试
  • 四,Leaf Snowflake
    • 1,安装Zookeeper
      • 1.1,下载
      • 1.2,安装
    • 2,Leaf Snowflake项目配置
    • 3,测试
  • 五,模块集成
    • 1,新建模块
    • 2,模块引入与配置
    • 3,测试
    • 4,依赖冲突

一,背景

在复杂的分布式系统中,往往需要对大量的数据和消息进行唯一标识,唯一的标识ID可以为业务处理提供便利。一个好的全局唯一ID系统应该能满足如下要求:

  • 全局唯一性:不能出现重复的ID号,既然是唯一标识,这是最基本的要求。
  • 趋势递增:在MySQL InnoDB引擎中使用的是聚簇索引,采用B+树的数据结构来存储索引数据,在主键的选择上面我们应该尽量使用有序的主键保证写入性能。
  • 单调递增:保证下一个ID一定大于上一个ID,例如事务版本号、IM增量消息,排序等特殊需求。
  • 信息安全:如果ID是连续的,恶意用户的扒取工作就非常容易做了,直接按照顺序下载指定URL即可;如果是订单号,交易号这些,可以通过ID知道一天的交易量,所以在一些应用场景下,会需要ID无规则,不规则。

上述最后两条是互斥的,无法使用同一个方案满足,Leaf Segment可以满足单调递增,在需要ID无规则,不规则时,可以使用Leaf Snowflake。

二,ID生成方案

1,UUID

UUID(Universally Unique Identifier)的标准型式包含32个16进制数字,以连字号分为五段,形式为8-4-4-4-12的36个字符,示例:550e8400-e29b-41d4-a716-446655440000,到目前为止业界一共有5种方式生成UUID。
优点:

  • 性能非常高:本地生成,没有网络消耗。
    缺点:

  • 不易于存储:UUID太长,16字节128位,通常以36长度的字符串表示,很多场景不适用。

  • 信息不安全:基于MAC地址生成的UUID的算法可能会造成MAC地址泄露,这个漏洞曾被用于寻找梅丽莎病毒的制作者位置。

  • 作为主键时在特定环境会存在一些问题,比如做DB主键的场景下,UUID就非常不适用:

    • ① MySQL官方有明确的建议主键要尽量越短越好,36个字符长度的UUID不符合要求。

    All indexes other than the clustered index are known as secondary indexes. In InnoDB, each record in a secondary index contains the primary key columns for the row, as well as the columns specified for the secondary index. InnoDB uses this primary key value to search for the row in the clustered index.*** If the primary key is long, the secondary indexes use more space, so it is advantageous to have a short primary key***.

    翻译过来就是:聚集索引以外的所有索引都称为辅助索引。在InnoDB中,辅助索引中的每个记录都包含该行的主键列,以及为辅助索引指定的列。InnoDB使用这个主键值来搜索聚集索引中的行。如果主键长,则二级索引占用的空间更大,因此主键短是有利的

    • ② 对MySQL索引不利:如果作为数据库主键,在InnoDB引擎下,UUID的无序性可能会引起数据位置频繁变动,严重影响性能。

2,类snowflake方案

这种方案大致来说是一种以划分命名空间(UUID也算,由于比较常见,所以单独分析)来生成ID的一种算法,这种方案把64-bit分别划分成多段,分开来标示机器,时间等,比如在snowflake中的64-bit分别表示如下图(图片来自网络)所示:


41-bit的时间可以表示(1L<<41)/(1000L360024*365)=69年的时间,10-bit机器可以分别表示1024台机器。如果我们对IDC划分有需求,还可以将10-bit分5-bit给IDC,分5-bit给工作机器。这样就可以表示32个IDC,每个IDC下可以有32台机器,可以根据自身需求定义。12个自增序列号可以表示2^12个ID,理论上snowflake方案的QPS约为409.6w/s,这种分配方式可以保证在任何一个IDC的任何一台机器在任意毫秒内生成的ID都是不同的。
这种方式的优缺点是:
优点:

  • 毫秒数在高位,自增序列在低位,整个ID都是趋势递增的。
  • 不依赖数据库等第三方系统,以服务的方式部署,稳定性更高,生成ID的性能也是非常高的。
  • 可以根据自身业务特性分别bit位,非常灵活。
    缺点:
  • 强依赖机器时钟,如果机器上时钟回拨,会导致发号重复或者服务处于不可用状态。

3,号段模式

号段模式是当下分布式ID生成器主流实现方式之一,号段模式可以理解为从数据库批量的获取自增ID,每次从数据库取出一个号段范围,例如(1,1000]代表1000个ID,具体的业务服务将本号段,生成1~1000的自增ID并加载到内存。表结构如下:

CREATE TABLE id_generator (
  id int(10) NOT NULL,
  max_id bigint(20) NOT NULL COMMENT '当前最大id',
  step int(20) NOT NULL COMMENT '号段的布长',
  biz_type    int(20) NOT NULL COMMENT '业务类型',
  version int(20) NOT NULL COMMENT '版本号',
  PRIMARY KEY (`id`)
) 
  • id:主键
  • max_id:当前最大的可用id
  • step:代表号段的长度
  • version:版本
    等这批号段ID用完,再次向数据库申请新号段,对max_id字段做一次update操作,update max_id= max_id + step,update成功则说明新号段获取成功,新的号段范围是(max_id ,max_id +step]。
update id_generator set max_id = #{max_id+step}, version = version + 1 where version = # {version} and biz_type = XXX

由于多业务端可能同时操作,所以采用版本号version乐观锁方式更新,这种分布式ID生成方式不强依赖于数据库,不会频繁的访问数据库,对数据库的压力小很多。

4,基于Redis模式

Redis也同样可以实现,原理就是利用redis的 incr命令实现ID的原子性自增。

127.0.0.1:6379> set seq_id 1 // 初始化自增ID为1
OK
127.0.0.1:6379> incr seq_id // 增加1,并返回递增后的数值
(integer) 2

用redis实现需要注意一点,要考虑到redis持久化的问题。redis有两种持久化方式RDB和AOF

  • RDB会定时打一个快照进行持久化,假如连续自增但redis没及时持久化,而这会Redis挂掉了,重启Redis后会出现ID重复的情况。
  • AOF会对每条写命令进行持久化,即使Redis挂掉了也不会出现ID重复的情况,但由于incr命令的特殊性,会导致Redis重启恢复的数据时间过长。

5,数据库自增ID

基于数据库的auto_increment自增ID完全可以充当分布式ID,具体实现:需要一个单独的MySQL实例用来生成ID,建表结构如下:

CREATE DATABASE `DIS_ID`;
CREATE TABLE DIS_ID.SEQUENCE_ID (
    id bigint(20) unsigned NOT NULL auto_increment, 
    value char(10) NOT NULL default '',
    PRIMARY KEY (id),
) ENGINE=MyISAM;
-- 插入一条数据,返回一个自增的ID
insert into SEQUENCE_ID(value)  VALUES ('values');

当我们需要一个ID的时候,向表中插入一条记录返回主键ID,但这种方式有一个比较致命的缺点,访问量激增时MySQL本身就是系统的瓶颈,用它来实现分布式服务风险比较大,不推荐!
优点:

  • 实现简单,ID单调自增,数值类型查询速度快
    缺点:
  • DB单点存在宕机风险,无法扛住高并发场景

三,Leaf Segment

Leaf Segment是美团开源的关于号段模式ID生成的方法之一,除了满足号段模式的基本功能外,还通过双Buffer缓存的方式进行了优化,一个缓存用来提供生成id服务,另一个buffer通过新开一个线程从数据库中更新号段,项目源码已上传到Github。
Leaf Github

1,拉取源码

  • 通过Git拉取项目
git clone git@github.com:Meituan-Dianping/Leaf.git
  • 项目是一个SpringBoot架构的。
    在这里插入图片描述

2,修改配置并创建号段表

1)这里以IDEA为例,进入到leaf-server目录下,在资源路径下找到leaf.properties文件,这里是号段模式和雪花算法模式的配置信息。

leaf.name=com.sankuai.leaf.opensource.test
# 是否启用号段模式
leaf.segment.enable=true
# 数据库连接信息
leaf.jdbc.url=jdbc:mysql://localhost:3306/demo?serverTimezone=Asia/Shanghai&characterEncoding=utf-8&autoReconnect=true&failOverReadOnly=false&zeroDateTimeBehavior=convertToNull
# 数据库用户名
leaf.jdbc.username=root
# 数据库密码
leaf.jdbc.password=12345

# 雪花算法模式暂时先不启用
leaf.snowflake.enable=false
#leaf.snowflake.zk.address=
#leaf.snowflake.port=

2)在leaf-server的scripts目录下是号段模式要用到的建表语句。

DROP TABLE IF EXISTS `leaf_alloc`;

CREATE TABLE `leaf_alloc` (
  `biz_tag` varchar(128)  NOT NULL DEFAULT '',
  `max_id` bigint(20) NOT NULL DEFAULT '1',
  `step` int(11) NOT NULL,
  `description` varchar(256)  DEFAULT NULL,
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`biz_tag`)
) ENGINE=InnoDB;

insert into leaf_alloc(biz_tag, max_id, step, description) values('leaf-segment-test', 1, 2000, 'Test leaf Segment Mode Get Id')

3,项目启动并测试

#segment
curl http://localhost:8080/api/segment/get/leaf-segment-test
#snowflake
curl http://localhost:8080/api/snowflake/get/test

在这里插入图片描述

四,Leaf Snowflake

Leaf-segment方案可以生成趋势递增的ID,同时ID号是可计算的,但是在有些情况下,我们希望id是递增但无规律的,这时就可以使用Leaf Snowflake来实现。
在这里插入图片描述
Leaf-snowflake方案完全沿用snowflake方案的bit位设计,即是“1+41+10+12”的方式组装ID号。对于workerID的分配,当服务集群数量较小的情况下,完全可以手动配置。Leaf服务规模较大,动手配置成本太高。所以使用Zookeeper持久顺序节点的特性自动对snowflake节点配置wokerID。Leaf-snowflake是按照下面几个步骤启动的:

  • 1,启动Leaf-snowflake服务,连接Zookeeper,在leaf_forever父节点下检查自己是否已经注册过(是否有该顺序子节点)。
  • 2,如果有注册过直接取回自己的workerID(zk顺序节点生成的int类型ID号),启动服务。
  • 3,如果没有注册过,就在该父节点下面创建一个持久顺序节点,创建成功后取回顺序号当做自己的workerID号,启动服务。

1,安装Zookeeper

1.1,下载

Zookeeper官网
在这里插入图片描述
这里我们下载最新的当前版本:

选择官方推荐的下载方式:
在这里插入图片描述

1.2,安装

  • 将文件上传到服务器,随后解压
tar -zxvf apache-zookeeper-3.8.1-bin.tar.gz
  • 进入解压后的zookeeper目录,并创建两个目录:data,log
cd apache-zookeeper-3.8.1-bin
mkdir data
mkdir log
  • 进入zookeeper的conf目录,将样例的配置文件拷贝一份,修改里面的部分配置
cd conf
cp zoo_sample.cfg zoo.cfg
vim zoo.cfg

在这里插入图片描述
dataDir是zookeeper的数据存放位置,之前是/tmp目录下,该目录是Linux的临时目录,会被定期清理掉,我们修改成自己的位置,并添加日志的目录

dataDir=/home/zookeeper/apache-zookeeper-3.8.1-bin/data
dataLogDir=/home/zookeeper/apache-zookeeper-3.8.1-bin/log
  • 进入bin目录,启动zookeeper
# 运行zookeeper
sh zkServer.sh start
# 通过客户端工具验证zookeeper是否启动成功
./zkCli.sh

# 停止zookeeper
sh zkServer.sh stop
# 重启zookeeper
sh zkServer.sh restart

在这里插入图片描述

  • 开发端口或关闭防火墙
# leaf-server需要连接zookeeper,端口未开放会连接失败
firewall-cmd --add-port=2181/tcp --permanent
systemctl restart firewalld

2,Leaf Snowflake项目配置

修改leaf.properties文件:

# 开启雪花算法模式
leaf.snowflake.enable=true
# zookeeper连接信息,包括ip和端口
leaf.snowflake.zk.address=192.168.1.9:2181
# 类似于workId,用来与其它服务做区分
leaf.snowflake.port=8080

配置完成后就可以启动项目进行测试了。

3,测试

curl http://localhost:8080/api/snowflake/get/test

在这里插入图片描述

五,模块集成

唯一主键服务可以单独部署,也可以模块的方式集成到自己的项目中,或者是springboot-starter的方式直接引入到项目中,下面以模块的方式为例,将leaf-core集成到自己的项目中。

1,新建模块

新建一个模块,命名unique_id,该模块属于自己父项目下的子模块,模块的代码直接拷贝leaf-core中的即可,除了pom.xml中需要修改,因为原leaf-core中的某些依赖版本是使用了它自己的leaf-parent,我们需要在自己的parent模块中定义,或者直接在本模块中使用指定的版本号,下面的pom.xml是经过实践可行的。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>ticktack</artifactId>
        <groupId>com.demo</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>unique_id</artifactId>
    <version>1.0.1</version>
    <packaging>jar</packaging>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <mysql-connector-java.version>5.1.38</mysql-connector-java.version>
        <commons-io.version>2.4</commons-io.version>
        <log4j.version>2.7</log4j.version>
        <mybatis-spring.version>1.2.5</mybatis-spring.version>
        <jackson-databind.version>2.9.6</jackson-databind.version>
        <spring.version>4.3.18.RELEASE</spring.version>
        <log4j.version>2.7</log4j.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.9</version>
        </dependency>

        <dependency>
            <groupId>org.perf4j</groupId>
            <artifactId>perf4j</artifactId>
            <version>0.9.16</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.2</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.4</version>
        </dependency>
        <!--zk-->
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>2.6.0</version>
            <!--            <scope>provided</scope>-->
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>${jackson-databind.version}</version>
            <scope>provided</scope>
        </dependency>
        <!-- test scope -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <version>${log4j.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>${log4j.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>${log4j.version}</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.18</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql-connector-java.version}</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>${mybatis-spring.version}</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
            <version>4.12</version>
        </dependency>

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>16.0.1</version>
        </dependency>

    </dependencies>

</project>

2,模块引入与配置

  • 在需要使用主键服务的模块引入
<!--引入第三方模块:unique_id-->
<dependency>
   <groupId>com.hao</groupId>
   <artifactId>unique_id</artifactId>
   <version>1.0.1</version>
</dependency>
  • 配置文件 leaf.properties
leaf.name=com.sankuai.leaf.opensource.test
leaf.segment.enable=true
leaf.segment.url=jdbc:mysql://127.0.0.1:3306/thtrafficgenemgmt?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=GMT%2B8
leaf.segment.driver-class-name=com.mysql.cj.jdbc.Driver
leaf.segment.username=root
leaf.segment.password=123456

# Leaf Snow Flake配置
leaf.snowflake.enable=true
leaf.snowflake.zk.address=192.168.1.9:2181
leaf.snowflake.port=8090

3,测试

  • 引入生成主键的service类
import com.alibaba.druid.pool.DruidDataSource;
import com.sankuai.inf.leaf.IDGen;
import com.sankuai.inf.leaf.common.PropertyFactory;
import com.sankuai.inf.leaf.common.Result;
import com.sankuai.inf.leaf.common.ZeroIDGen;
import com.sankuai.inf.leaf.segment.SegmentIDGenImpl;
import com.sankuai.inf.leaf.segment.dao.IDAllocDao;
import com.sankuai.inf.leaf.segment.dao.impl.IDAllocDaoImpl;
import com.tick.tack.exception.ServiceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.sql.SQLException;
import java.util.Properties;

@Service("SegmentService")
public class SegmentService {
    // 号段模式下数据库信息配置
    private static final String LEAF_SEGMENT_ENABLE = "leaf.segment.enable";
    private static final String LEAF_JDBC_URL = "leaf.segment.url";
    private static final String LEAF_JDBC_USERNAME = "leaf.segment.username";
    private static final String LEAF_JDBC_PASSWORD = "leaf.segment.password";

    private Logger logger = LoggerFactory.getLogger(SegmentService.class);

    private IDGen idGen;
    private DruidDataSource dataSource;

    public SegmentService() throws SQLException, ServiceException {
        Properties properties = PropertyFactory.getProperties();
        boolean flag = Boolean.parseBoolean(properties.getProperty(LEAF_SEGMENT_ENABLE, "true"));
        if (flag) {
            // Config dataSource
            dataSource = new DruidDataSource();
            dataSource.setUrl(properties.getProperty(LEAF_JDBC_URL));
            dataSource.setUsername(properties.getProperty(LEAF_JDBC_USERNAME));
            dataSource.setPassword(properties.getProperty(LEAF_JDBC_PASSWORD));
            dataSource.init();

            // Config Dao
            IDAllocDao dao = new IDAllocDaoImpl(dataSource);

            // Config ID Gen
            idGen = new SegmentIDGenImpl();
            ((SegmentIDGenImpl) idGen).setDao(dao);
            if (idGen.init()) {
                logger.info("Segment Service Init Successfully");
            } else {
                throw new ServiceException("Segment Service Init Fail");
            }
        } else {
            idGen = new ZeroIDGen();
            logger.info("Zero ID Gen Service Init Successfully");
        }
    }

    public Result getId(String key) {
        return idGen.get(key);
    }

    public SegmentIDGenImpl getIdGen() {
        if (idGen instanceof SegmentIDGenImpl) {
            return (SegmentIDGenImpl) idGen;
        }
        return null;
    }
}
import com.sankuai.inf.leaf.IDGen;
import com.sankuai.inf.leaf.common.PropertyFactory;
import com.sankuai.inf.leaf.common.Result;
import com.sankuai.inf.leaf.common.ZeroIDGen;
import com.sankuai.inf.leaf.snowflake.SnowflakeIDGenImpl;
import com.tick.tack.exception.ServiceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.util.Properties;

@Service("SnowflakeService")
public class SnowflakeService {
    // 雪花算法-zookeeper相关配置
    private static final String LEAF_SNOWFLAKE_ENABLE = "leaf.snowflake.enable";
    private static final String LEAF_SNOWFLAKE_PORT = "leaf.snowflake.port";
    private static final String LEAF_SNOWFLAKE_ZK_ADDRESS = "leaf.snowflake.zk.address";

    private Logger logger = LoggerFactory.getLogger(SnowflakeService.class);

    private IDGen idGen;

    public SnowflakeService() throws ServiceException {
        Properties properties = PropertyFactory.getProperties();
        boolean flag = Boolean.parseBoolean(properties.getProperty(LEAF_SNOWFLAKE_ENABLE, "true"));
        if (flag) {
            String zkAddress = properties.getProperty(LEAF_SNOWFLAKE_ZK_ADDRESS);
            int port = Integer.parseInt(properties.getProperty(LEAF_SNOWFLAKE_PORT));
            idGen = new SnowflakeIDGenImpl(zkAddress, port);
            if (idGen.init()) {
                logger.info("Snowflake Service Init Successfully");
            } else {
                throw new ServiceException("Snowflake Service Init Fail");
            }
        } else {
            idGen = new ZeroIDGen();
            logger.info("Zero ID Gen Service Init Successfully");
        }
    }

    public Result getId(String key) {
        return idGen.get(key);
    }
}
  • 属性注入,与方法测试
import com.sankuai.inf.leaf.common.Result;
import com.sankuai.inf.leaf.common.Status;
import com.tick.tack.config.AuthAccess;
import com.tick.tack.utils.SegmentService;
import com.tick.tack.utils.SnowflakeService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Api(tags = "唯一ID测试类")
public class UniqueIdGenerateController {
    @Autowired
    private SegmentService segmentService;

    @Autowired
    private SnowflakeService snowflakeService;

    @GetMapping("/generateRidBySegment")
    @ApiOperation(value = "号段模式生成唯一ID")
    @AuthAccess
    public void generateRidBySegment() {
        Result result = segmentService.getId("leaf-segment-test");
        if (result.getStatus().equals(Status.SUCCESS)) {
            System.out.println(result.getId());
        } else {
            System.out.println("号段模式获取分布式ID失败");
        }

    }

    @GetMapping("/generateRidBySnowflake")
    @ApiOperation(value = "雪花算法生成唯一ID")
    @AuthAccess
    public void generateRidBySnowflake() {
        Result snowflakeServiceId = snowflakeService.getId("test");
        if (snowflakeServiceId.getStatus().equals(Status.SUCCESS)) {
            System.out.println(snowflakeServiceId.getId());
        } else {
            System.out.println("雪花算法获取分布式ID失败");
        }
    }
}

4,依赖冲突

模块集成后可能会出现依赖版本冲突,导致项目启动失败,这个时候就需要调整版本,其实最好是将所有的版本都定义到我们的父工程,然后唯一id模块和我们自己的模块都使用同一套版本,这样就可以降低版本冲突的问题,另外leaf-core中有些依赖作用范围是provided,会导致运行时找不到依赖,报ClassNoDefException,这时将依赖的作用范围设置成Runtime,或者直接删除<scope>provided</scope>

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

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

相关文章

FM实现F4帮助系列三:弹出框多筛选条件的搜索帮助(根据搜索帮助筛选字段)...

函数&#xff1a;F4IF_GET_SHLP_DESCR F4IF_START_VALUE_REQUEST 效果图&#xff1a; 本例子代码&#xff1a; 找到需要的帮助: *& Report ZLM_TEST_045 REPORT zlm_test_045. TABLES makt. DATA: BEGIN OF str_f4, matnr TYPE matnr, maktx TYPE maktx, END OF str_f4.…

【JavaEE初阶】前端第四节.JavaScript入门学习笔记

作者简介&#xff1a;大家好&#xff0c;我是未央&#xff1b; 博客首页&#xff1a;未央.303 系列专栏&#xff1a;Java测试开发 每日一句&#xff1a;人的一生&#xff0c;可以有所作为的时机只有一次&#xff0c;那就是现在&#xff01;&#xff01;&#xff01; 前言 一、…

反馈体系

&#xff08;1&#xff09;引子 我前段时间看了一个小短视频&#xff0c;有主持人问马斯克&#xff1a;你最害怕什么&#xff1f; 马斯克想了很久&#xff0c;回答&#xff1a;我最害怕反馈机制失灵。 马斯克说的不是特斯拉汽车的反馈机制失灵&#xff08;虽然特斯拉汽车上装了…

Office Visio 2010安装

哈喽&#xff0c;大家好。今天一起学习的是Visio 2010的安装&#xff0c;这是一个绘制流程图的软件&#xff0c;用有效的绘图表达信息&#xff0c;比任何文字都更加形象和直观。Office Visio 是office软件系列中负责绘制流程图和示意图的软件&#xff0c;便于IT和商务人员就复杂…

Java程序设计入门教程-- if 条件语句

目录 单分支选择语句&#xff08;if&#xff09; 双分支选择语句&#xff08;if…else&#xff09; 嵌套if语句 单分支选择语句&#xff08;if&#xff09; 情形 当判断条件满足时&#xff0c;执行语句体S&#xff0c;而不满足则什么都不做。 格式 if &#xff08;条件判断表…

Web3:实质、本质和棒喝

本文的名称可能让人困惑&#xff0c;实质和本质不一样吗&#xff1f;棒喝又是个什么。 什么是实质、本质和棒喝 如果不是很计较的话&#xff0c;“实质”和“本质”其实差不多。但在这篇文章里&#xff0c;略有区别。 “实质”是说一个东西原原本本是个什么东西。 “本质”是一…

刚刚,吴恩达 ChatGPT 新课三连发!

你有没有想过&#xff0c;你可以自己构建一个AI系统&#xff0c;或者开发一个使用大语言模型&#xff08;LLM&#xff09;的应用&#xff0c;甚至理解并创建扩散模型&#xff1f;我在吴恩达的三门新课程中找到了答案&#xff0c;这些课程让我看到了AI的无限可能性。 好消息&…

Neo4j图数据库介绍及简单入门

文章目录 Neo4j图数据库介绍Neo4j数据库安装可视化例子Neo4j增删改查增删改查 Neo4j图数据库介绍 电影里有这样的片段&#xff0c;警察抓捕凶手时&#xff0c;在墙上会画一个图&#xff1a; 这里也有一个demo可以让我们看到一个做好的图数据库&#xff1a; 这个demo也是用Neo4…

前端基础几大件

文章目录 HTMLCSSJavaScriptAjaxAxios&#xff08;第三方库&#xff0c;专门用于请求数据&#xff09;SpringBoot单例模式与前端异步请求 HTML 在HTML当中&#xff0c;一切都是节点Object&#xff1a;&#xff08;非常重要&#xff09; 整个Html文档就是一个DOM文档节点。所有…

26 strcpy 的调试

前言 同样是一个 很常用的 glibc 库函数 不管是 用户业务代码 还是 很多类库的代码, 基本上都会用到 字符串的复制 不过 我们这里是从 具体的实现 来看一下 它的实现 主要是使用 汇编 来进行实现的, 因此 理解需要一定的基础 测试用例 就是简单的使用了一下 strcpy, s…

推进印度制造受挫,苹果仍踢出13家中国企业,一条道走到黑?

苹果公布了2022年的供应商名单&#xff0c;让人惊讶的是苹果将13家中国供应商踢出了供应链&#xff0c;而美国、日本的供应商却有所增加&#xff0c;似乎苹果仍然在降低对中国制造的依赖&#xff0c;这对于苹果来说未必是好事。 一、苹果的印度制造计划受挫 数年前苹果推动印度…

深度学习在金融领域的十大应用算法【附Python代码】

引言 随着金融数据的不断增长和复杂化&#xff0c;传统的统计方法和机器学习技术面临着挑战。深度学习算法通过多层神经网络的构建&#xff0c;以及大规模数据的训练和优化&#xff0c;可以从数据中提取更加丰富、高级的特征表示&#xff0c;从而提供更准确、更稳定的预测和决策…

chatgpt赋能python:如何用Python创建一个九宫格

如何用Python创建一个九宫格 作为一种流行的编程语言&#xff0c;Python可以用于各种各样的项目。这篇文章将介绍如何使用Python创建一个九宫格布局&#xff0c;并展示如何在网页优化&#xff08;SEO&#xff09;中使用它。在本文中&#xff0c;我们将使用Python 3和Flask框架…

如何衡量客户对产品的推荐意愿?

如何衡量客户对产品的推荐意愿&#xff1f;净推荐值 趣讲大白话&#xff1a;衡量客户对你好不好 【趣讲信息科技184期】 **************************** 净推荐值(Net Promoter Score,简称NPS)是一种衡量客户忠诚度和推荐意愿的指标。 净推荐值的计算公式为&#xff1a; NPS (…

flex+margin布局方法

方式&#xff1a;【具体代码1】 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"…

全志V3S嵌入式驱动开发(串口驱动)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 全志V3S支持三个串口&#xff0c;但是因为其中UART1的pin和其他功能是复用的&#xff0c;所以这个时候一般只用UART0和UART2。当然我们在linux开发…

Hive---5、分区表和分桶表

1、分区表和分桶表 1.1 分区表 Hive中的分区就是把一张大表的数据按照业务需求分散的存储到多个目录&#xff0c;每个目录就称为该表的一个分区。在查询时通过where子句中的表达式选择查询所需要的分区&#xff0c;这样的查询效率会提高很多。 1.1.1 分区表基本语法 1、创建…

scratch舞蹈演出 中国电子学会图形化编程 少儿编程 scratch编程等级考试一级真题和答案解析2023年5月

目录 scratch舞蹈演出 一、题目要求 1、准备工作 2、功能实现 二、案例分析

Spring MVC的高级功能(异常处理器,拦截器,文件操作)

目录&#xff1a; 异常处理拦截器文件上传和下载 1.异常处理&#xff08;简单异常处理&#xff0c;自定义异常处理&#xff0c;异常处理注解&#xff09; 简单异常处理器 HandlerExceptionResolver接口 如果希望对Spring MVC中所有异常进行统一处理&#xff0c;可以使用S…

【Spring】——Spring的创建与使用

目录 一、传统程序开发与控制反转&#x1f36d; 1、传统程序开发&#x1f349; 传统程序开发的缺陷&#x1f353; 解决传统开发中的缺陷&#x1f353; 2、控制反转思维程序开发&#x1f349; 3 对比总结规律&#x1f349; 二、Spring创建与使用&#x1f36d; 1、创建Sp…