Zookeeper 之二、实现Rmi服务的高可用

1、先看不适用zookeeper Rmi服务的实现

首先定义三个Rmi服务端代码类:

HelloService  --》接口,继承remote ;其中有为实现的方法
HelloServiceImpl  --》HelloService  的实现类;实现  HelloService  中的方法
RmiServer   ---》将 HelloServiceImpl 注册为Rmi远程调用方法。

代码: 

package com.bjsxt.server;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface HelloService extends Remote{
	String sayHello(String name) throws RemoteException;
}
package com.bjsxt.server;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

public class HelloServiceImpl extends UnicastRemoteObject implements HelloService  {

	protected HelloServiceImpl() throws RemoteException {
		super();
	}

	@Override
	public String sayHello(String name) throws RemoteException {
		return String.format("hello  %s",name);
	}

}
package com.bjsxt.server;

import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;

public class RmiServer {
	public static void main(String[] args) throws  Exception {
		int port = 8888;
		String url = "rmi://localhost:8888/demo.zookeeper.remoting.server.HelloServiceImpl";
		LocateRegistry.createRegistry(port);
		Naming.rebind(url	, new HelloServiceImpl());
	}
}

Rmi客户端调用:

客户端比较简单,只需要根据服务端的url地址调用就可以了

代码:

package com.bjsxt.client;

import java.rmi.Naming;
import java.rmi.RemoteException;

import com.bjsxt.server.HelloService;

public class RmiClient {
	
	public static void main(String[] args) throws Exception {
//服务端对应的url
		String url ="rmi://localhost:8888/demo.zookeeper.remoting.server.HelloServiceImpl";
		HelloService helloService = (HelloService) Naming.lookup(url);
	
		while(true){
			String result = helloService.sayHello("haijing");
			System.out.println(result);
		}
	
	}

}

 

2、由于现实生活中可能存在Rmi服务的不稳定性,所以要启动多个Rmi服务共客户端调用,但是这心Rmi由谁来管理调用呢,这时zookeeper就出现了,zookeeper可以实现Rmi服务url的管理。(注:这里要先启动zookeeper集群服务)

1)、看流程图:

这就是zookeeper管理rmi服务的流程图。

2)服务端代码:

在实现代码之前要导入相应的jar包

这里zookeeper.jar是必须的,其他的可以不用

代码:

Constant  -->静态常量接口

ServiceProvider  -->服务的提供类,相关方法的实现

Server  --->注册服务的server

package com.bjsxt.util;

public interface Constant {
	
	 String ZK_CONNECTION_STRING = "192.168.47.21:2181,192.168.47.22:2181,192.168.47.23:2181";
	    int ZK_SESSION_TIMEOUT = 5000;
	    String ZK_REGISTRY_PATH = "/registry";
	    String ZK_PROVIDER_PATH = ZK_REGISTRY_PATH + "/provider";

}
package com.bjsxt.server;

import java.io.IOException;
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.util.concurrent.CountDownLatch;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.bjsxt.util.Constant;

/**
 * 本类的作用是 注册rmi服务,并将服务地址放在zookeeper上 ;消费者在消费时
 * @author root
 *
 */
public class ServiceProvider {
	
	  private static final Logger LOGGER = LoggerFactory.getLogger(ServiceProvider.class);
	  
	  //CountDownLatch 中配置的参数是当调用countDown 方法时 参数 -1 当参数变为 0 时 就可以 跳过 await方法 否则 就会阻塞,线程停止运行;
	 CountDownLatch  latch = new CountDownLatch(1);
	
	 /**
	  * 注册服务并建立zookeeper临时节点的方法,入口
	  * @param remote
	  * @param host
	  * @param port
	  */
	public void  publish(Remote remote, String host, int port) {
		 String url = publishService(remote,host,port);
		 if(url!=null){
			   ZooKeeper  zk = connectServer();
			   if(zk !=null){
				   createNode(zk ,url);
			   }
		 }
	}



	/**
	 * 发布rmi服务
	 * @param remote
	 * @param host
	 * @param port
	 * @return
	 */
	private String publishService(Remote remote, String host, int port) {
		String url =null;
		
		try {
			url = String.format("rmi://%s:%d/%s", host,port,remote.getClass().getName());
			LocateRegistry.createRegistry(port);
			 Naming.rebind(url, remote);
		} catch (RemoteException e) {
				e.printStackTrace();
		} catch (MalformedURLException e) {
				e.printStackTrace();
		}
		
		return url;
	}



