既然选择,就注定风雨兼程!

开始吧!

准备:Idea201902/JDK11/ZK3.5.5/Gradle5.4.1/RabbitMQ3.7.13/Mysql8.0.11/Lombok0.26/Erlang21.2/postman7.5.0

难度:新手--战士--老兵--大师

目标:1,使用“雪花算法”生成订单ID  2,使用集中式Redis生成订单明细ID,3.Logback+slf4j打印日志

步骤:1.项目架构及代码基础设施,见往期文章。2.整体思路:其实ID的生成有很多种方案,如UUID,DB自增id,那分布式环境下有何方案呢?UUID也可以,但无规律;DB生成法,间隔初始值加步长,水平扩展差;雪花算法,服务器间时间同步是个问题,Redis集中式生成,容易单点瓶颈。总结而言,各有千秋,一是独立式,每个使用者自己生成,二是中心式,ID集中产生再分发。今天我们来看看典型代表: 雪花算法和Redis集中式。3.先说"雪花算法",理解也简单,“世界上没有一片雪花是相同的”顾名思义,雪花算法即是多维度组合,生成一个ID值,且这个值“趋势递增”,其核心构成如下图:

64bit,第一位0固定,二进制符号位,41bit时间戳,注意是当前与初始值相减的值,10bit工作机器id,12bit序列号,毫秒内的计数,其中工作机器id可预先人工指定,最后64位刚好转成Long类型即可。

4.算法生成类,放公共模块,com.biao.mall.common.util.SnowFlake

先来个整体的代码,包含了几个内部方法:

再来分析一下,

重点看下最大值,使用对-1左移位算法,(二进制右边补0),再按位取反,举例:最多2位,

~(-1L<<2)-->11111111-->11111100-->00000011-->3,实际上这里,就是(2的n次方-1),因移位算法是效率最高的,因此采用。

private final static long MAX_DATAC_ENTER_ID = ~(-1L << DATACENTER_BIT);
private final static long MAX_WORK_ID= ~(-1L << MACHINE_BIT);
private final static long MAX_SEQUENCE = ~(-1L << SEQUENCE_BIT);

构造函数中判断下数据中心id和主机id是否合法,

5.算法核心部分,nextId()方法,注意这是个同步方法,线程安全的:

A.先取得当前主机的毫秒,判断下是否小于上个时间戳,这个很重要!因为如果时间回拨,会导致可能的ID重复,因此当遇到时间回拨的情况,可以考虑使用备份的主机ID,这里是直接抛异常了。

B.如果取ID并发非常高,导致时间间隔很小,在同一毫秒内,则序列号自增并和最大序列号"按位与",这样的目的就是如果序列号达到最大,+1再按位与,就会等于0,(假设2位最大值加1:011+001=100,再100 & 011=000,然后只有2位就成0了)

C. 如果同一毫秒内序列号达到最大归为0,就使用getNextMill(),无限循环直到获得下个毫秒数

D.最后生成id就是时间戳差值,移位再按位或其他部分的数值,

6.再看Redis方案,这个主要是利用Redis速度快的特点,并可以使用Redis集群,就可确保速度和可用性,集中式的好处即统一生产,易控制,但分发受网络波动影响。具体位置在:com.biao.mall.common.util.RedisUitl7.分析重点:类里写了两个生成方式,第一个doGetId()是个简单生成法,直接使用对一个key做自增操作,每次递增一个,但坏处明显,容易暴露一天的订单量!数鸭子的游戏。

这里使用的是redisAPI的redisTemplate,其有操作如下,即对redis的五种数据类型都有对应的封装方法。

还有一个思路是先缓存,再取值,过程是:先生成一定量的顺序数,打乱顺序,再分成若干段,循环存入redis,取值时,根据全局计数器进行检索取值,

以下为分段存储,每个段都有个keyId。这里为啥,做分段?主要是存取效率问题,显然从一小段中检索数据要快于直接从一个大的List中检索快。

