命名服務,顧名思義,就是幫助咱們對資源進行命名的服務,命名的目的固然是爲了更好的定位了。這裏所提到的資源在不一樣場景中包括但不限於計算機(主機)名和地址、應用提供的服務的地址或者遠程對象等。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就夠了。