	/**
	 * 连接zookeeper服务器
	 * @return
	 */
	private ZooKeeper connectServer(){
		ZooKeeper zk = null;
		  try {
			zk = new ZooKeeper(Constant.ZK_CONNECTION_STRING, Constant.ZK_SESSION_TIMEOUT, new Watcher(){

				@Override
				public void process(WatchedEvent event) {
					if(event.getState() == Event.KeeperState.SyncConnected){
						latch.countDown();
						
					}
					
				}
				  
			  });
	
		  latch.await();
			} catch (IOException e) {
				e.printStackTrace();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		  
		  return zk ;
		
	}
	
	/**
	 * 在zookeeper上创建节点
	 * @param zk
	 * @param url
	 */
	private void createNode(ZooKeeper zk, String url) {
		try {
		 
			byte[] data = url.getBytes();
//			ZooDefs.Ids.OPEN_ACL_UNSAFE  这个是最大权限,所有人都可以来修改
			String path = zk.create(Constant.ZK_PROVIDER_PATH, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
			  LOGGER.debug("create zookeeper node ({} => {})", path, url);
		} catch (KeeperException | InterruptedException e) {
			 
			e.printStackTrace();
		}
		
	}
	
}

 

package com.bjsxt.server;

import java.rmi.RemoteException;

public class Server {
	
	public static void main(String[] args) throws Exception {
		String host = "192.168.1.148";
		int port = Integer.parseInt("11233");
		ServiceProvider serviceProvider = new ServiceProvider();
		
		HelloServiceImpl service = new HelloServiceImpl();
		serviceProvider.publish(service,host,port);
		Thread.sleep(Long.MAX_VALUE);
	}

}

执行server中的main方法会报错说  registry 目录不存在,需要手动创建

手动创建

此时再启动 Server 就不报错了;观察zookeeper中的变化

 

rmi服务已经注册在zookeeper中了。

客户端代码

Client  -->启动客户端的入口

ServiceConsumer  -->相关方法的实现

package com.bjsxt.client;

import java.io.IOException;
import java.net.ConnectException;
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadLocalRandom;

import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.bjsxt.server.HelloService;
import com.bjsxt.util.Constant;

public class ServiceConsumer {
	
	 
    private static final Logger LOGGER = LoggerFactory.getLogger(ServiceConsumer.class);
 
    
    // 用于等待 SyncConnected 事件触发后继续执行当前线程
    private CountDownLatch latch = new CountDownLatch(1);
 
//    放置url的容器
    private volatile List<String> urlList = new ArrayList<>(); 

    public  ServiceConsumer() throws KeeperException, InterruptedException {
		ZooKeeper zk = connectServer();
		if(zk!=null){
			watchNode(zk);
		}
	}
    
    /**
     * 观察节点是否有变化,并将url放入容器中
     * @param zk
     */
	private void watchNode(final ZooKeeper zk)   {
		try {
			List<String> nodelist = zk.getChildren(Constant.ZK_REGISTRY_PATH, new Watcher(){

				@Override
				public void process(WatchedEvent event) {
					if(event.getType()==Event.EventType.NodeChildrenChanged){
						watchNode(zk);
					}
					
				}
				
			});
			
			ArrayList<String> datalist = new ArrayList<String>();
			for (String node : nodelist) {
				byte[] data = zk.getData(Constant.ZK_REGISTRY_PATH+"/"+node, false, null);
				datalist.add(new String(data));
			}
			
			urlList =datalist;// 更新最新的 RMI 地址
		} catch (KeeperException | InterruptedException e) {
			e.printStackTrace();
		}
		
	}
	public <T extends Remote> T lookup() {
		T service = null;
		int size = urlList.size();
		if (size>0) {
			String url;
			if (size==1) {
				url = urlList.get(0);
				
			}else {
				url = urlList.get(ThreadLocalRandom.current().nextInt(size));
			}
			service = lookupService(url);
		}
		return service;
	}
	
	
	/**
	 * 查找rmi远程服务对象
	 * @param url
	 * @return
	 */
    @SuppressWarnings("unchecked")
	private <T> T lookupService(String url) {
    	
    	T remote = null;
    	try {
			remote = (T) Naming.lookup(url);
		} catch (MalformedURLException | RemoteException | NotBoundException e) {
				if ( e instanceof ConnectException) {
					if (urlList.size() !=0) {
						url = urlList.get(0);
						return lookupService(url);
					}
				}
				LOGGER.error("", e);
		}
		return remote;
	}

	// 连接 ZooKeeper 服务器
    private ZooKeeper connectServer() {
        ZooKeeper zk = null;
        try {
            zk = new ZooKeeper(Constant.ZK_CONNECTION_STRING, Constant.ZK_SESSION_TIMEOUT, new Watcher() {
                @Override
                public void process(WatchedEvent event) {
                    if (event.getState() == Event.KeeperState.SyncConnected) {
                        latch.countDown(); // 唤醒当前正在执行的线程
                    }
                }
            });
            latch.await(); // 使当前线程处于等待状态
        } catch (IOException | InterruptedException e) {
            LOGGER.error("", e);
        }
        return zk;
    }
}

 

package com.bjsxt.client;

import java.rmi.RemoteException;

import org.apache.zookeeper.KeeperException;

import com.bjsxt.server.HelloService;

public class Client {
	
	public static void main(String[] args) throws RemoteException, InterruptedException, KeeperException {
		ServiceConsumer consumer = new ServiceConsumer();
//		zookeeper测试
		
		while (true) {
			HelloService helloService = consumer.lookup();
			String sayHello = helloService.sayHello("haijing");
			System.out.println(sayHello);
			Thread.sleep(3000);
			
		}
	}

}

 

启动client就可以了  ,这里的server可以更换端口启用多个

转载于:https://my.oschina.net/captainliu/blog/746011

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值