美團Leaf分布式ID Leaf安裝和使用,美團Leaf snowflake雪花算法模式,美團Leaf segment號段模式


美團Leaf分布式ID Leaf安裝和使用,

美團Leaf snowflake雪花算法模式,

美團Leaf segment號段模式

 

================================

©Copyright 蕃薯耀 2021-05-17

https://www.cnblogs.com/fanshuyao/

 

一、美團Leaf分布式ID概述

命名規划:

There are no two identical leaves in the world.
世界上沒有兩片完全相同的樹葉。
​ — 萊布尼茨

Leaf 提供兩種生成的ID的方式(segment號段模式和snowflake雪花算法模式),你可以同時開啟兩種方式,也可以指定開啟某種方式(默認兩種方式為關閉狀態)。

 目前Leaf覆蓋了美團點評公司內部金融、餐飲、外賣、酒店旅游、貓眼電影等眾多業務線。在4C8G VM基礎上,通過公司RPC方式調用,QPS壓測結果近5w/s,TP999 1ms。

 

二、美團Leaf snowflake雪花算法模式


snowflake雪花算法模式要依賴於Zookeeper組件,必須要懂得安裝Zookeeper和使用。

snowflake雪花算法模式同一個服務器(同一個IP地址)不能部署多個項目,必須分別部署在不同IP的服務器

 

1、pom.xml 引入依賴

需要注意的是:leaf-boot-starter在Maven互聯網的倉庫是沒有的,需要自己從官網下載生成相應的Jar到自己本地的Maven倉庫

官網默認下載地址:

https://github.com/Meituan-Dianping/Leaf

 

官網spring-boot-starter下載地址:

https://github.com/Meituan-Dianping/Leaf/tree/feature/spring-boot-starter

 

在Eclipse 導入項目leaf-parent,使用Maven install到Maven本地倉庫,然后在自己新建立的項目中引入依賴

創建自己的SpringBoot項目:id-leaf,在Pom.xml引入依賴:

<dependency>
            <groupId>com.sankuai.inf.leaf</groupId>
            <artifactId>leaf-boot-starter</artifactId>
            <version>1.0.1-RELEASE</version>
        </dependency>
        
        <dependency>
    <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>2.6.0</version>
            <exclusions>
               <exclusion>
                   <artifactId>log4j</artifactId>
                   <groupId>log4j</groupId>
               </exclusion>
            </exclusions>
    </dependency>

 


2、創建leaf.properties文件

美團Leaf 相關屬性配置在文件leaf.properties,需要創建,或者從leaf-core項目復制一個(但segment號段模式相應的屬性不是最新的),建議自己創建

#屬性注入類文件是:com.sankuai.inf.leaf.plugin.LeafSpringBootProperties

#leaf.name:leaf 服務名,用於zookeeper的保存路徑
#com.sankuai.inf.leaf.common.PropertyFactory
leaf.name=leaf_id

#雪花算法ID
#當然,為了追求更高的性能,需要通過RPC Server來部署Leaf 服務,那僅需要引入leaf-core的包,把生成ID的API封裝到指定的RPC框架中即可。
#是否開啟snowflake模式,默認是false
leaf.snowflake.enable=true
#leaf.snowflake.address:snowflake模式下的zk地址,包括IP和端口
leaf.snowflake.address=192.168.170.14:2181
#leaf.snowflake.port:snowflake模式下的服務注冊端口(非Zk端口)
#leaf.snowflake.port就是leaf項目的訪問端口
#例如你在一台服務器上部署了多個leaf服務,每個leaf服務的HTTP服務訪問端口不一樣,
#那么在使用snowflake模式生成ID時,每個leaf服務都會有一個唯一的workId進行區分,來防止id重復,
#這個workId就是個根據服務所在的服務器Ip+服務端口leaf.snowflake.port來唯一確定的,
#主要通過將ip:port以寫入到zookeeper特定路徑下,寫入一個永久有序節點,會生成一個唯一的序列號,
#這個序列作為這個leaf服務的workId,以后再在這個ip的服務器上以這個端口啟動leaf服務,
#會根據ip和端口去Zookeeper找到以前生成的對應的workId,進行使用
#示例:
#[zk: localhost:2181(CONNECTED) 7] get /snowflake/null/forever/168.168.2.120:9600-0000000002 
#{"ip":"168.168.2.120","port":"9600","timestamp":1621225915118}
leaf.snowflake.port=9600

 

leaf.name要加上,不然在zookeeper設置的路徑就是null,看起來不直觀,建議設置成自己的,如:leaf_id

未設置前:

[zk: localhost:2181(CONNECTED) 16] ls  /snowflake
[null]

