超简单的分布式ID生成方案!美团开源框架介绍

目录

  1. 阐述背景

  2. Leaf snowflake 模式介绍

  3. Leaf segment 模式介绍

  4. Leaf 改造支持 RPC

阐述背景

不吹嘘,不夸张,项目中用到 ID 生成的场景确实挺多。比如业务要做幂等的时候,如果没有合适的业务字段去做唯一标识,那就需要单独生成一个唯一的标识,这个场景相信大家不陌生。

很多时候为了图方便可能就是写一个简单的 ID 生成工具类,直接开用。做的好点的可能单独出一个 Jar 包让其他项目依赖,做的不好的很有可能就是 Copy 了 N 份一样的代码。

单独搞一个独立的 ID 生成服务非常有必要,当然我们也没必要自己做造轮子,有现成开源的直接用就是了。如果人手够,不差钱,自研也可以。

今天为大家介绍一款美团开源的 ID 生成框架 Leaf,在 Leaf 的基础上稍微扩展下,增加 RPC 服务的暴露和调用,提高 ID 获取的性能。

Leaf 介绍

Leaf 最早期需求是各个业务线的订单 ID 生成需求。在美团早期,有的业务直接通过 DB 自增的方式生成 ID,有的业务通过 redis 缓存来生成 ID,也有的业务直接用 UUID 这种方式来生成 ID。以上的方式各自有各自的问题,因此我们决定实现一套分布式 ID 生成服务来满足需求。

具体 Leaf 设计文档见:https://tech.meituan.com/2017/04/21/mt-leaf.html[1]

目前 Leaf 覆盖了美团点评公司内部金融、餐饮、外卖、酒店旅游、猫眼电影等众多业务线。在 4C8G VM 基础上,通过公司 RPC 方式调用,QPS 压测结果近 5w/s,TP999 1ms。

snowflake 模式

snowflake 是 Twitter 开源的分布式 ID 生成算法,被广泛应用于各种生成 ID 的场景。Leaf 中也支持这种方式去生成 ID。

使用步骤如下:

修改配置 leaf.snowflake.enable=true 开启 snowflake 模式。

修改配置 leaf.snowflake.zk.address 和 leaf.snowflake.port 为你自己的 Zookeeper 地址和端口。

想必大家很好奇,为什么这里依赖了 Zookeeper 呢?

那是因为 snowflake 的 ID 组成中有 10bit 的 workerId,如下图:

snowflake组成

一般如果服务数量不多的话手动设置也没问题,还有一些框架中会采用约定基于配置的方式,比如基于 IP 生成 wokerID,基于 hostname 最后几位生成 wokerID,手动在机器上配置,手动在程序启动时传入等等方式。

Leaf 中为了简化 wokerID 的配置,所以采用了 Zookeeper 来生成 wokerID。就是用了 Zookeeper 持久顺序节点的特性自动对 snowflake 节点配置 wokerID。

如果你公司没有用 Zookeeper,又不想因为 Leaf 去单独部署 Zookeeper 的话,你可以将源码中这块的逻辑改掉,比如自己提供一个生成顺序 ID 的服务来替代 Zookeeper。

segment 模式

segment 是 Leaf 基于数据库实现的 ID 生成方案,如果调用量不大,完全可以用 Mysql 的自增 ID 来实现 ID 的递增。

Leaf 虽然也是基于 Mysql,但是做了很多的优化,下面简单的介绍下 segment 模式的原理。

首先我们需要在数据库中新增一张表用于存储 ID 相关的信息。

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;

biz_tag 用于区分业务类型,比如下单,支付等。如果以后有性能需求需要对数据库扩容,只需要对 biz_tag 分库分表就行。

max_id 表示该 biz_tag 目前所被分配的 ID 号段的最大值。

step 表示每次分配的号段长度。

下图是 segment 的架构图:

segment架构

从上图我们可以看出,当多个服务同时对 Leaf 进行 ID 获取时,会传入对应的 biz_tag,biz_tag 之间是相互隔离的,互不影响。

比如 Leaf 有三个节点,当 test_tag 第一次请求到 Leaf1 的时候,此时 Leaf1 的 ID 范围就是 1~1000。

当 test_tag 第二次请求到 Leaf2 的时候,此时 Leaf2 的 ID 范围就是 1001~2000。

当 test_tag 第三次请求到 Leaf3 的时候,此时 Leaf3 的 ID 范围就是 2001~3000。

