IT博文
MySQL 事务隔离级别详解
使用 docker compose 安装 tidb
架构师日记-如何写的一手好代码
生产事故-记一次特殊的OOM排查
Docker安装RabbitMQ——基于docker-compose工具
使用 docker-compose 部署单机 RabbitMQ
只需3步,即刻体验Oracle Database 23c
长达 1.7 万字的 explain 关键字指南!
Redis为什么能抗住10万并发?揭秘性能优越的背后原因
深度剖析Redis九种数据结构实现原理
【绩效季】遇到一个好领导有多重要,从被打差绩效到收获成长
为什么Redis不直接使用C语言的字符串?
Java阻塞队列中的异类,SynchronousQueue底层实现原理剖析
如何调整和优化 Go 程序的内存管理方式?
应用部署引起上游服务抖动问题分析及优化实践方案
Java 并发工具合集 JUC 大爆发!!!
卷起来!!这才是 MySQL 事务 & MVCC 的真相。
JDK8 到 JDK17 有哪些吸引人的新特性?
告别StringUtil:使用Java 11的全新String API优化你的代码
从JDK8飞升到JDK17,再到未来的JDK21
Java JMH Benchmark Tutorial
linux和macOS下top命令区别
Windows10关闭Hyper-V的三种方法
为什么应该选择 POSTGRES?
阿里云对象存储 OSS 限流超过阈值自动关闭【防破产,保平安】
Java高并发革命!JDK19新特性——虚拟线程(Virtual Threads)
“请不要在虚拟机中运行此程序”的解决方案
Spring中的循环依赖及解决
浅谈复杂业务系统的架构设计 | 京东云技术团队
面试题:聊聊TCP的粘包、拆包以及解决方案
操作日志记录实现方式
字节跳动技术团队-慢 SQL 分析与优化
Spring Boot 使用 AOP 防止重复提交
Controller层代码就该这么写,简洁又优雅!
SpringBoot 项目 + JWT 完成用户登录、注册、鉴权
重复提交不再是问题!SpringBoot自定义注解+AOP巧妙解决
SpringBoot 整合 ES 实现 CRUD 操作
SpringBoot 整合 ES 进行各种高级查询搜索
SpringBoot操作ES进行各种高级查询
SpringBoot整合ES查询
如何做架构设计? | 京东云技术团队
最值得推荐的五个VPN软件(便宜+好用+稳定),靠谱的V2ray梯子工具
我说MySQL每张表最好不超过2000万数据,面试官让我回去等通知?
vivo 自研鲁班分布式 ID 服务实践
使用自带zookeeper超简单安装kafka
推荐 6 个很牛的 IDEA 插件
喜马拉雅 Redis 与 Pika 缓存使用军规
「程序员转型技术管理」必修的 10 个能力提升方向
jdk17 下 netty 导致堆内存疯涨原因排查 | 京东云技术团队
如何优雅做好项目管理?
MySQL 到 TiDB:Hive Metastore 横向扩展之路
聊聊即将到来的 MySQL5.7 停服事件
Linux终端环境配置
微软 Edge 浏览器隐藏功能一览:多线程下载、IE 模式、阻止视频自动播放等
Hutool 中那些常用的工具类和实用方法
clash 内核删库?汇总目前常用的内核仓库和客户端
JDK11 升级 JDK17 最全实践干货来了 | 京东云技术团队
我是如何写一篇技术文的?
虚拟线程原理及性能分析
Java线程池实现原理及其在美团业务中的实践
Editplus和EmEditor配置一键编译java运行环境
用Spring Boot 3.2虚拟线程搭建静态文件服务器有多快?
SpringBoot中使用LocalDateTime踩坑记录 - 程序员偏安 - 博客园
程序员必备!10款实用便捷的Git可视化管理工具 - 追逐时光者 - 博客园
基于Netty开发轻量级RPC框架
开发Java应用时如何用好Log
复杂SQL治理实践 | 京东物流技术团队
火山引擎ByteHouse:分析型数据库如何设计并发控制?
多次崩了之后,阿里云终于改了
推荐程序员必知的四大神级学习网站
初探分布式链路追踪
新项目为什么决定用 JDK 17了
Java上进了,JDK21 要来了,并发编程再也不是噩梦了
mapstruct这么用,同事也开始模仿
再见RestTemplate,Spring 6.1新特性:RestClient 了解一下!
【MySQL】MySQL表设计的经验(建议收藏)
如何正确地理解应用架构并开发
解读工行专利CN112905176B
工商银行取得「基于 Spring Boot 的 web 系统后端实现方法及装置」专利
IDEA 2024.1:Spring支持增强、GitHub Action支持增强、更新HTTP Client等
TIOBE 2 月:Go 首次进入前十、“上古语言” COBOL 和 Fortran 排名飙升
Java 21 虚拟线程如何限流控制吞吐量
🎉 通用、灵活、高性能分布式 ID 生成器 | CosId 2.6.6 发布
20年编程,AI编程6个月,关于Copliot辅助编码工具,你想知道的都在这里
Java 8 内存管理原理解析及内存故障排查实践
消息队列选型之 Kafka vs RabbitMQ
从 MongoDB 到 PostgreSQL 的大迁移
腾讯云4月8日故障复盘及情况说明
PHP 在 2024 年还值得学习吗?
AMD集显安装显卡驱动之后出现黑屏,建议这样解决
使用 Docker 部署 moments 微信朋友圈 - 谱次· - 博客园
Java 17 是最常用的 Java LTS 版本
盘点Lombok的几个骚操作
Llama 3 + Ollama + Open WebUI打造本机强大GPT
如何优雅地编写缓存代码
Gmeek快速上手
笔记软件思源远程和本地接入大语言模型服务Ollama实现AI辅助写作(Windows篇)
Git Subtree:简单粗暴的多项目管理神器
这款轻量级规则引擎,真香!!
Ollama教程:本地LLM管理、WebUI对话、Python/Java客户端API应用
GLM-4-9B支持 Ollama 部署
智谱AI开源代码生成大模型第四代版本:CodeGeeX4-ALL-9B
美团二面:如何保证Redis与Mysql双写一致性?连续两个面试问到了!
免费开源好用,Obsidian和Omnivore真正实现一键联动剪藏文章,手把手教程!
得物 Redis 设计与实践
架构图怎么画?手把手教您,以生鲜电商为例剖析业务/应用/数据/技术架构图
使用Hutool要注意了!升级到6.0后你调用的所有方法都将报错 - 掘金
别再用雪花算法生成ID了!试试这个吧
无敌的Arthas!
Navicat Premium v16、v17 破解激活
🎉 分布式接口文档聚合,Solon 是怎么做的?
深入体验全新 Cursor AI IDE 后,说杀疯了真不为过!
Nacos 3.0 架构全景解读,AI 时代服务注册中心的演进
本文档使用 MrDoc 发布
-
+
vivo 自研鲁班分布式 ID 服务实践
作者:vivo IT 平台团队 - An Peng 本文介绍了什么是分布式 ID,分布式 ID 的业务场景以及 9 种分布式 ID 的实现方式,同时基于 vivo 内部 IT 的业务场景,介绍了自研鲁班分布式 ID 服务的实践。 一、方案背景 1.1 分布式 ID 应用的场景 随着系统的业务场景复杂化、架构方案的优化演进,我们在克服问题的过程中,也总会延伸出新的技术诉求。分布式 ID 也是诞生于这样的 IT 发展过程中,在不同的关联模块内,我们需要一个全局唯一的 ID 来让模块既能并行地解耦运转,也能轻松地进行整合处理。以下,首先让我们一起回顾这些典型的分布式 ID 场景。 **1.1.1 系统分库分表** 随着系统的持续运作,常规的**单库单表**在支撑更高规模的数量级时,无论是在性能或稳定性上都已经难以为继,需要我们对目标逻辑数据表进行合理的物理拆分,这些同一业务表数据的拆分,需要有一套完整的 ID 生成方案来保证拆分后的各物理表中同一业务 ID 不相冲突,并能在后续的合并分析中可以方便快捷地计算。 以公司的营销系统的订单为例,当前**不但以分销与零售的目标组织区别来进行分库存储,来实现多租户的数据隔离,并且会以订单的业务属性(订货单、退货单、调拔单等等)来进一步分拆订单数据**。在订单创建的时候,根据这些规则去构造全局唯一 ID,创建订单单据并保存在对应的数据库中;在通过订单号查询时,通过 ID 的规则,快速路由到对应的库表中查询;在 BI 数仓的统计业务里,又需要汇总这些订单数据进行报表分析。 **1.1.2 系统多活部署** 无论是面对着全球化的**各国数据合规诉求**,还是针对**容灾高可用的架构设计**,我们都会对同一套系统进行多活部署。多活部署架构的各单元化服务,存储的单据(如订单 / 出入库单 / 支付单等)均带有部署区域属性的 ID 结构去构成全局唯一 ID,创建单据并保存在对应单元的数据库中,在前端根据单据号查询的场景,通过 ID 的规则,可快速路由到对应的单元区域进行查询。对应多活部署架构的中心化服务,同步各单元的单据数据时,单据的 ID 是全局唯一,避免了汇聚数据时的 ID 冲突。 在公司的系统部署中,公共领域的 BPM 、待办、营销领域的系统都大范围地实施多活部署。 **1.1.3 链路跟踪技术** 在微服务架构流行的大背景下,此类微服务的应用对比单体应用的调用链路会更长、更复杂,对问题的排查带来了挑战,应对该场景的解决方案,会在流量入口处产生全局唯一的 TraceID,并在各微服务之间进行透传,进行流量染色与关联,后续通过该**全局唯一的 TraceID**,可快速地查询与关联全链路的调用关系与状态,快速定位根因问题。 在公司的各式各样的监控系统、灰度管理平台、跨进程链路日志中,都会伴随着这么一个技术组件进行支撑服务。 1.2 分布式 ID 核心的难点 - **唯一性**: 保持生成的 ID 全局唯一,在任何情况下也不会出现重复的值(如防止时间回拔,时钟周期问题)。 - **高性能**: ID 的需求场景多,中心化生成组件后,需要高并发处理,以接近 0ms 的响应大规模并发执行。 - **高可用**: 作为 ID 的生产源头,需要 100% 可用,当接入的业务系统多的时候,很难调整出各方都可接受的停机发布窗口,只能接受无损发布。 - **易接入**: 作为逻辑上简单的分布式 ID 要推广使用,必须强调开箱即用,容易上手。 - **规律性**: 不同业务场景生成的 ID 有其特征,例如有固定的前后缀,固定的位数,这些都需要配置化管理。 1.3 分布式 ID 常见的方案 常用系统设计中主要有下图 9 种 ID 生成的方式:   1.4 分布式 ID 鲁班的方案 我们的系统跨越了**公共、生产制造、营销、供应链、财经**等多个领域。在分布式 ID 诉求下还有如下的**特点**: - 在业务场景上除了常规的 **Long 类型** ID,也需要支持 “**String 类型**”、“**MixId 类型**”(后详述)等多种类型的 ID 生成,每一种类型也需要支持不同的长度的 ID。 - 在 ID 的构成规则上需要涵盖如操作类型、**区域**、**代理**等业务属性的标识;需要集中式的配置管理。 - 在一些特定的业务上,基于安全的考虑,还需要在尾部加上随机数来保证 ID 不能被轻易猜测。 综合参考了业界优秀的开源组件与常用方案均不能满足,为了统一管理这类基础技术组件的诉求,我们选择基于公司业务场景自研一套分布式 ID 服务:**鲁班分布式 ID 服务**。 二、系统架构  2.1 架构说明  三、 设计要点 3.1 支持多种类型的 ID 规则 目前鲁班分布式 ID 服务共提供 "**Long 类型** "、“**String 类型**”、“**MixId 类型**” 等三种主要类型的 ID,相关 ID 构成规则与说明如下: **3.1.2 Long 类型** **(1)构成规则** 静态结构由以下三部分数据组成,组成部分共 **19 位**: - **固定部分(4 位)**: 由 FixPart+ServerPart 组成。 **①** **FixPart(4 位)**:由大区 zone 1 位 / 代理 agent 1 位 / 项目 project 1 位 / 应用 app 1 位,组成的 4 位数字编码。 **②** **ServerPart(4 位)**:用于定义产生全局 ID 的服务器标识位,服务节点部署时动态分配。 - **动态部分 DynPart(13 位)**: System.currentTimeMillis ()- 固定配置时间的 TimeMillis (可满足使用 100 年)。 - **自增部分 SelfIncreasePart(2 位)**:用于在全局 ID 的客户端 SDK 内部自增部分,由客户端 SDK 控制,业务接入方无感知。共 2 位组成。 **(2)降级机制** 主要自增部分在服务器获取初始值后,由客户端 SDK 维护,直到自增 99 后再次访问服务端获取下一轮新的 ID 以减少服务端交互频率,提升性能,服务端获取失败后抛出异常,接入业务侧需介入进行处理。 **(3)样例说明**  **3.1.2 String 类型** **(1)构成规则** 静态结构由以下五部分数据组成,组成部分共 **25~27 位**: - **固定部分操作位 op+FixPart(9~11 位)**: **① 操作位 op(2~4 位)**:2~4 位由业务方传入的业务类型标识字符。 **② FixPart(7 位)**:业务接入时申请获取,由大区 zone 1 位,代理 agent 2 位,项目 project 2 位,应用 app 2 位组成。 - **服务器标识部分 ServerPart(1 位)**: 用于定义产生全局 ID 的服务器标识位,服务节点部署时动态分配 A~Z 编码。 - **动态部分 DynPart(9 位)**: System.currentTimeMillis ()- 固定配置时间的 TimeMillis ,再转换为 32 进制字符串(可满足使用 100 年)。 - **自增部分 SelfIncreasePart(3 位)**:用于在全局 ID 的客户端 SDK 内部自增部分,由客户端 SDK 控制,业务接入方无感知。 - **随机部分 secureRandomPart(3 位)**:用于在全局 ID 的客户端 SDK 的随机部分,由 SecureRandom 随机生成 3 位 0-9,A-Z 字母数字组合的安全随机数,业务接入方无感知。 **(2)降级机制** 主要自增部分由客户端 SDK 内部维护,一般情况下只使用 001–999 共 999 个全局 ID。也就是每向服务器请求一次,都在客户端内可以自动维护 999 个唯一的全局 ID。特殊情况下在访问服务器连接出问题的时候,可以使用带字符的自增来做服务器降级处理,使用产生 00A, 00B... 0A0, 0A1,0A2....ZZZ. 共有 36 \* 36 \* 36 - 1000 (999 纯数字,000 不用)**\= 45656 个降级使用的全局 ID**。 **(3)样例说明**  **3.1.3 MixId 类型** **(1)构成规则** 静态结构由以下三部分数据组成,组成部分共 **17 位**: - **固定部分 FixPart (4~6 位)**: **① 操作位 op(2~4 位)**:2~4 位由业务方传入的业务类型标识字符 **② FixPart(2 位)**:业务接入时申请获取由代理 agent 2 位组成。 - **动态部分 DynPart(6 位)**: 生成 ID 的时间,年(2 位)月(2 位)日(2 位)。 - **自增部分 SelfIncreasePart(7 位)**:用于在全局 ID 的客户端 SDK 内部自增部分,由客户端 SDK 控制,业务接入方无感知。 **(2)降级机制** 无,每次 ID 产生均需到服务端请求获取,服务端获取失败后抛出异常,接入业务侧需介入进行处理。 **(3)样例说明**  3.2 业务自定义 ID 规则实现 鲁班分布式 ID 服务内置 “Long 类型”,“String 类型”,“MixId 类型” 等三种长度与规则固定的 ID 生成算法,除以上三种类型的 ID 生成算法外,业务侧往往有自定义 ID 长度与规则的场景诉求,在鲁班分布式 ID 服务内置 ID 生成算法未能满足业务场景时,为了能在该场景快速支持业务,鲁班分布式 ID 服务提供了业务自定义接口并通过 SPI 机制在服务运行时动态加载,以实现业务自定义 ID 生成算法场景的支持,相关能力的**实现设计与接入流程**如下: (1)ID 的构成部分主要分 FixPart、DynPart、SelfIncreasePart 三个部分。 (2)鲁班分布式 ID 服务的客户端 SDK 提供 **LuBanGlobalIDClient 的接口与 getGlobalId(...)/****setFixPart (...)/setDynPart (...)/setSelfIncreasePart (...) 等四个接口方法**。 (3)业务侧实现 LuBanGlobalIDClient 接口内的 4 个方法,通过 SPI 机制在业务侧服务进行加载,并向外暴露出 HTTP 或 DUBBO 协议的接口。 (4)用户在鲁班分布式 ID 服务管理后台对自定义 ID 生成算法的类型名称与服务地址信息进行配置,并关联需要使用的 AK 接入信息。 (5)业务侧使用时调用客户端 SDK 提供的 LuBanGlobalIDClient 的接口与 getGlobalId 方法,并传入 ID 生成算法类型与 IdRequest 入参对象,鲁班分布式 ID 服务接收请求后,动态识别与路由到对应 ID 生产算法的实现服务,并构建对象的 ID 返回给客户端,完成整个 ID 生成与获取的过程。 3.3 保证 ID 生成不重复方案  3.4 ID 服务无状态无损管理 服务部署的环境在虚拟机上,ip 是固定,常规的做法是在配置表里配置 ip 与机器码的绑定关系(这样在服务扩缩容的时候就需要人为介入操作,存在一定的遗漏配置风险,也带来了一定的运维成本),但在容器的部署场景,因为每次部署时 IP 均是动态变化的,以前通过配置表里 ip 与机器码的映射关系的配置实现方式显然不能满足运行在容器场景的诉求,故在服务端设计了通过心跳上报实现机器码动态分配的机制,实现服务端节点 ip 与机器码动态分配、绑定的能力,达成部署自动化与无损发布的目的。 相关流程如下:  **【注意】** 服务端节点可能因为异常,非正常地退出,对于该场景,这里就需要有一个解绑的过程,当前实现是通过公司平台团队的分布式定时任务服务,检查持续 5 分钟 (可配置) 没有上报心跳的机器码分配节点进行数据库绑定信息清理的逻辑,重置相关机器码的位置供后续注册绑定使用。 3.5 ID 使用方接入 SDK 设计 SDK 设计主要以 " **接入快捷,使用简单** " 的原则进行设计。 (1)接入时: 鲁班分布式 ID 服务提供了 spring-starter 包,应用只需再 pom 文件依赖该 starter,在启动类里添加 **@EnableGlobalClient**,并配置 **AK/SK** 等租户参数即可完成接入。 同时鲁班分布式 ID 服务提供 Dubbo & Http 的调用方式,通过在启动注解配置 **accessType** 为 **HTTP/DUBBO** 来确定,SDK 自动加载相关依赖。 (2)使用时: 根据 "**Long**"、"**String**"、"**MixId**" 等三种 id 类型分别提供 **GlobalIdLongClient**、 **GlobalIdStringClient**、**GlobalIdMixIDClient** 等三个客户端对象,并封装了统一的入参 **RequestDTO** 对象,业务系统使用时只需构建对应 Id 类型的 RequestDTO 对象(支持链式构建),并调用对应 id 类型的客户端对象 **getGlobalID****(GlobalBaseRequestDTO** **globalBaseRequestDTO)**方法,即可完成 ID 的构建。 Long 类型 Id 获取代码示例 ``` package com.vivo.it.demo.controller; ``` 3.6 关键运行性能优化场景 **3.6.1 内存使用优化** 在项目上线初时,经常发生 FGC,导致服务停顿,获取 ID 超时,经过分析,鲁班分布式 ID 服务的服务端主要为内存敏感的应用,当高并发请求时,过多对象进入老年代从而触发 FGC,经过排查主要是 JVM 内存参数上线时是使用默认的,没有经过优化配置,JVM 初始化的内存较少,高并发请求时 JVM 频繁触发内存重分配,相关的对象也流程老年代导致最终频繁发送 FGC。 对于这个场景的优化思路主要是要相关内存对象在年轻代时就快速经过 YGC 回收,尽量少的对象进行老年代而引起 FGC。 基于以上的思路主要做了以下的优化: - 增大 JVM 初始化内存(-Xms,容器场景里为 - XX:InitialRAMPercentage) - 增大年轻代内存(-Xmn) - 优化代码,减少代码里临时对象的复制与创建 **3.6.2 锁颗粒度优化** 客户端 SDK 再自增值使用完或一定时间后会向服务端请求新的 id 生成,这个时候需要保证该次请求在多线程并发时是只请求一次,当前设计是基于用户申请 ID 的接入配置,组成为 key,去获取对应 key 的对象锁,以减少同步代码块锁的粒度,避免不同接入配置去在并发去远程获取新的 id 时,锁粒度过大,造成线程的阻塞,从而提升在高并发场景下的性能。 四、 业务应用 当前鲁班分布式 ID 服务日均 ID 生成量**亿级**,平均 RT 在 **0~1ms** 内,单节点可支持 **万级** QPS,已全面应用在公司 IT 内部**营销订单、支付单据、库存单据、履约单据、资产管理编码**等多个领域的业务场景。 五、未来规划 在可用性方面,当前鲁班分布式 ID 服务仍对 Redis、Mysql 等外部 DB 组件有一定的依赖(如应用接入配置信息、MixId 类型自增部分 ID 计数器),规划在该依赖极端宕机的场景下,鲁班分布式 ID 服务仍能有一些降级策略,为业务提供可用的服务。 同时基于业务场景的诉求,支持标准形式的雪花算法等 ID 类型。 六、 回顾总结 本文通过对分布式 ID 的 3 种应用场景,实现难点以及 9 种分布式 ID 的实现方式进行介绍,并对结合 vivo 业务场景特性下自研的鲁班分布式 id 服务从系统架构,ID 生成规则与部分实现源码进行介绍,希望对本文的阅读者在分布式 ID 的方案选型或自研提供参考。
admin
2023年7月2日 17:52
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
PDF文档(打印)
分享
链接
类型
密码
更新密码