[zk: localhost:2181(CONNECTED) 7] get /snowflake/null/forever/168.168.2.120:9600-0000000002 
{"ip":"168.168.2.120","port":"9600","timestamp":1621225915118

 

設置leaf.name=leaf_id后:

[zk: localhost:2181(CONNECTED) 16] ls  /snowflake
[leafId, leaf_id, null]
[zk: localhost:
2181(CONNECTED) 15] get /snowflake/leaf_id/forever/168.168.2.120:9600-0000000000 {"ip":"168.168.2.120","port":"9600","timestamp":1621232446738}

 

 

3、啟動類加上@EnableLeafServer 注解:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;

import com.sankuai.inf.leaf.plugin.annotation.EnableLeafServer;

@EnableLeafServer
@SpringBootApplication(exclude=DataSourceAutoConfiguration.class)
public class IdLeafApplication {

    public static void main(String[] args) {
        SpringApplication.run(IdLeafApplication.class, args);
    }
}

 

 

4、Controller端生成snowflake模式的ID

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.lqy.Result;
import com.sankuai.inf.leaf.common.Status;
import com.sankuai.inf.leaf.service.SnowflakeService;

@RestController
@RequestMapping("/leaf")
public class IdController {
    //雪花算法ID
    @Autowired
    private SnowflakeService snowflakeService; 
    
    @RequestMapping("/snowflake")
    public Result getSnowflakeId() {
        
        //獲取snowflake分布式ID
        //id  這個參數是沒有意義的,只是為了和號段模式的接口統一,所以要傳一個參數,自己隨意定義一個
        com.sankuai.inf.leaf.common.Result r =  snowflakeService.getId("id");
        
        //判斷是否成功,成功返回具體的id,不成功返回錯誤提示
        if(r.getStatus() == Status.SUCCESS) {
            return Result.ok(r.getId());
        }
        
        return Result.failMsg("獲取snowflake分布式ID失敗");
    }
    
    
    
}

 

 

5、測試snowflake模式的ID

瀏覽器發送請求:
http://127.0.0.1:9899/id/leaf/snowflake

返回結果:

{
"result": true,
"timestamp": "2021-05-17 19:52:38",
"msg": "操作成功。",
"datas": 1394199163356315654
}

 

 

 

三、 美團Leaf segment號段模式


1、pom.xml 引入依賴
同:美團Leaf snowflake雪花算法模式。如果之前已經使用snowflake模式,此時不用重復引入。

此處需要注意的是:
由於leaf-boot-starter使用的是druid連接數據,在默認情況使用的是Mysql 8.x版本的mysql-connector-java,會導致報錯,
因為連接的驅動類不是:com.mysql.cj.jdbc.Driver,會導致項目啟動不了,而且沒有相關的日志輸出。
最簡單的方法,是讓項目直接使用5.x版本的連接驅動,如下:

    <properties>
        <!-- 5.1.49 -->
        <!-- 8.0.25 -->
        <mysql.version>5.1.49</mysql.version>
    </properties>

 

數據庫連接驅動出錯,相關的報錯如下:

Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
2021-05-17 15:25:50 [WARN][com.alibaba.druid.pool.vendor.MySqlValidConnectionChecker.<init>(MySqlValidConnectionChecker.java:55)]
Cannot resolve com.mysq.jdbc.Connection.ping method.  Will use 'SELECT 1' instead.

java.lang.NullPointerException
    at com.alibaba.druid.pool.vendor.MySqlValidConnectionChecker.<init>(MySqlValidConnectionChecker.java:50)
    at com.alibaba.druid.pool.DruidDataSource.initValidConnectionChecker(DruidDataSource.java:944)
    at com.alibaba.druid.pool.DruidDataSource.init(DruidDataSource.java:659)
    at com.sankuai.inf.leaf.service.SegmentService.<init>(SegmentService.java:38)
    at com.sankuai.inf.leaf.plugin.LeafSpringBootStarterAutoConfigure.initLeafSegmentStarter(LeafSpringBootStarterAutoConfigure.java:29)
    at com.sankuai.inf.leaf.plugin.LeafSpringBootStarterAutoConfigure$$EnhancerBySpringCGLIB$$d9fca9be.CGLIB$initLeafSegmentStarter$0(<generated>)
    at com.sankuai.inf.leaf.plugin.LeafSpringBootStarterAutoConfigure$$EnhancerBySpringCGLIB$$d9fca9be$$FastClassBySpringCGLIB$$3261d340.invoke(<generated>)


java.sql.SQLException: validateConnection false
    at com.alibaba.druid.pool.DruidAbstractDataSource.validateConnection(DruidAbstractDataSource.java:1249)
    at com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource.java:1474)
    at com.alibaba.druid.pool.DruidDataSource$CreateConnectionThread.run(DruidDataSource.java:1969)
2021-05-17 15:26:20 [ERROR][com.alibaba.druid.pool.DruidDataSource$CreateConnectionThread.run(DruidDataSource.java:1971)]

 

 

2、在項目引入 log4j.properties 配置文件

解決因數據庫驅動連接出錯,導致無日志輸出的問題。

具體如下(log4j.appender.file.File:路徑修改成自己的路徑):

