基于zookeeper的远程方法调用(RMI)的实现

采用zookeeper的命令服务,采用不同的目录结构存储不同模块不同服务的rmi的url,使用key来对应不同的服务。同时采用zookeeper解决了单点问题。

当有两个相同的服务注册时,因为采用的是临时有序znode,也会注册成功,客户端在调用时,发现有多个不同url,则会随机调取一个使用。

当一个服务down时,zookeeper会通知客户端更新url缓存,从而去掉down服务的链接。加入时,也会同步客户端url数据。

原理图如下:

1).目录结构

2).zookeeper调用

3).分布式调用

实现代码如下:

1.定义测试服务接口与实现

public interface Service extends Remote,Serializable{ public void service(String name);

} 

public class ServiceImpl implements Service{

    @Override public void service(String name) {
        System.out.println(name);
    }

}

2.RMIServer

public class RMIServer { private static final Logger logger = LoggerFactory
            .getLogger(RMIServer.class); private static ZooKeeper zk; private boolean isRegistry = false; //同步锁 private Lock _lock = new ReentrantLock(); private static final Map<String,String> CACHED_URL = new HashMap<String,String>(); public RMIServer() {
        zk = connectServer(); new Thread(new Runnable() {
            @Override public void run() { while (true) { try {
                        Thread.currentThread().sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    logger.info("check zk...");
                    _lock.lock(); if (zk != null) { if (zk.getState().isAlive() && zk.getState().isConnected()) {
                            logger.info("zk is ok");
                            _lock.unlock(); continue;
                        }
                    }
                    close();
                    logger.info("reConnectServer ...");
                    zk = connectServer();
                    Iterator<String> it = CACHED_URL.keySet().iterator(); while(it.hasNext()){
                        String key = it.next();
                        String url = CACHED_URL.get(key);
                        createNode(zk,url,key);
                    }
                    logger.info("reConnectServer ok");
                    _lock.unlock();
                }

            } private void close() { if(zk!=null){ try {
                        zk.close();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    zk = null;
                }
            }
        }).start();
    } // 用于等待 SyncConnected 事件触发后继续执行当前线程 private CountDownLatch latch = new CountDownLatch(1); // 发布 RMI 服务并注册 RMI 地址到 ZooKeeper 中 public void publish(Remote remote, String key) {
        _lock.lock();
        String url = publishService(remote); // 发布 RMI 服务并返回 RMI 地址 if (url != null) { if (zk != null) { if (zk.getState().isAlive() && zk.getState().isConnected()) {
                } else { if(zk!=null){ try {
                            zk.close();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        zk = null;
                    }
                    zk = connectServer();
                }
                createNode(zk, url, key); // 创建 ZNode 并将 RMI 地址放入 ZNode 上  }
        }
        _lock.unlock();

    } // 发布 RMI 服务 private String publishService(Remote remote) {
        String url = null; try {
            String host = ConfigHelp.getLocalConifg("rmiIP", "127.0.0.1"); int port = Integer.valueOf(ConfigHelp.getLocalConifg("rmiPort", "10990"));
            url = String.format("rmi://%s:%d/%s", host, port, remote.getClass()
                    .getName()); if (!isRegistry) {
                LocateRegistry.createRegistry(port);
                isRegistry = true;
            }
            Naming.rebind(url, remote);
            logger.debug("publish rmi service (url: {})", url);
        } catch (RemoteException | MalformedURLException e) {
            logger.error("", e);
        } return url;
    } // 连接 ZooKeeper 服务器 private ZooKeeper connectServer() {
        ZooKeeper zk = null; try {

            zk = new ZooKeeper(ConfigHelp.ZK_CONNECTION_STRING,
                    ConfigHelp.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);
        } if (zk != null) { try {
                Stat stat = zk.exists(ConfigHelp.ZK_ROOT_PATH, false); if (stat == null) {
                    String path = zk.create(ConfigHelp.ZK_ROOT_PATH, "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,
                            CreateMode.PERSISTENT); // 创建一个临时性且有序的 ZNode logger.info("create zookeeper node ({})", path);
                }
                stat = zk.exists(ConfigHelp.ZK_RMI_PATH, false); if (stat == null) {

                    String path = zk.create(ConfigHelp.ZK_RMI_PATH, "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,
                            CreateMode.PERSISTENT); // 创建一个临时性且有序的 ZNode logger.info("create zookeeper node ({})", path);
                }
            } catch (KeeperException | InterruptedException e) {
                e.printStackTrace();
            }
        } return zk;
    } // 创建 ZNode private void createNode(ZooKeeper zk, String url, String key) { try {
            CACHED_URL.put(key, url); byte[] data = (key + "#:#" + url).getBytes();
            String path = zk.create(ConfigHelp.ZK_RMI_PATH + "/", data,
                    ZooDefs.Ids.OPEN_ACL_UNSAFE,
                    CreateMode.EPHEMERAL_SEQUENTIAL); // 创建一个临时性且有序的 ZNode logger.info("create zookeeper node ({} => {})", path, url);
        } catch (KeeperException | InterruptedException e) {
            logger.error("", e);
            e.printStackTrace();
        }
    } public static void main(String[] args) throws Exception {
        RMIServer server = new RMIServer();
        Service service = new ServiceImpl();
        server.publish(service, "Test");
        server.publish(service, "Test1");
        Thread.currentThread().sleep(50000);
        server.publish(service, "Test3");
        Thread.currentThread().sleep(Integer.MAX_VALUE);
    }
}

