服务注册与服务发现的原理

 

 

服务注册:

    当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();
	}
	
	
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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