Dubbo学习系列之七(分布式订单ID方案)
既然选择,就注定风雨兼程!
开始吧!
准备: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学习系列之四(MybaitsPlus+Druid使用)
我的微信公众号,只写原创文章!
Dubbo学习系列之七(分布式订单ID方案)的更多相关文章
- Dubbo学习系列之八(分布式事务之MQ方案)
自从小王玩起了微服务,发现微服务果然很强大,好处真是太多,心中暗喜,然而,却也遇到了分布式中最棘手的问题:分布式事务.小王遍访各路神仙,也无个完美开源解决方案,当然,也有些实际可行的手法,虽不算完美, ...
- Dubbo学习系列之九(Shiro+JWT权限管理)
村长让小王给村里各系统来一套SSO方案做整合,隔壁的陈家村流行使用Session+认证中心方法,但小王想尝试点新鲜的,于是想到了JWT方案,那JWT是啥呢?JavaWebToken简称JWT,就是一个 ...
- Dubbo学习系列之十(Sentinel之限流与降级)
各位看官,先提个问题,如果让你设计一套秒杀系统,核心要点是啥???我认为有三点:缓存.限流和分离.想当年12306大面积崩溃,还有如今的微博整体宕机情况,感觉就是限流降级没做好,"用有限的资 ...
- Dubbo学习系列之十五(Seata分布式事务方案TCC模式)
上篇的续集. 工具: Idea201902/JDK11/Gradle5.6.2/Mysql8.0.11/Lombok0.27/Postman7.5.0/SpringBoot2.1.9/Nacos1.1 ...
- Dubbo学习系列之十六(ELK海量日志分析框架)
外卖公司如何匹配骑手和订单?淘宝如何进行商品推荐?或者读者兴趣匹配?还有海量数据存储搜索.实时日志分析.应用程序监控等场景,Elasticsearch或许可以提供一些思路,作为业界最具影响力的海量搜索 ...
- Dubbo学习系列之十一(Dashboard+Nacos规则推送)
中国武术,门派林立,都是号称多少代的XXX传人,结果在面对现代武术时,经常被KO秒杀,为啥,光靠宣传和口号撑门面,终究是靠不住,必须得有真货 ,得经得住考验,所以不能只说Sentinel有多好,也得给 ...
- Dubbo学习系列之十三(Mycat数据库代理)
软件界有只猫,不用我说,各位看官肯定知道是哪只,那就是大名鼎鼎的Tomcat,现在又来了一只猫,据说是位东方萌妹子,暂且认作Tom猫的表妹,本来叫OpencloudDB,后又改名为Mycat,或许Ca ...
- Dubbo学习系列之十二(Quartz任务调度)
Quartz词义为"石英"水晶,然后聪明的人类利用它发明了石英手表,因石英晶体在受到电流影响时,它会产生规律的振动,于是,这种时间上的规律,也被应用到了软件界,来命名了一款任务调度 ...
- 大数据学习系列之七 ----- Hadoop+Spark+Zookeeper+HBase+Hive集群搭建 图文详解
引言 在之前的大数据学习系列中,搭建了Hadoop+Spark+HBase+Hive 环境以及一些测试.其实要说的话,我开始学习大数据的时候,搭建的就是集群,并不是单机模式和伪分布式.至于为什么先写单 ...
随机推荐
- Material for oauth 2
oauth 2 in 8 steps: https://knpuniversity.com/screencast/oauth Live demo of oauth 2 (with server im ...
- tomcat 部署springboot 项目
Springboot项目默认jar包,且内置Tomcat.现需要将项目打成war包,并部署到服务器tomcat中. 1.修改pom.xml文件.将jar修改为war. <packaging> ...
- 如何在 PHP 和 Laravel 中使用 Traits
事实上,PHP 作为一门编程语言存在的问题之一,就是你只能使用单继承.这意味着一个类只能从另一个类中继承.例如,可能希望从几个不同的类继承方法,以防止代码重复.在 PHP 5.4 中 一个新的语言特性 ...
- Test Complete 的自动化测试 --- 三角形
Test Complete 的自动化测试 --- 三角形 PS:工具:Test Complete , OS:win 10 这里做三角形的测试与上一篇博客做计算器的测试大致一样,都是对.exe的执行文件 ...
- Project Euler 63: Powerful digit counts
五位数\(16807=7^5\)也是一个五次幂,同样的,九位数\(134217728=8^9\)也是一个九次幂.求有多少个\(n\)位正整数同时也是\(n\)次幂? 分析:设题目要求的幂的底为\(n\ ...
- 分析facebook的AsyncDisplayKit框架中的Transaction的工作原理
在AsyncDisplayKit框架中有一个_ASAsyncTransaction模块,用于AsyncDiplayNode的异步事务,使用了dispatch_group实现. 主要目的是将operat ...
- Chocolatey初体验
新电脑安装Nodejs时发现安装包提示是否自动安装Chocolatey,之前没看到过这个名词,于是搜索了下,发现Chocolatey是Windows平台的包管理工具,类似于Linux的yum/apt- ...
- Java的Arrays类 基本用法
初识Java的Arrays类 Arrays类包括很多用于操作数组的静态方法(例如排序和搜索),且静态方法可以通过类名Arrays直接调用.用之前需要导入Arrays类: import java.uti ...
- ES6扩展运算符...
对象的扩展运算符理解对象的扩展运算符其实很简单,只要记住一句话就可以: 对象中的扩展运算符(...)用于取出参数对象中的所有可遍历属性,拷贝到当前对象之中 let bar = { a: 1, b: 2 ...
- [转载] Docker 实现原理
目录 Namespaces 进程 网络 libnetwork 挂载点 chroot CGroups UnionFS 存储驱动 AUFS 其他存储驱动 总结 原文链接:https://dravenes ...