服務註冊與服務發現的原理

 

 

服務註冊:

    當provider啓動的時候,連接zk集羣,便會在zk集羣中創建瞬時節點。將自己的url保存到瞬時節點中。

    當provider出現宕機,某個服務器的url就會減少,瞬時節點就會自動減少。該服務器重啓回覆正常,便會重新生成瞬時節點,將該服務器存儲的url又重新添加到zk集羣中。

 

服務發現:

  當consumer啓動的時候,連接zk集羣,會獲得所有瞬時節點的集合。將其混存到本地的集合中去。

 通過負載均衡算法將產生一個可以訪問的服務進行訪問。當provider發生宕機的情況,consumer會重新進行讀取zk集羣的列表,重新加載url,將其保存在本地的集合中。


代碼實現:

  情景: 3個服務器發佈三個url地址到zookeeper集羣中,當一臺服務器關閉(項目),zookeeper便會刪除該項目的url(瞬時節點),並重新進行讀取。

5.1.1創建zk-rmi-cluseter-provider(jar)項目

5.1.2添加依賴

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.bjsxt.zk.rmi</groupId>
  <artifactId>zk-rmi-cluster-provider</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <dependencies>
  	<!-- 添加公共資源依賴 -->
  	<dependency>
  		<groupId>com.bjsxt.resources</groupId>
  		<artifactId>rmi-resources</artifactId>
  		<version>0.0.1-SNAPSHOT</version>
  	</dependency>
  	<!-- zk的api依賴 -->
  	<dependency>
  		<groupId>org.apache.zookeeper</groupId>
	    <artifactId>zookeeper</artifactId>
	    <version>3.4.8</version>
  	</dependency>
  </dependencies>
</project>

5.1.3拷貝OrdersServiceImpl實現類

5.1.4創建ProviderApp實現服務發佈和註冊


package com.bjsxt.app;

import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;
import java.util.concurrent.CountDownLatch;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs.Ids;

import com.bjsxt.service.OrdersService;
import com.bjsxt.service.impl.OrdersServiceImpl;

import org.apache.zookeeper.ZooKeeper;

public class ProviderApp {
	
	//實現線程同步
	private CountDownLatch latch=new CountDownLatch(1);
	/***
	 * 建立和zk集羣的建立
	 */
	public ZooKeeper getConnection() {
		ZooKeeper zk=null;
		
		try {
			zk=new ZooKeeper(Constants.ZK_HOST, Constants.TIME_OUT, new Watcher() {

				@Override
				public void process(WatchedEvent event) {
					// 判斷是否和zk集羣建立鏈接
					if(event.getState()==Event.KeeperState.SyncConnected) {
						latch.countDown(); //喚醒阻塞的線程
					}
				}
				
				
			});
			//阻塞主線程
			latch.await();
		}catch(Exception ex) {
			ex.printStackTrace();
		}
		
		return zk;
		
	}
	
	/***
	 * 創建瞬時的,順序節點,同時將自己的url寫入到瞬時節點
	 */
	public void providerRegister(String url) {
		try {
			ZooKeeper zk=getConnection();
			if(zk!=null) {
				//創建瞬時,順序節點
				zk.create(Constants.URL, //指定的節點路徑,注意自己手動創建/provider
						url.getBytes(),  //節點中保存的數據
						Ids.OPEN_ACL_UNSAFE, //權限
						CreateMode.EPHEMERAL_SEQUENTIAL);//節點類型
			}
		}catch(Exception ex) {
			ex.printStackTrace();
		}
	}
	
	/***
	 * 實現服務的發佈
	 */
	public void pulisher(Integer port) {
		try {
			//指定暴露的遠程服務的訪問端口
			LocateRegistry.createRegistry(port);
			//創建OrdersServiceImpl實現類
			OrdersService remote=new OrdersServiceImpl();
			//定義遠程訪問的url
			String name="rmi://localhost:"+port+"/sxt";
			//進行給remote對象綁定一個遠程訪問的地址
			Naming.bind(name, remote);
			//調用服務的註冊方法,完成服務註冊
			providerRegister(name);
			System.out.println("==========發佈遠程服務===========9999");
		}catch(Exception ex) {
			ex.printStackTrace();
		}
	}
	
	/***
	 * 發佈集羣服務
	 */
	public static void main(String[] args) {
		ProviderApp app=new ProviderApp();
		app.pulisher(9999);
	}
}