3.RMIClient

public class RMIClient { private static final Logger logger = LoggerFactory.getLogger(RMIClient.class); // 用于等待 SyncConnected 事件触发后继续执行当前线程 private CountDownLatch latch = new CountDownLatch(1); // 定义一个 volatile 成员变量,用于保存最新的 RMI 地址(考虑到该变量或许会被其它线程所修改,一旦修改后,该变量的值会影响到所有线程) private volatile HashMap<String,List<String>> dataMap = new HashMap<String, List<String>>(); private Lock _lock = new ReentrantLock(); private static ZooKeeper zk; // 构造器 public RMIClient() {
        zk = connectServer(); // 连接 ZooKeeper 服务器并获取 ZooKeeper 对象  watchNode(); new Thread(new Runnable() {

            @Override public void run() { while (true) { try {
                        Thread.currentThread().sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    _lock.lock(); if (zk != null) { if (zk.getState().isAlive() && zk.getState().isConnected()) {
                            _lock.unlock(); continue;
                        }
                    } if(zk!=null){ try {
                            zk.close();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        zk = null;
                    }
                    zk = connectServer();
                    _lock.unlock();
                }
            }
        }).start();
    } // 查找 RMI 服务 public <T extends Remote> T lookup(String key) {
        T service = null; int size = dataMap.size(); if (size > 0) {
            String url = null; if(dataMap.containsKey(key)){
                List<String> urlList = dataMap.get(key); if(urlList.size()>0){ if(urlList.size()==1){
                         url = urlList.get(0);
                    }else{
                        url = urlList.get(ThreadLocalRandom.current().nextInt(size));
                    }
                }
                 service = lookupService(url,key); // 从 JNDI 中查找 RMI 服务  }
        } return service;
    } // 连接 ZooKeeper 服务器 private ZooKeeper connectServer() {
        ZooKeeper zk = null; try {
            zk = new ZooKeeper(ConfigHelp.ZK_CONNECTION_STRING, ConfigHelp.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;
    } // 观察 /registry 节点下所有子节点是否有变化 private void watchNode() {
        _lock.lock(); if(zk!=null&&zk.getState().isAlive()&&zk.getState().isConnected()){

        }else{ if(zk!=null){ try {
                    zk.close();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                zk = null;
            }
            zk = connectServer();
        } try {
            List<String> nodeList = zk.getChildren(ConfigHelp.ZK_RMI_PATH, new Watcher() {
                @Override public void process(WatchedEvent event) { if (event.getType() == Event.EventType.NodeChildrenChanged) {
                        watchNode(); // 若子节点有变化,则重新调用该方法(为了获取最新子节点中的数据)  }
                }
            });
            List<String> dataList = new ArrayList<>(); // 用于存放 /registry 所有子节点中的数据 HashMap<String,List<String>> dataMap = new HashMap<String, List<String>>(); for (String node : nodeList) { byte[] data = zk.getData(ConfigHelp.ZK_RMI_PATH + "/" + node, false, null); // 获取 /registry 的子节点中的数据 dataList.add(new String(data));
                String d = new String(data).toString();
                String key = d.split("#:#")[0];
                String url = d.split("#:#")[1]; if(dataMap.containsKey(key)){
                    dataMap.get(key).add(url);
                }else{
                    List<String> list = new ArrayList<String>();
                    list.add(url);
                    dataMap.put(key, list);
                }
            }
            logger.debug("node data: {}", dataList); this.dataMap = dataMap;
        } catch (KeeperException | InterruptedException e) {
            logger.error("", e);
        }
        _lock.unlock();
    } // 在 JNDI 中查找 RMI 远程服务对象 @SuppressWarnings("unchecked") private <T> T lookupService(String url,String key) {
        T remote = null; try {
            remote = (T) Naming.lookup(url);
        } catch (NotBoundException | MalformedURLException | RemoteException e) { if (e instanceof ConnectException) { // 若连接中断,则使用 urlList 中第一个 RMI 地址来查找(这是一种简单的重试方式,确保不会抛出异常) logger.error("ConnectException -> url: {}", url); if(dataMap.containsKey(key)){
                    List<String> urlList = dataMap.get(key); if(urlList.size()>0){ return lookupService(urlList.get(0),key);
                    }
                }
            }
            logger.error("", e);
        } return remote;
    } public static void main(String[] args) {
        RMIClient client = new RMIClient(); while(true){
            Service service = client.lookup("Test");
            service.service("test12"); try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

4.RMIHelper

public class RMIHelper { private static final RMIServer SERVER = new RMIServer(); private static final RMIClient CLIENT = new RMIClient(); public static synchronized void publish(Remote remote,String key){
        SERVER.publish(remote, key);
    } public static synchronized <T extends Remote> T lookup(String key){ return CLIENT.lookup(key);
    } public static void main(String[] args) throws Exception { while(true){
            Service service = RMIHelper.lookup("Test");
            service.service("test12");
            Service service1 = RMIHelper.lookup("Test1");
            service1.service("test12"); try {
                Thread.sleep(2000);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }
}

时间: 2024-10-09 03:01:44

基于zookeeper的远程方法调用(RMI)的实现的相关文章

远程方法调用——RMI

RMI RMI  (Remote  Method  Invocation)是Java用于实现透明远程调用的重要机制.在远程调用中,客户端仅有服务器端提供的接口 .通过此接口实现对远程服务器端的调用.其威力就体现在它强大的开发分布式网络应用的能力上,是纯Java的网络分布式应用系统的核心解决方案之一.其实它可以被看作是RPC的Java版本.但是传统RPC并不能很好地应用于分布式对象系统.而Java RMI 则支持存储于不同地址空间的程序级对象之间彼此进行通信,实现远程对象之间的无缝远程调用. Su

【转帖】基于Zookeeper的服务注册与发现

http://www.techweb.com.cn/network/hardware/2015-12-25/2246973.shtml 背景 大多数系统都是从一个单一系统开始起步的,随着公司业务的快速发展,这个单一系统变得越来越庞大,带来几个问题: 1. 随着访问量的不断攀升,纯粹通过提升机器的性能来已经不能解决问题,系统无法进行有效的水平扩展 2. 维护这个单一系统,变得越来越复杂 3. 同时,随着业务场景的不同以及大研发的招兵买马带来了不同技术背景的工程师,在原有达达Python技术栈的基础

Ignite集群管理——基于Zookeeper的节点发现

Ignite支持基于组播,静态IP,Zookeeper,JDBC等方式发现节点,本文主要介绍基于Zookeeper的节点发现. 环境准备,两台笔记本电脑A,B.A笔记本上使用VMware虚拟机安装了Ubuntu系统C. 1. C安装Zookeeper 由于主要测试Ignite,这里仅仅简单安装一个zookeeper节点,下载zookeeper解压后,直接执行zookeeper目录下的bin/zkServer.sh start命令则成功启动zookeeper. 查看Ubuntu系统C的IP地址为1

通过dubbo暴露接口调用方法,及基于zookeeper的dubbo涉及配置文件【转】

现在很流行的Dubbo很多朋友都听说过吧,最近我也在看这方面的东西,分享先我的心得笔记. 先说说我们团队要做的项目框架,很简单重在实现基于zookeeper的dubbo注册. 框架:springmvc+spring+zookeeper+dubbo 项目分三层,model存放数据,view页面展示.controller下面具体逻辑实现.通过dubbo消费方和供应方注册,供应方给消费方暴露接口,供消费方调用. 工程部署需要配置文件有: applicationContext-dubbo.xml {--

基于ZooKeeper的分布式Session实现(转)

1.   认识ZooKeeper ZooKeeper—— “动物园管理员”.动物园里当然有好多的动物,游客可以根据动物园提供的向导图到不同的场馆观赏各种类型的动物,而不是像走在原始丛林里,心惊胆颤的被动 物所观赏.为了让各种不同的动物呆在它们应该呆的地方,而不是相互串门,或是相互厮杀,就需要动物园管理员按照动物的各种习性加以分类和管理,这样我们才 能更加放心安全的观赏动物.回到我们企业级应用系统中,随着信息化水平的不断提高,我们的企业级系统变得越来越庞大臃肿,性能急剧下降,客户抱怨频频.拆 分系

dubbo 入门教程 基于zookeeper

从上午发布的安装zookeeper 遇到的第一个错误开始 一直在搞dubbo 中午吃了饭 睡了会觉 .中间错误一个又一个 慢慢的都解决了.小有成就,哈哈.算这个星期天没有白费吧. 学习的过程值得留恋.分享一下吧 只是之前听网友提过dubbo这个东西 一直都没有百度和使用过.前天答应老板 我们框架中留一个口出来 给dubbo .今天天气热 就在宿舍研究吧. 首先是百度 知道是做什么的 后来就开始找教程 但是大家都知道 百度的教程 基本都是一个模板.看了大概有20篇 基本就换换字 代码 都是一样的.

基于zookeeper+leveldb搭建activemq集群--转载

原地址:http://www.open-open.com/lib/view/open1410569018211.html 自从activemq5.9.0开始,activemq的集群实现方式取消了传统的 Master-Slave方式,增加了基于zookeeper+leveldb的实现方式,其他两种方式:目录共享和数据库共享依然存在.本文主要阐述基 于zookeeper和leveldb搭建activemq集群,这里需要特别提醒,本文实现的集群仅提供主备功能,避免单点故障,没有负载均衡功能. 下面开始

基于zookeeper+leveldb搭建activemq集群

自从activemq5.9.0开始,activemq的集群实现方式取消了传统的Master-Slave方式,增加了基于zookeeper+leveldb的实现方式,其他两种方式:目录共享和数据库共享依然存在.本文主要阐述基于zookeeper和leveldb搭建activemq集群,这里需要特别提醒,本文实现的集群仅提供主备功能,避免单点故障,没有负载均衡功能. 下面开始我们的征途. 一.搭建zookeeper集群 关于搭建zookeeper集群的文章请参考:zookeeper的集群模式下的安装

ActiveMQ 基于zookeeper的主从(levelDB Master/Slave)搭建以及Spring-boot下使用

0:说明 ActiveMQ 5.9.0新推出的主从实现,基于zookeeper来选举出一个master,其他节点自动作为slave实时同步消息.因为有实时同步数据的slave的存在,master不用担心数据丢失,所以leveldb会优先采用内存存储消息,异步同步到磁盘,所以该方式的activeMQ读写性能最好因为选举机制要超过半数,所以最少需要3台节点,才能实现高可用.如果集群是两台则master失效后slave会不起作用,所以集群至少三台.此种方式仅实现主备功能,避免单点故障,没有负载均衡功能