当前位置: 首页 > news >正文

美团Leaf分布式ID源码启动部署

1 概述

美团点评(Leaf)分布式主键支持数据库号段模式(segment mode)和雪花算法模式(Snowflake mode),可以根据不同业务场景灵活切换。

2 下载源码

Meituan-Dianping master分支

3 修改源码

3.1 项目结构

3.2 修改pom

mysql数据库版本支持8.X版本,需要升级leaf-server和leaf-core模块的mysql-connector以及druid的版本,升级版本号如下:

<mysql-connector-java.version>8.0.13</mysql-connector-java.version>
<druid.version>1.1.10</druid.version>

4 号段模式(Segment mode)

4.1 修改leaf-server配置文件leaf.properties

# Segment mode
leaf.name=com.sankuai.leaf.opensource.test
leaf.segment.enable=true
leaf.jdbc.url=jdbc:mysql://localhost:3306/meituan-leaf?autoReconnect=true&useUnicode=true&characterEncoding=utf-8&&zeroDateTimeBehavior=CONVERT_TO_NULL&&serverTimezone=GMT%2B8
leaf.jdbc.username=root
leaf.jdbc.password=123456
leaf.snowflake.enable=false

4.2 新建MySQL数据库和表

数据库:meituan-leaf

表:leaf_alloc

DROP TABLE IF EXISTS `leaf_alloc`;
CREATE TABLE `leaf_alloc` (
  `biz_tag` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '不同业务,用biz_tag字段来隔离,如果需要扩容时,对biz_tag分库分表即可',
  `max_id` bigint NOT NULL DEFAULT '1' COMMENT '当前业务号段的最大值,用于计算下一个号段',
  `step` int NOT NULL COMMENT '步长,每次获取新ID的数量',
  `description` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '对于业务的描述',
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`biz_tag`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

insert into leaf_alloc(biz_tag, max_id, step, description) values('biz-01-segment', 1, 10, '某某业务号段模式获取ID')

4.3 启动项目

1) 访问 http://127.0.0.1:8080/cache 业务号段已经分配ID信息监控

2) 访问 http://127.0.0.1:8080/db  数据库信息监控

3)访问 http://127.0.0.1:8080/api/segment/get/biz-01-segment 获取分布式ID

4 雪花模式(Snowflake mode)

4.1 修改leaf-server配置文件leaf.properties

# Snowflake mode
leaf.name=com.sankuai.leaf.opensource.test
leaf.segment.enable=false
leaf.snowflake.enable=true
# zk地址
leaf.snowflake.zk.address=127.0.0.1
# zk端口
leaf.snowflake.port=2181

4.2 启动zookeeper-3.4.11

.4.3 启动项目

1) 访问 http://127.0.0.1:8080/api/snowflake/get/biz-01-segment 获取分布式ID

 5 客户端服务获取分布式ID

5.1 远程FeignClient接口

@FeignClient(value = "meituanleafid", fallback = MeiTuanLeafIdApiFallback.class)
public interface MeiTuanLeafIdApi {
  // 雪花模式
  @PostMapping("/snowflake/mode/get")
  Result<List<Long>> getSnowflakeModeIds(BizIdDto dto);

  // 号段模式
  @PostMapping("/segment/mode/get")
  Result<SegmentDto> getSegmentModeIds(BizIdDto dto);
}


@Data
public class BizIdDto {
  @NotBlank(message = "业务id不能为空")
  private String bizId;

  @NotNull(message = "数量不能为空")
  @Min(value = 1, message = "数量必须是正整数!")
  @Max(value = 20000, message = "数量不能超过2万!")
  private Integer size;
}

@Data
@Builder
public class SegmentDto {
  // 当前号段的分布式id值
  private AtomicLong value;
  // 分布式id最大值
  private volatile long max;
}

5.2 定时任务

public class DistributedIdService {   
 
    private final ConcurrentLinkedQueue<Long> cacheIds = new ConcurrentLinkedQueue();
    private final AtomicBoolean isSnowflakeModePulling= new AtomicBoolean(false);
    private final ConcurrentLinkedQueue<SegmentDto> cacheSegment = new ConcurrentLinkedQueue();
    private volatile SegmentDto segmentDto = null;
    private final Semaphore segmentModeSemaphore= new Semaphore(1); 
    private final ExecutorService service=new ThreadPoolExecutor(4, 4, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue(10)); 
    // 本地缓存最大分布式ID数量
    private int cacheMaxSize;
    // 因子    
    private float loadFactor;
    // 每段的步长
    private volatile int pullSegmentStepSize = 1000;    

    public DistributedIdService () {        
          
    }
    
    @PostConstruct
    private void init() { 
       this.cacheMaxSize= 10000;
       this.loadFactor= 0.7F;     
       this.checkPullSnowflakeIds();
       this.checkPullSegment();
    }    

    // 开启异步线程检查是本地分布式ID缓存是否快消耗完毕,从服务器拉取新的分布式ID集合,本地缓存
    private void checkPullSnowflakeIds() {
        // 本地缓存的数量 < 阀值,请求新的分布式ID
        if ((float)this.cacheIds.size() < (float)this.cacheMaxSize * this.loadFactor && this.isSnowflakeModePulling.compareAndSet(false, true)) {
            this.service.execute(new Runnable() {
                public void run() {
                    this.pullSnowflakeIds();
                }
            });
        }
    }