取乱序值方法getShuffleId:因为这个是个开箱即用的静态方法,必须保证首次使用时,初始化计数器,并执行分段存储操作,而应用重启,不重复这个操作,故可以先判断计数器是否已存在。取值是先找到分段的list的key,再找到偏移量index:

7.测试一下,我写了个controller:com.biao.mall.business.controller.MsgProducerController

启动Redis-->zk-->business:

可以看到,前面两图就是顺序的,而后面的两图则是乱序的。

8.项目地址:day10

https://github.com/xiexiaobiao/dubbo-project.git

后记:

1.关于服务间通信:内部服务间通信宜使用私有通信,如RPC,Netty等,效率高,对外使用Restful,兼容性好,因此,项目代码编号day09成为了一个临时版,其实现了MQ独立部署,Restful对内通信的模式,但技术上不可取。

2.redis生产中不会使用单点,常用的是集群式,如三主三从集群,但从使用者角度,有JedisCluster,如操作redisTemplate类似,逻辑上是操作一个,不必考虑后台实现与具体的物理分库存储。

3.看代码,多思考,才有收获,此文中有关于二进制的操作,试问(1L<<-1)可否?为啥使用二进制操作呢?

4.分布式Id方案,有很多,各有特点,生搬硬套行不通,必须根据业务特点取舍和改造。

5.之前在做MQ解析时遇到,一定小心!注意jsonString传输:以下String将发生JSON.parse()错误。"{\"orderId\":\"362951082266333184\",\"URL\":\"http://localhost:8085/delivery/one\"}",去掉json中的转义字符,可使用StringEscapeUtils.unescapeJava(jsonString)

往期文章导航:

Dubbo学习系列之六(微服务架构实战)

Dubbo学习系列之五(MQ实现微服务间通信)

Dubbo学习系列之四(MybaitsPlus+Druid使用)

Dubbo学习系列之三(Gradle打包可运行jar)

Dubbo学习系列之二(Nacos做配置中心)

Dubbo学习系列之一(消费者生产者模式搭建)

我的微信公众号,只写原创文章!

Dubbo学习系列之七(分布式订单ID方案)的更多相关文章

  1. Dubbo学习系列之八(分布式事务之MQ方案)

    自从小王玩起了微服务,发现微服务果然很强大,好处真是太多,心中暗喜,然而,却也遇到了分布式中最棘手的问题:分布式事务.小王遍访各路神仙,也无个完美开源解决方案,当然,也有些实际可行的手法,虽不算完美, ...

  2. Dubbo学习系列之九(Shiro+JWT权限管理)

    村长让小王给村里各系统来一套SSO方案做整合,隔壁的陈家村流行使用Session+认证中心方法,但小王想尝试点新鲜的,于是想到了JWT方案,那JWT是啥呢?JavaWebToken简称JWT,就是一个 ...

  3. Dubbo学习系列之十(Sentinel之限流与降级)

    各位看官,先提个问题,如果让你设计一套秒杀系统,核心要点是啥???我认为有三点:缓存.限流和分离.想当年12306大面积崩溃,还有如今的微博整体宕机情况,感觉就是限流降级没做好,"用有限的资 ...

  4. Dubbo学习系列之十五(Seata分布式事务方案TCC模式)

    上篇的续集. 工具: Idea201902/JDK11/Gradle5.6.2/Mysql8.0.11/Lombok0.27/Postman7.5.0/SpringBoot2.1.9/Nacos1.1 ...

  5. Dubbo学习系列之十六(ELK海量日志分析框架)

    外卖公司如何匹配骑手和订单?淘宝如何进行商品推荐?或者读者兴趣匹配?还有海量数据存储搜索.实时日志分析.应用程序监控等场景,Elasticsearch或许可以提供一些思路,作为业界最具影响力的海量搜索 ...

  6. Dubbo学习系列之十一(Dashboard+Nacos规则推送)

    中国武术,门派林立,都是号称多少代的XXX传人,结果在面对现代武术时,经常被KO秒杀,为啥,光靠宣传和口号撑门面,终究是靠不住,必须得有真货 ,得经得住考验,所以不能只说Sentinel有多好,也得给 ...

  7. Dubbo学习系列之十三(Mycat数据库代理)

    软件界有只猫,不用我说,各位看官肯定知道是哪只,那就是大名鼎鼎的Tomcat,现在又来了一只猫,据说是位东方萌妹子,暂且认作Tom猫的表妹,本来叫OpencloudDB,后又改名为Mycat,或许Ca ...

  8. Dubbo学习系列之十二(Quartz任务调度)

    Quartz词义为"石英"水晶,然后聪明的人类利用它发明了石英手表,因石英晶体在受到电流影响时,它会产生规律的振动,于是,这种时间上的规律,也被应用到了软件界,来命名了一款任务调度 ...

  9. 大数据学习系列之七 ----- Hadoop+Spark+Zookeeper+HBase+Hive集群搭建 图文详解

    引言 在之前的大数据学习系列中,搭建了Hadoop+Spark+HBase+Hive 环境以及一些测试.其实要说的话,我开始学习大数据的时候,搭建的就是集群,并不是单机模式和伪分布式.至于为什么先写单 ...