log4j.rootLogger = INFO,stdout,file
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [%p][%l]%n%m%n%n
log4j.appender.stdout.DatePattern='.'dd

### direct messages to file land-landSupply.log ###
log4j.appender.file = org.apache.log4j.DailyRollingFileAppender
log4j.appender.file.DatePattern='.'yyyy-MM-dd'.log'
log4j.appender.file.File=E:/logs/id-leaf/leaf.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [%p][%l]%n%m%n%n


##打印sql部分
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Connection = DEBUG
log4j.logger.java.sql.Statement = DEBUG
log4j.logger.java.sql.PreparedStatement = DEBUG
log4j.logger.java.sql.ResultSet = DEBUG


#下面的兩條配置非常重要,設置為trace后,將可以看到打印出sql中 ? 占位符的實際內容  
#this is the most important config for showing parames like ?  
log4j.logger.org.hibernate.type=TRACE  
#above two configs   
log4j.logger.org.hibernate.tool.hbm2ddl=DEBUG   
log4j.logger.org.hibernate.hql=DEBUG   
log4j.logger.org.hibernate.cache=DEBUG
log4j.logger.org.hibernate.transaction=DEBUG   
log4j.logger.org.hibernate.jdbc=DEBUG   
log4j.logger.org.hibernate.connection.DriverManagerConnectionProvider=TRACE  
log4j.logger.org.hibernate.type.descriptor.sql.BasicBinder=TRACE  
### set log levels - for more verbose logging change 'info' to 'debug' ###

 

 

3、創建leaf.properties文件
同:美團Leaf snowflake雪花算法模式。

追加下面的配置(官網是沒有leaf.segment.driver-class-name,后面自己修改源碼加的,用於適配Mysql 8.x的連接)

#分段模式
#配置Mysql數據庫連接的Url、賬號、密碼(僅支持Mysql數據庫,要先執行腳本創建數據庫和表)
leaf.segment.enable=true
leaf.segment.url=jdbc:mysql://localhost:3307/leaf?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8&pinGlobalTxToPhysicalConnection=true&autoReconnect=true
#com.mysql.cj.jdbc.Driver
#com.mysql.jdbc.Driver
#官網是沒有leaf.segment.driver-class-name,后面自己修改源碼加的,用於適配Mysql 8.x的連接
#如果不懂修改,建議參考【1、pom.xml 引入依賴】,直接使用Mysql 5的數據庫驅動連接,就不需要配置修改leaf.segment.driver-class-name
leaf.segment.driver-class-name=com.mysql.cj.jdbc.Driver
leaf.segment.username=root
leaf.segment.password=root

 

美團Leaf分布式ID修改版依賴Jar包下載

鏈接: https://pan.baidu.com/s/103Ow87ZpTWltWFJj15aWJg
提取碼: 8juc

 

 

4、創建數據庫leaf和表leaf_alloc

標識符:biz_tag不能相同,所以可以直接使用表名作為biz_tag

 

#創建leaf數據庫
CREATE DATABASE leaf;

#創建表
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不能相同
INSERT INTO leaf_alloc(biz_tag, max_id, step, description) VALUES('leaf-segment-test', 1, 2000, 'Test leaf Segment Mode Get Id');

#插入分段測試數據,標識符:biz_tag不能相同
INSERT INTO leaf_alloc(biz_tag, max_id, step, description) VALUES('my-leaf-segment', 1, 20, 'Test leaf Segment Mode Get Id');

 

 

5、啟動類加上@EnableLeafServer 注解:
同美團Leaf snowflake雪花算法模式。


6、Controller端生成segment號段模式的ID

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.lqy.Result;
import com.sankuai.inf.leaf.common.Status;
import com.sankuai.inf.leaf.service.SegmentService;
import com.sankuai.inf.leaf.service.SnowflakeService;

@RestController
@RequestMapping("/leaf")
public class IdController {

    //號段ID
    @Autowired
    private SegmentService segmentService;
    
    @RequestMapping("/segment/{segmentTag}")
    public Result getSegmentId(@PathVariable("segmentTag") String segmentTag) {
        
        //獲取snowflake分布式ID
        com.sankuai.inf.leaf.common.Result r =  segmentService.getId(segmentTag);
        
        //判斷是否成功,成功返回具體的id,不成功返回錯誤提示
        if(r.getStatus() == Status.SUCCESS) {
            return Result.ok(r.getId());
        }
        
        return Result.failMsg("獲取snowflake分布式ID失敗");
    }
}

 

 

7、測試segment號段模式ID

瀏覽器發送請求:
http://127.0.0.1:9899/id/leaf/segment/my-leaf-segment

 

返回結果:
{
"result": true,
"timestamp": "2021-05-17 20:09:35",
"msg": "操作成功。",
"datas": 141
}

 

 

(時間寶貴,分享不易,捐贈回饋,^_^)

 

================================

©Copyright 蕃薯耀 2021-05-17

https://www.cnblogs.com/fanshuyao/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM