目录html
疯狂创客圈 Java 分布式聊天室【 亿级流量】实战系列之 -25【 博客园 总入口 】java
你们好,我是做者尼恩。目前和几个小伙伴一块儿,组织了一个高并发的实战社群【疯狂创客圈】。正在开始高并发、亿级流程的 IM 聊天程序 学习和实战node
前面,已经完成一个高性能的 Java 聊天程序的四件大事:面试
接下来,须要进入到分布式开发的环节了。 分布式的中间件,疯狂创客圈的小伙伴们,一致的选择了zookeeper,不只仅是因为其在大数据领域,太有名了。更重要的是,不少的著名框架,都使用了zk。算法
本篇介绍 ZK 的分布式命名服务 中的 分布式ID生成器。sql
zookeeper的命名服务,主要是利用zookeepeer节点的树型分层结构和子节点的次序维护能力,为分布式系统中的资源命名与标识能力。数据库
zookeeper的分布式命名服务,典型的应用场景有:缓存
(1)提供分布式JNDI的API目录服务功能。服务器
能够把系统中各类API接口服务的名称、连接地址放在zookeeper的树形分层结果中,提供分布式的API调用能力。著名的分布式框架,就是应用了zookeeper的分布式的JNDI能力。网络
开源的分布式服务框架Dubbo中使用ZooKeeper来做为其命名服务,维护全局的服务接口API地址列表。在Dubbo实现中,provider服务提供者在启动的时候,向ZK上的指定节点/dubbo/${serviceName}/providers文件夹下写入本身的API地址,这个操做就至关于服务的公开。
consumer服务消费者启动的时候,订阅节点/dubbo/{serviceName}/providers文件夹下的provider服务提供者URL地址,得到全部的访问提供者的API。
(2)制做分布式的ID生成器,为分布式系统中的每个数据资源,提供的惟一的标识能力。
在单体服务环境下,咱们惟一标识一个数据资源,一般利用数据库的主键自增功能。可是在大量服务器集群的场景下,依赖单体服务的数据库主键自增生成惟一ID,没有办法知足高并发和高负载的需求。
(3)分布式节点的命名服务
一个分布式系统会有不少的节点组成,并且,节点的数量是不断动态变化的。根据业务的膨胀须要和迎接流量洪峰,可能会加入大量的动态不少节点。流量洪峰过去,就须要下线大量的节点。或者说,因为机器或者网络的缘由,一些节点主动的离开的集群。
如何为大量的动态节点命名呢?一种简单的办法是,能够经过配置文件,手动的进行每个节点的命名。可是若是节点数据量太大,或者说变更频繁,手动命名是不现实的,这就须要用到分布式节点的命名服务。
疯狂创客圈的分布式IM实战项目,也会使用分布式命名服务,为每个IM节点动态命名。
在分布式系统中,ID生成器的使用场景,很是很是多:
(1)大量的数据记录,须要分布式ID
(2)大量的系统消息,须要分布式ID
(3)大量的请求日志,如http请求记录,须要惟一标识,以便进行后续的用户行为分析和调用链路分析,等等等等。
传统的数据库自增主键,或者单体的自增主键,已经不能知足需求。在分布式系统环境中,迫切须要一个全新的惟一ID的系统,这个系统须要知足如下需求:
(1)全局惟一:不能出现重复ID
(2)高可用:ID生成系统是基础系统,被许多关键系统调用,一旦宕机,会形成严重影响。
分布式惟一ID生成分案有不少种:
(1) java的UUID
(2) 利用分布式缓存Redis生成ID
利用Redis的原子操做INCR和INCRBY,生成全局惟一的ID。
(3) Twitter的snowflake算法
(4) ZooKeeper生成ID
利用ZooKeeper 的顺序节点,生成全局惟一的ID。
(5) MongoDb的ObjectId
利用分布式Nosql MongDB,生成全局惟一的ID。
首先分析一下java语言中的 UUID方案。
UUID是Universally Unique Identifier的缩写,它是在必定的范围内(从特定的名字空间到全球)惟一的机器生成的标识符。UUID在其余语言中也叫GUID,在java中,生成UUID的代码很简单:
String uuid = UUID.randomUUID().toString()
一个UUID是16字节长的数字,一共128位。一般以36字节的字符串表示,好比:3F2504E0-4F89-11D3-9A0C-0305E82C3301。 使用的时候,能够把中间的4个中划线去掉,剩下32位字符串。
UUID经由必定的算法机器生成,为了保证UUID的惟一性,规范定义了包括网卡MAC地址、时间戳、名字空间(Namespace)、随机或伪随机数、时序等元素,以及从这些元素生成UUID的算法。UUID的只能由计算机生成。
UUID的优势:本地生成ID,不须要进行远程调用,时延低,性能高。
UUID的缺点:UUID过长,16字节128位,一般以36长度的字符串表示,不少场景不适用,好比,因为UUID没有排序,没法保证趋势递增,用作数据库索引字段的效率就很低,新增记录存储入库时性能差
从高并发,高可用的角度出发,经过ZooKeeper实现分布式系统惟一ID的方案,是最为合适的解决方案之一。
经过建立ZK的顺序模式的节点,能够生成全局惟一的ID。
代码以下:
private String createSeqNode(String pathPefix) { try { // 建立一个 ZNode 顺序节点 String destPath = client.create() .creatingParentsIfNeeded() .withMode(CreateMode.*EPHEMERAL_SEQUENTIAL*) //避免zookeeper的顺序节点暴增,能够删除建立的顺序节点 .forPath(pathPefix); return destPath; } catch (Exception e) { e.printStackTrace(); } return null; }
节点建立完成后,会返回节点的完整的层次路径,生成的序号,放置在路径的末尾。通常为10位数字字符。
经过截取路径末尾的数字,做为新生成的ID。截取数字的代码以下:
public String makeId(String nodeName) { String str = createSeqNode(nodeName); if (null == str) { return null; } int index = str.lastIndexOf(nodeName); if (index >= 0) { index += nodeName.length(); return index <= str.length() ? str.substring(index) : ""; } return str; }
调用的代码以下:
*/\*** ** create by 尼恩 @ 疯狂创客圈* **\*/*@Slf4j public class IDMakerTester { @Test public void testMakeId() { IDMaker idMaker = new IDMaker(); idMaker.init(); String nodeName = "/test/IDMaker/ID-"; for (int i = 0; i < 10; i++) { String id = idMaker.makeId(nodeName); log.info("第"+ i + "个建立的id为:" + id); } idMaker.destroy(); } }
下面是部分的运行输出:
第0个建立的id为:0000000010 第1个建立的id为:0000000011
下一篇:基于 zookeeper 实现snowflake 算法 。
Java (Netty) 聊天程序【 亿级流量】实战 开源项目实战
疯狂创客圈 【 博客园 总入口 】