Zookeeper集群实现服务发现

作者前言:这篇文章是zk集群服务发现的原始搭建,不介入dubbo技术实现,可以使我们通过底层更加了解服务发现的原理。

1.創建服務端項目
該項目完成集群服務的發佈,同時將自己的url,按照順序,瞬時節點形式保存到zk中,發佈集群服務,注冊到zookeeper中
1.1添加zookeeper依賴 服務端客戶端都需要

 <dependencies>

**<!-- 添加zk依赖 -->**

   <dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.4.6</version>
</dependency>
  </dependencies>

1.2創建接口,此接口繼承Remote遠程接口,定義方法,並抛出異常throws RemoteException;
1.3創建常量接口

public interface Constants {
 
//访问zk集群的地址,218*是每個服務器的端口號,是服務端訪問zookeeper的入口,服務端集群訪問zk,自己變成了客戶端。
String ZK_HOST="192.168.25.30:2181,192.168.25.30:2182,192.168.25.30:2183";
//zk集群的链接超时时间
Integer TIME_OUT=5000;
//定义远程服务的节点
String REGISTER="/red";
//定义保存远程服务url的节点
String PROVIDER=REGISTER+"/provider";
/****
 *
 *   /red
 *    |--- -e  -s  provider00000000001    rmi://localhost:7777/sxt
 *    |--- -e  -s  provider00000000002    rmi://localhost:8888/sxt
 *    |--- -e  -s  provider00000000003    rmi://localhost:9999/sxt
 */
}

1.4創建1.2接口實現類,並繼承extends UnicastRemoteObject,重寫方法。
1.5創建啓動類(1.服務端集群連接zk,寫入url 2.發佈遠程集群服務)
//实现线程通信,賦值1,儅成功喚醒阻塞綫程,值變爲0
private CountDownLatch latch=new CountDownLatch(1);
1.5.1定義連接zk集群方法,如果

  public ZooKeeper getConnection(){
     ZooKeeper zk=null;
     try{
     //创建zk对象,链接zk集群,參數為連接zk集群的地址,連接超時時間,監聽對象
     zk=new ZooKeeper(Constants.ZK_HOST, Constants.TIME_OUT, new Watcher(){
 
@Override 匿名内部類
public void process(WatchedEvent event) {
//判断是否和zk集群链接成功 
//Event.KeeperState.SyncConnected  監聽時間對象的同步連接方法和當前服務器集群連接zk的狀態相等,説明連接成功。兩者存放的供客戶端訪問的地址是一致的。
if(Event.KeeperState.SyncConnected==event.getState()){
//唤醒阻塞的主线程,目的讓主綫程main方法繼續執行,當前方法是主綫程以外的綫程,必須獲得連接才讓主綫程繼續執行,否則主綫程執行完畢,這裏還沒連接上。这个方法是说明已经连接上,让主线程继续执行。
latch.countDown();
}
}
     });
     //沒連接上,让主线程阻塞
     latch.await();
     }catch(Exception ex){
    	 ex.printStackTrace();
     }
    	 return zk;
    }

1.5.2連接成功,將服務器集群中的供客戶端訪問的url寫入zk中 注冊

 public void writeUrl(String url){
     try{
  	   //链接zk集群,獲得連接對象
         ZooKeeper zk=getConnection();
//對象可以使用
         if(zk!=null){
         //手动使用zk命令,在zk集群中创建/red节点  : create /red null
         zk.create(Constants.PROVIDER,  //数据存放的节点路径
         url.getBytes(),  //需要保存的数据轉換成byte數組
         Ids.OPEN_ACL_UNSAFE,  //权限
         CreateMode.EPHEMERAL_SEQUENTIAL);//节点类型-s + -e
         }
     }catch(Exception ex){
   	  ex.printStackTrace();
     }
    }

1.5.3 //發佈遠程服務

 public void pulisher(int port){
     try{
//服务发布在主机的7777端口,消费者调用的访问服务提供者的7777端口
LocateRegistry.createRegistry(port);
//发布远程服务对应的url,消费者访问远程服务的地址
String url="rmi://localhost:"+port+"/sxt";
//创建提供远程服务的对象
RedService redService=new RedServiceImpl();
//完成远程服务发布,给提供远程服务的对象,绑定了一个url
Naming.bind(url, redService);
//调用writeUrl方法,把當前地址傳給寫入zk的方法。
writeUrl(url);
System.out.println("=========远程服务发布成功========"+port);
}catch(Exception ex){
ex.printStackTrace();
}
    }

1.5.4 主綫程調用,進行發佈