比如 Leaf1 已经知道自己的 test_tag 的 ID 范围是 1~1000,那么后续请求过来获取 test_tag 对应 ID 时候,就会从 1 开始依次递增,这个过程是在内存中进行的,性能高。不用每次获取 ID 都去访问一次数据库。

问题一

这个时候又有人说了,如果并发量很大的话,1000 的号段长度一下就被用完了啊,此时就得去申请下一个范围,这期间进来的请求也会因为 DB 号段没有取回来,导致线程阻塞。

放心,Leaf 中已经对这种情况做了优化,不会等到 ID 消耗完了才去重新申请,会在还没用完之前就去申请下一个范围段。并发量大的问题你可以直接将 step 调大即可。

问题二

这个时候又有人说了,如果 Leaf 服务挂掉某个节点会不会有影响呢?

首先 Leaf 服务是集群部署,一般都会注册到注册中心让其他服务发现。挂掉一个没关系,还有其他的 N 个服务。问题是对 ID 的获取有问题吗? 会不会出现重复的 ID 呢?

答案是没问题的,如果 Leaf1 挂了的话,它的范围是 1~1000,假如它当前正获取到了 100 这个阶段,然后服务挂了。服务重启后,就会去申请下一个范围段了,不会再使用 1~1000。所以不会有重复 ID 出现。

Leaf 改造支持 RPC

如果你们的调用量很大,为了追求更高的性能,可以自己扩展一下,将 Leaf 改造成 Rpc 协议暴露出去。

首先将 Leaf 的 Spring 版本升级到 5.1.8.RELEASE,修改父 pom.xml 即可。

<spring.version>5.1.8.RELEASE</spring.version>

然后将 Spring Boot 的版本升级到 2.1.6.RELEASE,修改 leaf-server 的 pom.xml。

<spring-boot-dependencies.version>2.1.6.RELEASE</spring-boot-dependencies.version>

还需要在 leaf-server 的 pom 中增加 nacos 相关的依赖,因为我们 kitty-cloud 是用的 nacos。同时还需要依赖 dubbo,才可以暴露 rpc 服务。

<dependency><groupId>com.cxytiandi</groupId><artifactId>kitty-spring-cloud-starter-nacos</artifactId><version>1.0-SNAPSHOT</version>
</dependency>
<dependency><groupId>com.cxytiandi</groupId><artifactId>kitty-spring-cloud-starter-dubbo</artifactId><version>1.0-SNAPSHOT</version>
</dependency>
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId>
</dependency>

在 resource 下创建 bootstrap.properties 文件,增加 nacos 相关的配置信息。

spring.application.name=LeafSnowflake
dubbo.scan.base-packages=com.sankuai.inf.leaf.server.controller
dubbo.protocol.name=dubbo
dubbo.protocol.port=20086
dubbo.registry.address=spring-cloud://localhost
spring.cloud.nacos.discovery.server-addr=47.105.66.210:8848
spring.cloud.nacos.config.server-addr=${spring.cloud.nacos.discovery.server-addr}

Leaf 默认暴露的 Rest 服务是 LeafController 中,现在的需求是既要暴露 Rest 又要暴露 RPC 服务,所以我们抽出两个接口。一个是 Segment 模式,一个是 Snowflake 模式。

Segment 模式调用客户端

/*** 分布式ID服务客户端-Segment模式** @作者 尹吉欢* @个人微信 jihuan900* @微信公众号 猿天地* @GitHub https://github.com/yinjihuan* @作者介绍 http://cxytiandi.com/about* @时间 2020-04-06 16:20*/
@FeignClient("${kitty.id.segment.name:LeafSegment}")
public interface DistributedIdLeafSegmentRemoteService {@RequestMapping(value = "/api/segment/get/{key}")String getSegmentId(@PathVariable("key") String key);
}

Snowflake 模式调用客户端

/*** 分布式ID服务客户端-Snowflake模式** @作者 尹吉欢* @个人微信 jihuan900* @微信公众号 猿天地* @GitHub https://github.com/yinjihuan* @作者介绍 http://cxytiandi.com/about* @时间 2020-04-06 16:20*/
@FeignClient("${kitty.id.snowflake.name:LeafSnowflake}")
public interface DistributedIdLeafSnowflakeRemoteService {@RequestMapping(value = "/api/snowflake/get/{key}")String getSnowflakeId(@PathVariable("key") String key);
}