5.1.5查看發佈結果、

 

 

 

 

5.2.1創建zk-rmi-cluster-consumer(jar)項目

5.2.2添加依賴

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.bjsxt.zk.rmi.consumer</groupId>
  <artifactId>zk-rmi-cluster-consumer</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <dependencies>
  	<!-- 添加公共資源依賴 -->
  	<dependency>
  		<groupId>com.bjsxt.resources</groupId>
  		<artifactId>rmi-resources</artifactId>
  		<version>0.0.1-SNAPSHOT</version>
  	</dependency>
  	<!-- zk的api依賴 -->
  	<dependency>
  		<groupId>org.apache.zookeeper</groupId>
	    <artifactId>zookeeper</artifactId>
	    <version>3.4.8</version>
  	</dependency>
  </dependencies>
</project>

5.2.3拷貝Constants接口

5.2.4創建ConsumerApp啓動發現和消費服務

package com.bjsxt.app;

import java.rmi.ConnectException;
import java.rmi.Naming;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadLocalRandom;

import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;

import com.bjsxt.pojo.Orders;
import com.bjsxt.service.OrdersService;

/***
 * 實現服務的發現和消費
 * @author EDZ
 *
 */
public class ConsumerApp {
	//緩存可用的服務地址列表
	private volatile List<String> urls=new ArrayList<>();

	
	//實現線程同步
	private CountDownLatch latch=new CountDownLatch(1);
	/***
	 * 建立和zk集羣的建立
	 */
	public ZooKeeper getConnection() {
		ZooKeeper zk=null;
		
		try {
			zk=new ZooKeeper(Constants.ZK_HOST, Constants.TIME_OUT, new Watcher() {

				@Override
				public void process(WatchedEvent event) {
					// 判斷是否和zk集羣建立鏈接
					if(event.getState()==Event.KeeperState.SyncConnected) {
						latch.countDown(); //喚醒阻塞的線程
					}
				}
				
				
			});
			//阻塞主線程
			latch.await();
		}catch(Exception ex) {
			ex.printStackTrace();
		}
		
		return zk;
		
	}
	
	/***
	 * 讀取zk集羣中註冊的服務
	 */
	public void providerFetch() {
		try {
			ZooKeeper zk=getConnection();
			if(zk!=null) {
				//讀取/provider接下的所有子節點
				List<String> children = zk.getChildren(Constants.PROVIDER, new Watcher() {

					/***
					 * 觀察/provider節點的子節點是否有變化
					 */
					@Override
					public void process(WatchedEvent event) {
						// TODO Auto-generated method stub
						if(event.getType()==Event.EventType.NodeChildrenChanged) {
							System.out.println("========子節點發生變化========");
							providerFetch(); //如果有子節點變化,回調重新讀取
						}
						
					}
					
				});
				//保存讀取的服務地址
				List<String> path=new ArrayList<>();
				//遍歷子節點集合children
				for(String node:children) {
					//System.out.println(node);
					//獲得node節點中的數據
					byte[] data = zk.getData(Constants.PROVIDER+"/"+node, false, null);
					//byte[]轉化爲String
					String url=new String(data);
					
					path.add(url);
					
				}
				
				this.urls=path;
			}
		}catch(Exception ex) {
			ex.printStackTrace();
		}
	}
	
	/***
	 * 完成服務消費
	 */
	public void consumer() {
		while (true) {
			try {

				// 產生一個隨機數
				int index = ThreadLocalRandom.current().nextInt(this.urls.size());
				// 遠程服務的訪問地址
				String name = this.urls.get(index);
				// 獲得遠程服務的代理對象
				OrdersService remote = (OrdersService) Naming.lookup(name);

				// 獲得會員訂單的集合
				List<Orders> list = remote.loadOrdersList(111111);
				for (Orders orders : list) {
					System.out.println(orders.getId() + "\t" 
								+ orders.getVip() + "\t" + orders.getOdate() + "\t"
							+ orders.getRemark());
				}

				Thread.sleep(3000);

			} catch (Exception e) {
				 
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * 無參數構造方法
	 */
	public ConsumerApp() {
		providerFetch();
	}
	
	public static void main(String[] args) {
		ConsumerApp app=new ConsumerApp();
		app.consumer();
	}
	
	
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章