陕西省建设厅申报网站重庆短视频行业
陕西省建设厅申报网站,重庆短视频行业,青岛网站建设青岛新思维,个人网站开发需求分析 前几天写过一篇《一口气说出 9种 分布式ID生成方式#xff0c;面试官有点懵了》#xff0c;里边简单的介绍了九种分布式ID生成方式#xff0c;但是对于像美团#xff08;Leaf#xff09;、滴滴#xff08;Tinyid#xff09;、百度#xff08;uid-generator面试官有点懵了》里边简单的介绍了九种分布式ID生成方式但是对于像美团Leaf、滴滴Tinyid、百度uid-generator都是一笔带过。而通过读者留言发现大家普遍对他们哥三更感兴趣所以后边会结合实战详细的对三种分布式ID生成器学习今天先啃下美团Leaf。
不了解分布式ID的同学先行去看《一口气说出 9种 分布式ID生成方式面试官有点懵了》温习一下基础知识这里就不再赘述了
美团Leaf
Leaf是美团推出的一个分布式ID生成服务名字取自德国哲学家、数学家莱布尼茨的一句话“There are no two identical leaves in the world.”“世界上没有两片相同的树叶”取个名字都这么有寓意美团程序员牛掰啊
Leaf的优势高可靠、低延迟、全局唯一等特点。
目前主流的分布式ID生成方式大致都是基于数据库号段模式和雪花算法snowflake而美团Leaf刚好同时兼具了这两种方式可以根据不同业务场景灵活切换。
接下来结合实战详细的介绍一下Leaf的Leaf-segment号段模式和Leaf-snowflake模式
一、 Leaf-segment号段模式
Leaf-segment号段模式是对直接用数据库自增ID充当分布式ID的一种优化减少对数据库的频率操作。相当于从数据库批量的获取自增ID每次从数据库取出一个号段范围例如 (1,1000] 代表1000个ID业务服务将号段在本地生成1~1000的自增ID并加载到内存.。
大致的流程入下图所示 号段耗尽之后再去数据库获取新的号段可以大大的减轻数据库的压力。对max_id字段做一次update操作update max_id max_id stepupdate成功则说明新号段获取成功新的号段范围是(max_id ,max_id step]。
由于依赖数据库我们先设计一下表结构
CREATE TABLE leaf_alloc (biz_tag varchar(128) NOT NULL DEFAULT COMMENT 业务key,max_id bigint(20) NOT NULL DEFAULT 1 COMMENT 当前已经分配了的最大id,step int(11) NOT NULL COMMENT 初始步长也是动态调整的最小步长,description varchar(256) DEFAULT NULL COMMENT 业务key的描述,update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 数据库维护的更新时间,PRIMARY KEY (biz_tag)
) ENGINEInnoDB DEFAULT CHARSETutf8;预先插入一条测试的业务数据
INSERT INTO leaf_alloc (biz_tag, max_id, step, description, update_time) VALUES (leaf-segment-test, 0, 10, 测试, 2020-02-28 10:41:03);1 biz_tag针对不同业务需求用biz_tag字段来隔离如果以后需要扩容时只需对biz_tag分库分表即可 max_id当前业务号段的最大值用于计算下一个号段 step步长也就是每次获取ID的数量 description对于业务的描述没啥好说的
将Leaf项目下载到本地https://github.com/Meituan-Dianping/Leaf
修改一下项目中的leaf.properties文件添加数据库配置
leaf.namecom.sankuai.leaf.opensource.test
leaf.segment.enabletrue
leaf.jdbc.urljdbc:mysql://127.0.0.1:3306/xin-master?useUnicodetruecharacterEncodingutf8
leaf.jdbc.usernamejunkang
leaf.jdbc.passwordjunkangleaf.snowflake.enablefalse注意leaf.snowflake.enable 与 leaf.segment.enable 是无法同时开启的否则项目将无法启动。
配置相当的简单直接启动LeafServerApplication后就OK了接下来测试一下leaf是基于Http请求的发号服务 LeafController 中只有两个方法一个号段接口一个snowflake接口key就是数据库中预先插入的业务biz_tag。 RestController
public class LeafController {private Logger logger LoggerFactory.getLogger(LeafController.class);Autowiredprivate SegmentService segmentService;Autowiredprivate SnowflakeService snowflakeService;/*** 号段模式* param key* return*/RequestMapping(value /api/segment/get/{key})public String getSegmentId(PathVariable(key) String key) {return get(key, segmentService.getId(key));}/*** 雪花算法模式* param key* return*/RequestMapping(value /api/snowflake/get/{key})public 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());}
}访问http://127.0.0.1:8080/api/segment/get/leaf-segment-test结果正常返回感觉没毛病但当查了一下数据库表中数据时发现了一个问题。 通常在用号段模式的时候取号段的时机是在前一个号段消耗完的时候进行的可刚刚才取了一个ID数据库中却已经更新了max_id也就是说leaf已经多获取了一个号段这是什么鬼操作
Leaf为啥要这么设计呢
Leaf 希望能在DB中取号段的过程中做到无阻塞
当号段耗尽时再去DB中取下一个号段如果此时网络发生抖动或者DB发生慢查询业务系统拿不到号段就会导致整个系统的响应时间变慢对流量巨大的业务这是不可容忍的。
所以Leaf在当前号段消费到某个点时就异步的把下一个号段加载到内存中。而不需要等到号段用尽的时候才去更新号段。这样做很大程度上的降低了系统的风险。
那么某个点到底是什么时候呢
这里做了一个实验号段设置长度为step10max_id1 当我拿第一个ID时看到号段增加了1/10 当我拿第三个Id时看到号段又增加了3/10Leaf采用双buffer的方式它的服务内部有两个号段缓存区segment。当前号段已消耗10%时还没能拿到下一个号段则会另启一个更新线程去更新下一个号段。
简而言之就是Leaf保证了总是会多缓存两个号段即便哪一时刻数据库挂了也会保证发号服务可以正常工作一段时间。 通常推荐号段segment长度设置为服务高峰期发号QPS的600倍10分钟这样即使DB宕机Leaf仍能持续发号10-20分钟不受影响。
优点
Leaf服务可以很方便的线性扩展性能完全能够支撑大多数业务场景。容灾性高Leaf服务内部有号段缓存即使DB宕机短时间内Leaf仍能正常对外提供服务。
缺点
ID号码不够随机能够泄露发号数量的信息不太安全。DB宕机会造成整个系统不可用用到数据库的都有可能。
二、Leaf-snowflake
Leaf-snowflake基本上就是沿用了snowflake的设计ID组成结构正数位占1比特 时间戳占41比特 机器ID占5比特 机房ID占5比特 自增值占12比特总共64比特组成的一个Long类型。
Leaf-snowflake不同于原始snowflake算法地方主要是在workId的生成上Leaf-snowflake依靠Zookeeper生成workId也就是上边的机器ID占5比特 机房ID占5比特。Leaf中workId是基于ZooKeeper的顺序Id来生成的每个应用在使用Leaf-snowflake时启动时都会都在Zookeeper中生成一个顺序Id相当于一台机器对应一个顺序节点也就是一个workId。
Leaf-snowflake启动服务的过程大致如下
启动Leaf-snowflake服务连接Zookeeper在leaf_forever父节点下检查自己是否已经注册过是否有该顺序子节点。如果有注册过直接取回自己的workerIDzk顺序节点生成的int类型ID号启动服务。如果没有注册过就在该父节点下面创建一个持久顺序节点创建成功后取回顺序号当做自己的workerID号启动服务。
但Leaf-snowflake对Zookeeper是一种弱依赖关系除了每次会去ZK拿数据以外也会在本机文件系统上缓存一个workerID文件。一旦ZooKeeper出现问题恰好机器出现故障需重启时依然能够保证服务正常启动。
启动Leaf-snowflake模式也比较简单起动本地ZooKeeper修改一下项目中的leaf.properties文件关闭leaf.segment模式启用leaf.snowflake模式即可。
leaf.segment.enablefalse
#leaf.jdbc.urljdbc:mysql://127.0.0.1:3306/xin-master?useUnicodetruecharacterEncodingutf8
#leaf.jdbc.usernamejunkang
#leaf.jdbc.passwordjunkangleaf.snowflake.enabletrue
leaf.snowflake.zk.address127.0.0.1
leaf.snowflake.port2181/*** 雪花算法模式* param key* return*/RequestMapping(value /api/snowflake/get/{key})public String getSnowflakeId(PathVariable(key) String key) {return get(key, snowflakeService.getId(key));}
测试一下访问http://127.0.0.1:8080/api/snowflake/get/leaf-segment-test优点
ID号码是趋势递增的8byte的64位数字满足上述数据库存储的主键要求。
缺点
依赖ZooKeeper存在服务不可用风险实在不知道有啥缺点了
三、Leaf监控
请求地址http://127.0.0.1:8080/cache
针对服务自身的监控Leaf提供了Web层的内存数据映射界面可以实时看到所有号段的下发状态。比如每个号段双buffer的使用情况当前ID下发到了哪个位置等信息都可以在Web界面上查看。 总结
对于Leaf具体使用哪种模式还是根据具体的业务场景使用本文并没有对Leaf源码做过多的分析因为Leaf 代码量简洁很好阅读。后续还会把其他几种分布式ID生成器依次结合实战介绍给大家欢迎大家关注。 今天就说这么多如果本文对您有一点帮助希望能得到您一个点赞哦
您的认可才是我写作的动力
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/86599.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!