随机推荐

  1. js在字符串中加入一段字符串

    在这个功能的实现主要是slice()方法的掌握 arrayObject.slice(start,end) start 必需.规定从何处开始选取.如果是负数,那么它规定从数组尾部开始算起的位置.也就是说 ...

  2. 万恶之源-python加深

    1.列表 1.1列表的含义: ​ 它是以[]括起来,每个元素用""引起来,用逗号隔开而且可以存放各种类型的数据. li=["樊大爷",王立军",&qu ...

  3. 什么是PHP Socket?

    什么是 Socket? Socket 的中文翻译过来就是“套接字”.套接字是什么,我们先来看看它的英文含义:插座. Socket 就像一个电话插座,负责连通两端的电话,进行点对点通信,让电话可以进行通 ...

  4. node.js安装express框架(1)

    一.全局安装express 使用express首先确保你的node.js已经安装好了环境变量配置成功,安装了npm或者cnpm 你可以在终端上面输入node -v查看你的node版本号 打开cmd终端 ...

  5. C#连接SAP【生产系统与ERP对接】

    企业如果上了ERP系统,比如SAP.用友.金蝶或者E10等等,只需要ERP里面提供相应的接口,则可以直接将PMC创建的工单信息抛转至 MTS 系统,当该工单生产完成之后,MTS 将完成数据回传至 ER ...

  6. linux bash编程之函数和循环控制

    函数:实现独立功能的代码段 函数只有在调用时才会执行 语法一: function F_NAME{ 函数体 } 语法二: F_NAME() { 函数体 } 函数的返回值: 默认函数返回值:函数执行状态返 ...

  7. Rocket框架多文件上传,介绍rocket_upload 使用

    不知道你的体会是什么,我从C切换到Rust以来,最大的感受并不是语法方面的---那些方面已经有足够多人抱怨而又享受着了.我最大的感受是终于把Web编程工具,同系统编程工具统一了起来. C/C++其实也 ...

  8. SpringBoot项目集成Hystrix

    Hystrix Hystrix是由Netflix开源的一个服务隔离组件,通过服务隔离来避免由于依赖延迟.异常,引起资源耗尽导致系统不可用的解决方案. 1.什么是服务熔断   服务熔断就是对该服务的调用 ...

  9. 简单地认识一下 HTML

    简单复盘一下 HTML. 1.HTML 什么是 HTML?HTML 是 Hyper Text Markup Language 的简写,译成中文是「超文本标记语言」. 顾名思义,超文本,就是不止于文本, ...

  10. CCNA 之 七 路由协议 三 OSPF

    OSPF协议 OSPF开放式最短路径优先 全称:Open Shortest Path First 是目前使用最为广泛的路由协议,主要因为OSPF是开放式协议,和IGRP.EIGRP思科的私有协议不同. ...