使用方可以根据使用场景来决定用 RPC 还是 Http 进行调用,如果用 RPC 就@Reference 注入 Client,如果要用 Http 就用@Autowired 注入 Client。

最后改造 LeafController 同时暴露两种协议即可。

@Service(version = "1.0.0", group = "default")
@RestController
public class LeafController implements DistributedIdLeafSnowflakeRemoteService, DistributedIdLeafSegmentRemoteService {private Logger logger = LoggerFactory.getLogger(LeafController.class);@Autowiredprivate SegmentService segmentService;@Autowiredprivate SnowflakeService snowflakeService;@Overridepublic String getSegmentId(@PathVariable("key") String key) {return get(key, segmentService.getId(key));}@Overridepublic String getSnowflakeId(@PathVariable("key") String key) {return get(key, snowflakeService.getId(key));}private String get(@PathVariable("key") String key, Result id) {Result result;if (key == null || key.isEmpty()) {throw new NoKeyException();}result = id;if (result.getStatus().equals(Status.EXCEPTION)) {throw new LeafServerException(result.toString());}return String.valueOf(result.getId());}
}

扩展后的源码参考:https://github.com/yinjihuan/Leaf/tree/rpc_support[2]

参考资料

[1]

mt-leaf.html: https://tech.meituan.com/2017/04/21/mt-leaf.html

[2]

Leaf/tree/rpc_support: https://github.com/yinjihuan/Leaf/tree/rpc_support

[3]

kitty: https://github.com/yinjihuan/kitty

往期推荐

4种分布式Session的实现方式!老大直呼666...

最简单的6种防止数据重复提交的方法!(干货)

关注下方二维码,每一天都有干货!

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

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

相关文章

教你写Bug,常见的 OOM 异常分析

在《Java虚拟机规范》的规定里&#xff0c;除了程序计数器外&#xff0c;虚拟机内存的其他几个运行时区域都有发生 OutOfMemoryError 异常的可能。本篇主要包括如下 OOM 的介绍和示例&#xff1a;java.lang.StackOverflowErrorjava.lang.OutOfMemoryError: Java heap spacejava…

池化技术到达有多牛?看了线程和线程池的对比吓我一跳!

这是我的第 82 篇原创文章作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;情商高的人是能洞察并照顾到身边所有人的情绪&#xff0c;而好的文章应该让所有人都能看懂。尼采曾…

spearman相关性_Spearman的相关性及其在机器学习中的意义

spearman相关性This article is about correlation and its implication in the machine learning. In my previous article, I have discussed Pearson’s correlation coefficient and later we have written a code to show the usefulness of finding Pearson’s correlati…

被问哭了,一位小姐姐的阿里面经!(附部分答案)

这篇文章是一位 女读者 &#xff08;加粗&#xff01;太难得&#xff09;的面试阿里的经历分享&#xff0c;虽然第二面就失败了&#xff0c;但是这样的经历对自己帮助应该还是很大的。下面的一些问题非常具有代表性&#xff0c;部分问题我简单做了修改&#xff08;有些问题表述…

阿里《Java开发手册》最新嵩山版发布!

《Java 开发手册》是阿里巴巴集团技术团队的集体智慧结晶和经验总结&#xff0c;经历了多次大规模一线实战的检验及不断完善&#xff0c;公开到业界后&#xff0c;众多社区开发者踊跃参与&#xff0c;共同打磨完善&#xff0c;系统化地整理成册&#xff0c;当前的版本是嵩山版。…

递归转化成非递归过程_8086微处理器中的递归和重入过程

递归转化成非递归过程As we all know that a procedure is a set of instruction written separately which can be used any time in the code when required. A normal procedure execution includes calling of the procedure, shifting the control of the processor to th…

漫谈软件研发特种部队之中的一个

特种部队&#xff0c;是指进行特殊任务的部队&#xff0c;具有编制灵活、人员精干、装备精良、机动高速、训练有素、战斗力强等特点。 特种部队最早出如今二战期间。德国于1939年9月1日的波兰战役中首次投入了一种被称为“勃兰登堡”部队的特种部队作为德国突击波兰的先锋&…

不要一把梭了,这才是SQL优化的正确姿势!|原创干货

这是我的第 83 篇原创文章作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;年少不知优化苦&#xff0c;遇坑方知优化难。——村口王大爷全文内容预览&#xff1a;我之前有很多…

PLSQL_性能优化系列10_Oracle Array数据组优化

