easyui treegrid获取父节点的id_超简单的分布式ID生成方案!美团开源框架介绍
目錄
闡述背景
不吹噓,不夸張,項(xiàng)目中用到 ID 生成的場(chǎng)景確實(shí)挺多。比如業(yè)務(wù)要做冪等的時(shí)候,如果沒(méi)有合適的業(yè)務(wù)字段去做唯一標(biāo)識(shí),那就需要單獨(dú)生成一個(gè)唯一的標(biāo)識(shí),這個(gè)場(chǎng)景相信大家不陌生。
很多時(shí)候?yàn)榱藞D方便可能就是寫(xiě)一個(gè)簡(jiǎn)單的 ID 生成工具類(lèi),直接開(kāi)用。做的好點(diǎn)的可能單獨(dú)出一個(gè) Jar 包讓其他項(xiàng)目依賴(lài),做的不好的很有可能就是 Copy 了 N 份一樣的代碼。
單獨(dú)搞一個(gè)獨(dú)立的 ID 生成服務(wù)非常有必要,當(dāng)然我們也沒(méi)必要自己做造輪子,有現(xiàn)成開(kāi)源的直接用就是了。如果人手夠,不差錢(qián),自研也可以。
今天為大家介紹一款美團(tuán)開(kāi)源的 ID 生成框架 Leaf,在 Leaf 的基礎(chǔ)上稍微擴(kuò)展下,增加 RPC 服務(wù)的暴露和調(diào)用,提高 ID 獲取的性能。
Leaf 介紹
Leaf 最早期需求是各個(gè)業(yè)務(wù)線的訂單 ID 生成需求。在美團(tuán)早期,有的業(yè)務(wù)直接通過(guò) DB 自增的方式生成 ID,有的業(yè)務(wù)通過(guò) redis 緩存來(lái)生成 ID,也有的業(yè)務(wù)直接用 UUID 這種方式來(lái)生成 ID。以上的方式各自有各自的問(wèn)題,因此我們決定實(shí)現(xiàn)一套分布式 ID 生成服務(wù)來(lái)滿(mǎn)足需求。
具體 Leaf 設(shè)計(jì)文檔見(jiàn):https://tech.meituan.com/2017/04/21/mt-leaf.html[1]
目前 Leaf 覆蓋了美團(tuán)點(diǎn)評(píng)公司內(nèi)部金融、餐飲、外賣(mài)、酒店旅游、貓眼電影等眾多業(yè)務(wù)線。在 4C8G VM 基礎(chǔ)上,通過(guò)公司 RPC 方式調(diào)用,QPS 壓測(cè)結(jié)果近 5w/s,TP999 1ms。
snowflake 模式
snowflake 是 Twitter 開(kāi)源的分布式 ID 生成算法,被廣泛應(yīng)用于各種生成 ID 的場(chǎng)景。Leaf 中也支持這種方式去生成 ID。
使用步驟如下:
修改配置 leaf.snowflake.enable=true 開(kāi)啟 snowflake 模式。
修改配置 leaf.snowflake.zk.address 和 leaf.snowflake.port 為你自己的 Zookeeper 地址和端口。
想必大家很好奇,為什么這里依賴(lài)了 Zookeeper 呢?
那是因?yàn)?snowflake 的 ID 組成中有 10bit 的 workerId,如下圖:
一般如果服務(wù)數(shù)量不多的話(huà)手動(dòng)設(shè)置也沒(méi)問(wèn)題,還有一些框架中會(huì)采用約定基于配置的方式,比如基于 IP 生成 wokerID,基于 hostname 最后幾位生成 wokerID,手動(dòng)在機(jī)器上配置,手動(dòng)在程序啟動(dòng)時(shí)傳入等等方式。
Leaf 中為了簡(jiǎn)化 wokerID 的配置,所以采用了 Zookeeper 來(lái)生成 wokerID。就是用了 Zookeeper 持久順序節(jié)點(diǎn)的特性自動(dòng)對(duì) snowflake 節(jié)點(diǎn)配置 wokerID。
如果你公司沒(méi)有用 Zookeeper,又不想因?yàn)?Leaf 去單獨(dú)部署 Zookeeper 的話(huà),你可以將源碼中這塊的邏輯改掉,比如自己提供一個(gè)生成順序 ID 的服務(wù)來(lái)替代 Zookeeper。
segment 模式
segment 是 Leaf 基于數(shù)據(jù)庫(kù)實(shí)現(xiàn)的 ID 生成方案,如果調(diào)用量不大,完全可以用 Mysql 的自增 ID 來(lái)實(shí)現(xiàn) ID 的遞增。
Leaf 雖然也是基于 Mysql,但是做了很多的優(yōu)化,下面簡(jiǎn)單的介紹下 segment 模式的原理。
首先我們需要在數(shù)據(jù)庫(kù)中新增一張表用于存儲(chǔ) ID 相關(guān)的信息。
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 用于區(qū)分業(yè)務(wù)類(lèi)型,比如下單,支付等。如果以后有性能需求需要對(duì)數(shù)據(jù)庫(kù)擴(kuò)容,只需要對(duì) biz_tag 分庫(kù)分表就行。
max_id 表示該 biz_tag 目前所被分配的 ID 號(hào)段的最大值。
step 表示每次分配的號(hào)段長(zhǎng)度。
下圖是 segment 的架構(gòu)圖:
從上圖我們可以看出,當(dāng)多個(gè)服務(wù)同時(shí)對(duì) Leaf 進(jìn)行 ID 獲取時(shí),會(huì)傳入對(duì)應(yīng)的 biz_tag,biz_tag 之間是相互隔離的,互不影響。
比如 Leaf 有三個(gè)節(jié)點(diǎn),當(dāng) test_tag 第一次請(qǐng)求到 Leaf1 的時(shí)候,此時(shí) Leaf1 的 ID 范圍就是 1~1000。
當(dāng) test_tag 第二次請(qǐng)求到 Leaf2 的時(shí)候,此時(shí) Leaf2 的 ID 范圍就是 1001~2000。
當(dāng) test_tag 第三次請(qǐng)求到 Leaf3 的時(shí)候,此時(shí) Leaf3 的 ID 范圍就是 2001~3000。
比如 Leaf1 已經(jīng)知道自己的 test_tag 的 ID 范圍是 1~1000,那么后續(xù)請(qǐng)求過(guò)來(lái)獲取 test_tag 對(duì)應(yīng) ID 時(shí)候,就會(huì)從 1 開(kāi)始依次遞增,這個(gè)過(guò)程是在內(nèi)存中進(jìn)行的,性能高。不用每次獲取 ID 都去訪問(wèn)一次數(shù)據(jù)庫(kù)。
問(wèn)題一
這個(gè)時(shí)候又有人說(shuō)了,如果并發(fā)量很大的話(huà),1000 的號(hào)段長(zhǎng)度一下就被用完了啊,此時(shí)就得去申請(qǐng)下一個(gè)范圍,這期間進(jìn)來(lái)的請(qǐng)求也會(huì)因?yàn)?DB 號(hào)段沒(méi)有取回來(lái),導(dǎo)致線程阻塞。
放心,Leaf 中已經(jīng)對(duì)這種情況做了優(yōu)化,不會(huì)等到 ID 消耗完了才去重新申請(qǐng),會(huì)在還沒(méi)用完之前就去申請(qǐng)下一個(gè)范圍段。并發(fā)量大的問(wèn)題你可以直接將 step 調(diào)大即可。
問(wèn)題二
這個(gè)時(shí)候又有人說(shuō)了,如果 Leaf 服務(wù)掛掉某個(gè)節(jié)點(diǎn)會(huì)不會(huì)有影響呢?
首先 Leaf 服務(wù)是集群部署,一般都會(huì)注冊(cè)到注冊(cè)中心讓其他服務(wù)發(fā)現(xiàn)。掛掉一個(gè)沒(méi)關(guān)系,還有其他的 N 個(gè)服務(wù)。問(wèn)題是對(duì) ID 的獲取有問(wèn)題嗎? 會(huì)不會(huì)出現(xiàn)重復(fù)的 ID 呢?
答案是沒(méi)問(wèn)題的,如果 Leaf1 掛了的話(huà),它的范圍是 1~1000,假如它當(dāng)前正獲取到了 100 這個(gè)階段,然后服務(wù)掛了。服務(wù)重啟后,就會(huì)去申請(qǐng)下一個(gè)范圍段了,不會(huì)再使用 1~1000。所以不會(huì)有重復(fù) ID 出現(xiàn)。
Leaf 改造支持 RPC
如果你們的調(diào)用量很大,為了追求更高的性能,可以自己擴(kuò)展一下,將 Leaf 改造成 Rpc 協(xié)議暴露出去。
首先將 Leaf 的 Spring 版本升級(jí)到 5.1.8.RELEASE,修改父 pom.xml 即可。
<spring.version>5.1.8.RELEASE</spring.version>`
然后將 Spring Boot 的版本升級(jí)到 2.1.6.RELEASE,修改 leaf-server 的 pom.xml。
<spring-boot-dependencies.version>2.1.6.RELEASE</spring-boot-dependencies.version>
還需要在 leaf-server 的 pom 中增加 nacos 相關(guān)的依賴(lài),因?yàn)槲覀?kitty-cloud 是用的 nacos。同時(shí)還需要依賴(lài) dubbo,才可以暴露 rpc 服務(wù)。
`<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 下創(chuàng)建 bootstrap.properties 文件,增加 nacos 相關(guān)的配置信息。
`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 默認(rèn)暴露的 Rest 服務(wù)是 LeafController 中,現(xiàn)在的需求是既要暴露 Rest 又要暴露 RPC 服務(wù),所以我們抽出兩個(gè)接口。一個(gè)是 Segment 模式,一個(gè)是 Snowflake 模式。
Segment 模式調(diào)用客戶(hù)端
`/*** 分布式ID服務(wù)客戶(hù)端-Segment模式** @作者* @個(gè)人微信 * @博客* @GitHub * @作者介紹* @時(shí)間 */ @FeignClient("${kitty.id.segment.name:LeafSegment}") public interface DistributedIdLeafSegmentRemoteService {@RequestMapping(value = "/api/segment/get/{key}")String getSegmentId(@PathVariable("key") String key); }`Snowflake 模式調(diào)用客戶(hù)端
/*** 分布式ID服務(wù)客戶(hù)端-Snowflake模式** @作者 * @個(gè)人微信* @微信公眾號(hào)* @GitHub* @作者介紹* @時(shí)間 */ @FeignClient("${kitty.id.snowflake.name:LeafSnowflake}") public interface DistributedIdLeafSnowflakeRemoteService {@RequestMapping(value = "/api/snowflake/get/{key}")String getSnowflakeId(@PathVariable("key") String key); }使用方可以根據(jù)使用場(chǎng)景來(lái)決定用 RPC 還是 Http 進(jìn)行調(diào)用,如果用 RPC 就@Reference 注入 Client,如果要用 Http 就用@Autowired 注入 Client。
最后改造 LeafController 同時(shí)暴露兩種協(xié)議即可。
@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());} } 與50位技術(shù)專(zhuān)家面對(duì)面20年技術(shù)見(jiàn)證,附贈(zèng)技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的easyui treegrid获取父节点的id_超简单的分布式ID生成方案!美团开源框架介绍的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: vb红绿灯自动切换_什么是自动驻车
- 下一篇: 语音通话框架_教资公告还没出,普通话测试