命名服务,顾名思义,就是帮助咱们对资源进行命名的服务,命名的目的固然是为了更好的定位了。这里所提到的资源在不一样场景中包括但不限于计算机(主机)名和地址、应用提供的服务的地址或者远程对象等。java
本文主要介绍Java中的命名服务、简单的命名服务的实现策略以及在分布式场景中如何实现命名服务。mysql
JNDI程序员
要介绍命名服务,不得不提 Java 命名和目录接口(Java Naming and Directory Interface,JNDI),他是J2EE中重要的规范之一,标准的J2EE容器都提供了对JNDI规范的实现。web
在没有JNDI的场景中,咱们要配置一个JDBC驱动连接数据库时咱们须要作如下操做:sql
Class.forName("com.mysql.jdbc.Driver"); Connection conn=DriverManager.getConnection("jdbc:mysql://DBName?user=hollis&password=hollischuang");
上面的代码中,把数据库连接相关的字符串直接写到了代码中,这不是一个好的作法。有过web开发经验的人都知道,在真正的web开发中咱们并不须要这样定义JDBC的链接,咱们通常都是把哪些固定的字符串配置到配置文件中,而后在代码中直接从配置中读取。甚至有不少数据库处理的框架(Hibernate\mybatis)会帮咱们把建立数据库连接等操做所有都封装好。数据库
使用 JNDI 获得数据源:mybatis
Context ctx=new InitialContext(); Object datasourceRef=ctx.lookup("java:comp/env/jdbc/mydatasource"); DataSource ds=(Datasource)datasourceRef; Connection c=ds.getConnection();
为了让 JNDI 解析 java:comp/env/jdbc/mydatasource 引用,部署人员必须把 标签插入 web.xml 文件(Web 应用程序的部署描述符)。 标签的意思就是“这个组件依赖于外部资源”。框架
<resource-ref> <description>Dollys DataSource</description> <res-ref-name>jdbc/mydatasource</res-ref-name> <res-ref-type>javax.sql.DataSource</res-ref-type> <res-auth>Container</res-auth> </resource-ref>
上面介绍的JNDI是一种Java的命名服务。他充分的反映出命名服务的特色——对某一资源进行命名,而后经过名称来定位惟一的资源。dom
到这里,咱们能够肯定的是:命名服务的目的是定义一个惟一的名字。这个名字的做用是能够用来定义惟一的资源。那么,咱们想想,在平常开发中咱们如何给一组资源中的每个某一个进行一个惟一的命名呢?在数据库开发中,一般有两种方案:自增的ID和UUID。分布式
数据库自增ID
在数据库中,为了标识惟一记录,可使用自增ID,只要指定某个字段是自增的,那么数据库就会帮咱们维护这个字段的自增。不一样数据库的实现原理不同,即便是MySql数据库,不一样的引擎的实现方式也不尽相同。InnoDB 中AUTO_INCREMENT的实现原理能够参考:innodb-auto-increment-handling
可是,不管如何,自增ID的实现都是基于单库单表的。也就是说一旦涉及到分库分表及分布式环境,就不能依赖数据库的自增字段来惟一标识一条记录了。也就是说,他生成的ID也就再也不能保证是惟一的了。
UUID
UUID(Universally Unique Identifier)全局惟一标识符,是指在一台机器上生成的数字,它保证对在同一时空中的全部机器都是惟一的。按照开放软件基金会(OSF)制定的标准计算,用到了以太网卡地址、纳秒级时间、芯片ID码和许多可能的数字。由如下几部分的组合:当前日期和时间(UUID的第一个部分与时间有关,若是你在生成一个UUID以后,过几秒又生成一个UUID,则第一个部分不一样,其他相同),时钟序列,全局惟一的IEEE机器识别号(若是有网卡,从网卡得到,没有网卡以其余方式得到),UUID的惟一缺陷在于生成的结果串会比较长。
UUID是由一组32位数的16进制数字所构成,也就是说若每纳秒产生1兆个UUID,要花100亿年才会将全部UUID用完。
在Java中,能够经过java.util.UUID的UUID.randomUUID();来生成一个UUID。
UUID是能够保证惟一性的,由于在这个长度为32位的ID中包含了时间、时钟序列、全局惟一IEEE机器识别号等。可是,他有两个比较明显的缺点,那就是长度过长和没有任何含义。长度天然没必要说,他有32位16进制数字。对于『550e8400-e29b-41d4-a716-446655440000』这个字符串来讲,我想任何一个程序员都看不出其表达的含义。一旦使用它做为全局惟一标识,就意味着在往后的问题排查和开发调试过程当中会遇到很大的困难。
上面介绍了两种传统的数据库中生成惟一标识的方法:自增ID和UUID。他们的优缺点正好相反:
Zookeeper的命名服务
Zookeeper是一个开放源码的分布式服务协调组件,是Google Chubby的开源实现。是一个高性能的分布式数据一致性解决方案。他将那些复杂的、容易出错的分布式一致性服务封装起来,构成一个高效可靠的原语集,并提供一系列简单易用的接口给用户使用。(http://www.hollischuang.com/archives/tag/zookeeper)
Zookeeper 的命名服务与 JNDI 可以完成的功能是差很少的,它们都是将有层次的目录结构关联到必定资源上,可是 Zookeeper 的命名服务更加是普遍意义上的关联,也许你并不须要将名称关联到特定资源上,你可能只须要一个不会重复名称,就像数据库中产生一个惟一的数字主键同样。
Zookeeper能够实现命名服务有两个重要的前提
1、节点相似于文件系统中的目录结构
2、能够建立顺序节点
上面说过,咱们想在分布式环境生成一组自增的、惟一的ID,那么看看zookeeper如何保证这两点。
惟一性
自增性
ZkClient client = new ZkClient(server, 5000, 5000, new BytesPushThroughSerializer()); final String fullNodePath = root.concat("/home/admin").concat("hollis"); final String ourPath = client.createPersistentSequential(fullNodePath, null); client.delete(ourPath); sout(ourPath);
以上代码就能够在/home/admin节点下建立出顺序的hollis节点,节点名称hollis-0000000001 hollis-0000000002hollis-0000000003那么,咱们就能够经过/home/admin/hollis-0000000001来惟必定位到一个节点了,那么咱们直接用这个名称给其余的资源命名了。
总结
一些比较常见的分布式框架(RPC、RMI)等都须要用到命名服务,如何解决分布式场景中的统一命名是一个相当重要的话题。
经过本文的介绍,能够知道Zookeeper能够解决分布式场景中的统一命名问题。经过本文,读者没必要马上很深刻的理解其中的原理,只须要知道zookeeper是能够作分布式的命名服务的就能够了,在之后的工做中遇到相似的场景能够想到zookeeper就够了。