2014-09-25 Created By BaoXinjian 一、摘要 集合是Oracle开发中经常遇到的情况&#xff0c;Oracle集合分为三种情况&#xff1a;索引表集合(index by table)、嵌套表集合(nested table)、可变集合(varry table)。 PL/SQL中没有数组的概念&#xff0c;他的集合数据类型和数组是…

IDEA 2020.2 重磅发布,动画级新功能预览!

Guide 关注了 IDEA 的官推&#xff0c;平时没事就会去看看有没有啥比较好的更新。今天下午看到IntelliJ IDEA 2020.2 都已经发布并且还支持了 Java15。然后&#xff0c;我就去官网简单看了一下新特性。单看新特性&#xff0c;这个新版本还是有一点香的。虽然我还木有升级到这个…

访问控制模型ACL和RBAC

2019独角兽企业重金招聘Python工程师标准>>> 1.ACL ACL是最早也是最基本的一种访问控制机制&#xff0c;它的原理非常简单&#xff1a;每一项资源&#xff0c;都配有一个列表&#xff0c;这个列表记录的就是哪些用户可以对这项资源执行CRUD中的那些操作。当系统试图…

最常见并发面试题整理!(速度收藏)

前言并发编程是面试中必问的知识点之一&#xff0c;所以本文整理了一些最为常见的并发面试题&#xff0c;一起来看吧~1. synchronized的实现原理以及锁优化&#xff1f;synchronized的实现原理synchronized作用于「方法」或者「代码块」&#xff0c;保证被修饰的代码在同一时间…

JavaScript中的嵌套事件处理(在鼠标移动事件上)

Multiple event handling is the secret ingredient of dynamic WebPages seen now-a-days. 多重事件处理是当今动态网页的秘密组成部分。 Now, let’s get started... 现在&#xff0c;让我们开始吧... Example Code 范例程式码 <html lang"en"><head&…

皮尔逊相关性_皮尔逊的相关性及其在机器学习中的意义

皮尔逊相关性Today we would be using a statistical concept i.e. Pearsons correlation to help us understand the relationships between the feature values (independent values) and the target value (dependent value or the value to be predicted ) which will furt…

磊哥最近面试了好多人,聊聊我的感受!(附面试知识点)

这是我的第 84 篇原创文章作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;一些读者可能知道&#xff0c;磊哥前段时间又回来上班了&#xff0c;详见《磊哥又滚回职场了...》…

M4 宏处理器

2019独角兽企业重金招聘Python工程师标准>>> M4 宏处理器 Brian W. KernighanDennis M. Ritchie Bell LaboratoriesMurray Hill, New Jersey 07974 翻译&#xff1a;寒蝉退士 译者声明&#xff1a;译者对译文不做任何担保&#xff0c;译者对译文不拥有任何权利并且不…

绝了,几款主流的 JSON 库性能对比!

本篇通过JMH&#xff08;Oracle官方测试框架&#xff09;来测试一下Java中几种常见的JSON解析库的性能。每次都在网上看到别人说什么某某库性能是如何如何的好&#xff0c;碾压其他的库。但是百闻不如一见&#xff0c;只有自己亲手测试过的才是最值得相信的。JSON不管是在Web开…

DWZ使用笔记

DWZ使用笔记 一、前言 在近期的一个项目中&#xff0c;引入了DWZ这个富client框架&#xff0c;算是一次尝试吧。期间也遇到不少问题&#xff0c;总算一一攻克了。特以此文记之。本人用的是dwz-ria-1.4.5Asp.net webform&#xff0c;写这篇笔记时最新版本号已经是1.4.6了。DWZ官…

阿里的简历多久可以投递一次?次数多了有没有影响?可以同时进行吗?

最近&#xff0c;无论是读者群&#xff0c;还是公众号后台&#xff0c;很多人都比较关注以下几个问题&#xff1a;阿里的简历是半年只能投递一次吗&#xff1f;阿里的面试可以多个部门同时进行吗&#xff1f;面试没过&#xff0c;又被系统捞起来了&#xff0c;我该怎么办&#…

记一次蚂蚁金服面试被虐经历

本文来自作者投稿&#xff0c;原作者&#xff1a;yes面试前的小姐姐来说说前不久蚂蚁金服一面的情况。说来也是巧合&#xff0c;当时在群里有位蚂蚁金服的小姐姐发了个内推&#xff0c;看了下JD感觉可以试试于是就私聊了小姐姐发简历内推了。我16年也就是大三上就开始实习了&am…