public static void main(String[] args) {
//獲取類對象
ZkProviderApp app=new ZkProviderApp();
//調用遠程發佈方法,傳入提供給客戶端訪問并且要存入zk的端口號
app.pulisher(9999);
}

总结执行順序:
1.首先啓動主綫程,調用發佈遠程服務方法,傳入提供給客戶端訪問要寫入zk中的選口號。
2.發佈遠程服務方法拿到傳來的端口號,拼接好要發佈的協議 主機 端口,創建客戶端訪問的對象并且綁定該地址。然後調用把當前拼接好的訪問服務端集群的地址傳給寫入zk的方法。
3.寫入zk的方法首先調用連接zk的方法,判斷是否連接成功。成功,把信息寫入到zk包括(存放url節點路徑,url地址,權限和節點類型-s-e)
4.連接集群的方法首先創建連接zk對象,傳入自己雖然是服務端但現在作爲客戶端要連接zk的訪問zk集群的地址,連接超時時間,和艦艇是否連接成功的匿名内部類實例,在匿内部類中判斷連接狀態,連接成功喚醒阻塞的主綫程,主綫程繼續執行。如果連接失敗,主綫程阻塞。最後根據連接情況返回連接對象。連接對象被上面的寫入zk的方法調用。
**

查看当前当前zookeeper链接地址:ifconfig -a
2.客戶端項目
2.1添加zookeeper依賴和接口資源依賴

com.bisxt.service service_jar 0.0.1-SNAPSHOT org.apache.zookeeper zookeeper 3.4.6

2.2創建消費者啓動類
連接zookeeper集群
獲取服務列表
獲得遠程服務代理對象
調用服務

2.2.1//完成线程同步
private CountDownLatch latch=new CountDownLatch(1);  
//創建一個集合裝從服務中心獲取到的地址,用volatile修饰的变量,线程在每次使用变量的时	候,都会读取变量修改后的最的值。
private volatile List<String> urls =new ArrayList<>();
2.2.2 //創建連接zk方法,和服務端一致
 */
public ZooKeeper getConnection(){
ZooKeeper zooKeeper=null;
try{
zooKeeper=new ZooKeeper(Constants.ZK_HOST,
Constants.TIME_OUT, new Watcher() {
@Override
public void process(WatchedEvent event) {
// TODO Auto-generated method stub
if(Event.KeeperState.SyncConnected==event.getState()){
latch.countDown();
}
}
});
latch.await();
}catch(Exception ex){
ex.printStackTrace();
}
return zooKeeper;
}
2.2.3 讀取集群中的服務列表
public void readUrl(){
try{
//获得zk集群的链接
ZooKeeper zk=getConnection();
if(zk!=null){
//获得red节点下的所有子节点
List<String> childs=zk.getChildren(Constants.REGISTER, new Watcher(){
 
@Override
public void process(WatchedEvent event) {
//观察/red节点下的子节点是否发生改变,如果发生改变重新读取
if(Event.EventType.NodeChildrenChanged==event.getType()){
System.out.println("===========readUrl");
//回调readUrl()
readUrl();
}
}
});
//局部变量
List<String> list=new ArrayList<>();
//遍历/red下的所有子节点
for(String node:childs){
System.out.println(node);
//获得指定节点下的节点
byte[] data = zk.getData(Constants.REGISTER+"/"+node, //节点路径
false, null);
//将byte数组转化为字符串
String url = new String(data);
list.add(url);
}
//把每一個拿到的地址放到集合,把這個集合賦值給上面聲明存放地址的集合
urls=list;
}
}catch(Exception ex){
ex.printStackTrace();
}
}
2.2.4 通過負載均衡算法產生一個服務獲得一個對象
public RedService getRemote(){
RedService service=null;
try{
//产生一个随机数
int index=ThreadLocalRandom.current().nextInt(urls.size());
//随机获得一个远程服务的url,這個集合裏已經存在從服務中心獲取的地址
String url = urls.get(index);
//获得远程服务代理对象
service=(RedService) Naming.lookup(url);
}catch(Exception ex){
ex.printStackTrace();
}
return service;
}
public VipConsumerZkApp(){
//读取远程服务列表
readUrl();
}
1.2.5主方法調用
public static void main(String[] args) {
VipConsumerZkApp app=new VipConsumerZkApp();
while(true){
try{
//获得提供远程服务的对象
RedService service=app.getRemote();
//调用远程方法
String value = service.sendReg(110, 120, 119.0);
System.out.println(value);
Thread.sleep(3000);
}catch(Exception ex){
ex.printStackTrace();
}
}
}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值