zoukankan      html  css  js  c++  java
  • 美团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/

    今天越懒,明天要做的事越多。
  • 相关阅读:
    Linux中怎么通过PID号找到对应的进程名及所在目录
    MYSQL 1093 之You can't specify target table for update in FROM clause解决办法
    Spring注解@Resource和@Autowired区别对比
    Java数据类型和MySql数据类型对应一览
    java高分局之jstat命令使用(转)
    为python安装matplotlib模块
    Python中的文件IO操作(读写文件、追加文件)
    Python 3语法小记(九) 异常 Exception
    SpringBoot下的Job定时任务
    linux的top命令参数详解
  • 原文地址:https://www.cnblogs.com/fanshuyao/p/14777644.html
Copyright © 2011-2022 走看看