    // 开启异步线程检查本地业务号段缓存是否快消耗完毕,从服务器拉取新的号段集合,本地缓存
    private void checkPullSegment() {
        // 本地缓存的号段*号段步长 <  阀值,请求新的号段
        if ((float)(this.cacheSegment.size() * this.pullSegmentStepSize) < (float)this.cacheMaxSize * this.loadFactor && this.segmentModeSemaphore.tryAcquire()) {
            this.service.execute(() -> {
                try {
                    this.pullSegments();
                } finally {
                    segmentModeSemaphore.release();
                }
            });
        }
    }


    // 获取最新的雪花分布式ID集合进行缓存
    private void pullSnowflakeIds() {
        if (this.cacheIds.size() > this.cacheMaxSize) {            
            this.isSnowflakeModePulling.compareAndSet(true, false);
        } else {
            try {
                // 请求新的分布式ID,本地缓存
                List<Long> ids = this.requestPullSnowflakeIds(this.pullOrderedSize);
                this.cacheIds.addAll(ids);                
            } finally {
                this.isSnowflakeModePulling.compareAndSet(true, false);
            }
        }
    }
   
    // 获取最新的号段进行缓存
    private void pullSegments() {
        if (this.cacheSegment.size() * this.pullSegmentStepSize > this.cacheMaxSize) {
            log.info("号段拉数据段被取消");
        } else {
            // 请求新的号段,本地缓存
            SegmentDto segmentDto = this.requestPullSegment(this.pullOrderedSize);
            this.cacheSegment.add(segmentDto);            
        }
    }

    // 请求远程服务器获取最新的雪花分布式ID集合
    private List<Long> requestPullSnowflakeIds(int size) {
        BizIdDto bizIdDto = new BizIdDto();
        bizIdDto.setServiceId(""biz-01-segment);
        bizIdDto.setSize(1000);
        Result<List<Long>> result = this.meiTuanLeafIdApi.getSnowflakeModeIds(bizIdDto);
        if (ResultHelp.success(result)) {
            return (List)result.getData();
        } else {           
            throw new CusException("从服务端获取雪花分布式ID失败");
        }
    }

    // 请求远程服务器获取最新的号段
    private SegmentDto requestPullSegment(int size) {
        BizIdDto bizIdDto = new BizIdDto();
        bizIdDto.setServiceId(""biz-01-segment);
        bizIdDto.setSize(1000);
        Result<SegmentDto> result = this.meiTuanLeafIdApi.getSegmentModeIds(bizIdDto);
        if (ResultHelp.success(result)) {
            return (SegmentDto)result.getData();
        } else {           
            throw new CusException("从服务端获取号段失败");
        }
    }
}

相关文章:

  • 归一化小程序
  • 走过岁月我才发现——云IDE真方便(Python3.8环境测试)
  • SpringBoot核心技术 之 基础入门
  • Linux下编译工具:gcc/g++ の最全使用教程
  • 【计算机视觉】imutils的基本使用
  • Vue--nextTick--作用/用法/原理
  • 自动化测试项目学习笔记(五):Pytest结合allure生成测试报告以及重构项目
  • 计算机网络习题答案
  • js中的‘==‘和‘===‘
  • 一起来部署项目-采购一台云服务器
  • 【老生谈算法】matlab实现抽样定理算法源码——抽样定理
  • [从0开始机器学习]4.线性回归 正规方程
  • RayVentory以改进IT的分析,RayVentory原始数据之间轻松切换
  • Oracle 递归案例
  • Python编程 print输出函数
  • [译]前端离线指南(上)
  • 78. Subsets
  • Babel配置的不完全指南
  • E-HPC支持多队列管理和自动伸缩
  • FastReport在线报表设计器工作原理
  • leetcode386. Lexicographical Numbers
  • mysql外键的使用
  • Python socket服务器端、客户端传送信息
  • python_bomb----数据类型总结
  • Spark in action on Kubernetes - Playground搭建与架构浅析
  • Spark学习笔记之相关记录
  • Terraform入门 - 3. 变更基础设施
  • Vue2 SSR 的优化之旅
  • vue总结
  • XForms - 更强大的Form
  • 百度贴吧爬虫node+vue baidu_tieba_crawler
  • 等保2.0 | 几维安全发布等保检测、等保加固专版 加速企业等保合规
  • 技术攻略】php设计模式(一):简介及创建型模式
  • 类orAPI - 收藏集 - 掘金
  • 如何设计一个微型分布式架构?
  • Java数据解析之JSON
  • ​如何防止网络攻击?
  • ​虚拟化系列介绍(十)
  • # Python csv、xlsx、json、二进制(MP3) 文件读写基本使用
  • # 日期待t_最值得等的SUV奥迪Q9:空间比MPV还大,或搭4.0T,香
  • #Ubuntu(修改root信息)
  • (附源码)node.js知识分享网站 毕业设计 202038
  • (附源码)spring boot网络空间安全实验教学示范中心网站 毕业设计 111454
  • (教学思路 C#之类三)方法参数类型(ref、out、parmas)
  • (免费领源码)python+django+mysql线上兼职平台系统83320-计算机毕业设计项目选题推荐
  • (三维重建学习)已有位姿放入colmap和3D Gaussian Splatting训练
  • (十八)devops持续集成开发——使用docker安装部署jenkins流水线服务
  • (转)自己动手搭建Nginx+memcache+xdebug+php运行环境绿色版 For windows版
  • .bat文件调用java类的main方法
  • .Net各种迷惑命名解释
  • /bin、/sbin、/usr/bin、/usr/sbin
  • /boot 内存空间不够
  • @GlobalLock注解作用与原理解析
  • @SpringBootApplication 包含的三个注解及其含义
  • [100天算法】-每个元音包含偶数次的最长子字符串(day 53)