网站psd设计稿,沈阳网站建设团队,百度地图导航下载安装,汕头网站搭建这可不是目录 1.RMI原理与说明1.1含义1.2流程1.3rmi的简单实现1.4RMI的局限性 2.zookeeper实现RMI服务#xff08;高可用、HA#xff09;2.1实现原理2.2高可用分析2.3zookeeper实现2.3.1代码分析2.3.2公共部分2.3.3服务端2.3.4客户端2.3.5运行与部署2.3.6效果展示与说明 1.RM… 这可不是目录 1.RMI原理与说明1.1含义1.2流程1.3rmi的简单实现1.4RMI的局限性 2.zookeeper实现RMI服务高可用、HA2.1实现原理2.2高可用分析2.3zookeeper实现2.3.1代码分析2.3.2公共部分2.3.3服务端2.3.4客户端2.3.5运行与部署2.3.6效果展示与说明 1.RMI原理与说明
1.1含义
远程方法调用 仅适用于JAVA RMI是一种用于实现远程过程调用的应用程序编程接口。它使客户机上运行的程序可以调用远程服务器上的对象。远程方法调用特性使Java编程人员能够在网络环境中分布操作。RMI全部的宗旨就是尽可能简化远程接口对象的使用。RMIRemote Method Invocation远程方法调用是一种计算机之间利用远程对象互相调用实现双方通讯的一种通讯机制。使用这种机制某一台计算机上的对象可以调用另外一台计算机上的对象来获取远程数据。 1.2流程 1.3rmi的简单实现
客户端RmiClient.java
package com.rmi.client;import com.rmi.common.HelloService;import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.Remote;
import java.rmi.RemoteException;public class RmiClient {public static void main(String[] args) throws MalformedURLException, NotBoundException, RemoteException {System.out.println(rmi client running);//定义urlString url rmi://127.0.0.1:1099/com.rmi.server.HelloServiceImpl;//寻找发布的服务Remote lookup Naming.lookup(url);//强制类型转换HelloService helloService (HelloService) lookup;//调用目标方法String result helloService.sayHello(wunaiieq);System.out.println(result:result);}
}远程接口HelloService.java
package com.rmi.common;import java.rmi.Remote;
import java.rmi.RemoteException;public interface HelloService extends Remote {String sayHello(String name) throws RemoteException;
}远程接口的实现类HelloServiceImpl.java
package com.rmi.server;import com.rmi.common.HelloService;import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;public class HelloServiceImpl extends UnicastRemoteObject implements HelloService {protected HelloServiceImpl() throws RemoteException {}Overridepublic String sayHello(String name) throws RemoteException {return Helloname;}
}rmi服务RmiServer
package com.rmi.server;import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;public class RmiServer {public static void main(String[] args) throws Exception{//定义发布RMi服务的端口int port 1099;String url rmi://127.0.0.1:1099/com.rmi.server.HelloServiceImpl;//注册服务:相当于在JNDI中创建了一个注册表LocateRegistry.createRegistry(port);//绑定服务:将RMI服务的实现类对象和url绑定Naming.rebind(url,new HelloServiceImpl());}
}运行说明 先启动RmiServer注册远程对象再启动RmiCllient查找此对象并调用远程方法 1.4RMI的局限性
只能使用Java不支持跨语言RMI使用了Java默认的序列化方式对于要求较高的系统可能需要其他的序列化方案进行解决ProtobufRMI服务在运行时可能会出现单点故障的问题因此需要配置实现高可用HA 2.zookeeper实现RMI服务高可用、HA
2.1实现原理 服务注册与发现RMI服务端在启动后可以在ZooKeeper上注册一个临时节点Ephemeral Node并将自己的服务地址写入该节点。客户端在需要调用RMI服务时可以监听ZooKeeper上的这些临时节点以获取服务地址。由于ZooKeeper会监控这些节点的状态一旦服务端节点宕机或断开连接对应的临时节点就会被自动删除。客户端在感知到这一变化后可以重新获取有效的服务地址从而实现了服务的自动发现和故障切换。 负载均衡ZooKeeper还可以作为服务注册中心为多个RMI服务端实例提供统一的注册和发现接口。客户端在调用RMI服务时可以通过ZooKeeper获取到多个服务端的地址并根据一定的策略如轮询、随机等选择一个进行调用。这样可以实现负载的均衡分配避免单个服务端过载。注意此处只能实现相对均衡原则上不等同于负载均衡服务器 服务状态监控ZooKeeper可以监控RMI服务端的状态信息如CPU使用率、内存占用率等并将这些信息反馈给客户端或系统管理员。当发现某个服务端状态异常时可以及时采取措施如重启服务、扩展资源等来恢复服务的正常运行。 zookeeper在此处相当于一个注册表
2.2高可用分析 1. rmi服务高可用 首先一个服务端只能运行于一台服务中心上提供的同一个服务可以存在多个这样当某一个服务宕机时服务中心仍存在其他相同的服务。 因此这样的同名服务同时运行但是端口不一致客户端在调用这样的服务时随机选取(自定义选取也可以)一个znode节点调用rmi服务 2. 服务注册中心高可用 上述的服务已经保证了不会宕机但服务中心仍存在宕机的可能。 因此配置zookeeper高可用在每个zookeeper上运行相应的服务以保证当一个服务中心宕机仍然可以提供服务。
2.3zookeeper实现
2.3.1代码分析
将设置6个代码块红框表示客户端程序黄框表示服务器端中间为公共部分
2.3.2公共部分
客户端和服务端应共同规定这个包中所有的接口 Constant.java 这个接口主要是规定一些常量以便于服务端和客户端的使用
package com.zkrmi.common;public interface Constant {//zk集群的地址String ZK_CONNECTION_STRING192.168.80.111:2181,192.168.80.112:2181,192.168.80.113:2181;//连接超时时间int ZK_SESSION_TIMEOUT 5000;//服务列表对应的临时节点的parent节点String ZK_REGISTRY_PATH/registry;//临时节点的路径注意创建的是临时顺序节点因此最后显示的是provider0provider1....String ZK_provider_PATHZK_REGISTRY_PATH/provider;
}HelloService.java 远程接口这是提前定义好应当被客户端和服务端同时知晓的共同规定的。
package com.zkrmi.common;import java.rmi.Remote;
import java.rmi.RemoteException;public interface HelloService extends Remote {String sayHello(String name) throws RemoteException;
}2.3.3服务端
任务1. 实现远程接口 任务2. 在zookeeper上注册远程方法服务 HelloServiceImpl.java 这串代码主要用于实现远程接口没有什么特殊的点
package com.zkrmi.server;import com.rmi.common.HelloService;import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;public class HelloServiceImpl extends UnicastRemoteObject implements HelloService {protected HelloServiceImpl() throws RemoteException {}Overridepublic String sayHello(String name) throws RemoteException {//注意在不同的zk集群上请修改以下说明在实际生产中不同节点上的实现类应保持一致return Hello_ZK112name;}
}
ServiceProvider.java 在zookeeper上发布可以提供的服务即远程对象这里进行了封装后续的调用将在主函数中进行。
package com.zkrmi.server;import com.zkrmi.common.Constant;
import com.zookeeper.ZooKeeperFactory;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;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;public class ServiceProvider {private CountDownLatch latch new CountDownLatch(1);/*** 发布RMI服务** param remote 远程对象即HelloServiceImpl的实例* param host 192.168.80.113zookeeper的地址* param port 11214,11215,11216,这个表示端口* return rmi地址 rmi://192.168.80.113:11214/com.rmi.server.HelloServiceImpl*/private String publishServer(Remote remote, String host, int port) {String url null;try {//设置发布服务的rmi地址 rmi://192.168.80.113:11214/com.rmi.server.HelloServiceImplurl String.format(rmi://%s:%d/%s, host, port, remote.getClass().getName());//注册服务:相当于在JNDI中创建了一个注册表LocateRegistry.createRegistry(port);//绑定服务:将RMI服务的实现类对象和url绑定Naming.rebind(url, remote);} catch (RemoteException | MalformedURLException e) {e.printStackTrace();}return url;}/*** 创建临时节点** param zk:传入一个zookeeper对象* param url:url是临时节点的数据表示rmi地址*/private void createNode(ZooKeeper zk, String url) {try {byte[] data url.getBytes();//创建一个临时有序的节点String result zk.create(Constant.ZK_provider_PATH, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);System.out.println(节点创建成功result);} catch (Exception e) {e.printStackTrace();}}/*** param remote 调用时填入一个远程对象,这里使用remote进行标识表示此对象可以从服务器端调用* */public void publish(Remote remote, String host, int port) throws Exception {//调用publishServer发布rmi服务获取rmi的地址String url publishServer(remote, host, port);//连接zookeeper集群if (url!null){ZooKeeper zk ZooKeeperFactory.create(Constant.ZK_CONNECTION_STRING);if (zk!null){createNode(zk,url);}else {System.out.println(zknull,节点创建失败);}}else {System.out.println(urlnull,发布失败);}}}
Server.java 作为服务器端的主类值得说明的是远程服务端的端口自拟即可建议在1024到49151
package com.zkrmi.server;import com.rmi.common.HelloService;import java.rmi.RemoteException;//作为服务端的main类
public class Server {public static void main(String[] args) throws Exception {//zk节点String host 192.168.80.112;//第一个端口System.out.println(server 10100 start);int port Integer.parseInt(10100);//创建服务的生产者对象ServiceProvider provider0 new ServiceProvider();//创建远程对象客户端将使用此对象进行远程方法调用HelloService helloService new HelloServiceImpl();//发布rmi服务provider0.publish(helloService,host,port);//第二个端口System.out.println(server 10101 start);int port1 Integer.parseInt(10101);ServiceProvider provider1 new ServiceProvider();provider1.publish(helloService,host,port1);}
}
2.3.4客户端
任务1获取所有的rmi地址 任务2当rmi地址更新或者某个服务器异常时需要重新获取 ServiceConsumer.java 客户端的方法支持实现上述任务1和任务2的要求
package com.zkrmi.client;import com.zkrmi.common.Constant;
import com.zookeeper.ZooKeeperFactory;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;import java.rmi.ConnectException;
import java.rmi.Naming;
import java.rmi.Remote;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadLocalRandom;public class ServiceConsumer {private CountDownLatch latch new CountDownLatch(1);/**保存最新的rmi地址*/private volatile ListString urlList new ArrayList();/**构造器用于观察/registry节点的所有子节点并更新urlList*/public ServiceConsumer() throws Exception {ZooKeeper zk ZooKeeperFactory.create(Constant.ZK_CONNECTION_STRING);if (zk!null){watchNode(zk);}}/**观察/registry节点下所有子节点是否有变化* br初始化构造器中调用一次获取所有rmi地址* br若有重新调用此方法更新rmi地址* param zk 用final定义防止后续调用时获取的节点是新节点监听不到变化* */private void watchNode(final ZooKeeper zk){try {//获取所有子节点名称并设置监听ListString nodeList zk.getChildren(Constant.ZK_REGISTRY_PATH, new Watcher() {//当子节点发生变化时再次调用这个watchNode方法Overridepublic void process(WatchedEvent event) {if (event.getType()Event.EventType.NodeChildrenChanged){watchNode(zk);}}});ListString dataList new ArrayList();//根据子节点名称进行遍历,获取所有子节点的数据即rmi的地址for (String node:nodeList){byte[] data zk.getData(Constant.ZK_REGISTRY_PATH/node,false,null);dataList.add(new String(data));}//获取所有rmi地址更新--重新调用此方法--再度获取urlList dataList;} catch (InterruptedException e) {throw new RuntimeException(e);} catch (KeeperException e) {throw new RuntimeException(e);}}/**查找rmi服务* */public T extends Remote T lookup(){T service null;//由于前面的更新因此urlList始终保持的是最新的int size urlList.size();if (size0){String url;if (size 1){url urlList.get(0);System.out.println(只获取到一个url:url);}else {url urlList.get(ThreadLocalRandom.current().nextInt(size));System.out.println(获取一个随机的url:url);}System.out.println(当前url:url);service lookupService(url);}return service;}SuppressWarnings(unchecked)private T T lookupService(String url){T remote null;try {remote(T) Naming.lookup(url);}catch (Exception e){if (e instanceof ConnectException){System.out.println(连接中断重试);if (urlList.size()!0){urlurlList.get(0);return lookupService(url);}}}return remote;}
}
Client.java 作为客户端的主类实现远程方法调用
package com.zkrmi.client;import com.rmi.common.HelloService;public class Client {public static void main(String[] args) throws Exception {ServiceConsumer consumer new ServiceConsumer();while (true){HelloService helloService consumer.lookup();String result helloService.sayHello(wunaiieq);System.out.println(result);Thread.sleep(3000);}}
}2.3.5运行与部署
建议部署于虚拟机上为测试zookeeper效果请将服务端部署于不同的zookeeper节点上客户端可以运行于非zookeeper集群的主机上述代码中缺少zookeeperFactory.java可以去博客中复制不放这太乱了
打包 pom.xml 为实现如下效果请打3个包2个服务端1个客户端打包时修改pom.xml文件中的主函数
?xml version1.0 encodingUTF-8?
project xmlnshttp://maven.apache.org/POM/4.0.0xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersiongroupIdcom.wunaiieq/groupIdartifactIdzookeeper02/artifactIdversion1.0-SNAPSHOT/versionpropertiesmaven.compiler.source1.8/maven.compiler.sourcemaven.compiler.target1.8/maven.compiler.targetproject.build.sourceEncodingUTF-8/project.build.sourceEncoding/propertiesdependenciesdependencygroupIdorg.apache.zookeeper/groupIdartifactIdzookeeper/artifactIdversion3.7.1/version/dependency/dependenciesbuildpluginsplugin!--声明--groupIdorg.apache.maven.plugins/groupIdartifactIdmaven-assembly-plugin/artifactIdversion3.3.0/version!--具体配置--configurationarchivemanifest!--jar包的执行入口--mainClasscom.zkrmi.client.Client/mainClass/manifest/archivedescriptorRefs!--描述符此处为预定义的表示创建一个包含项目所有依赖的可执行 JAR 文件;允许自定义生成jar文件内容--descriptorRefjar-with-dependencies/descriptorRef/descriptorRefs/configuration!--执行配置--executionsexecution!--执行配置ID可修改--idmake-assembly/id!--执行的生命周期--phasepackage/phasegoals!--执行的目标single表示创建一个分发包--goalsingle/goal/goals/execution/executions/plugin/plugins/build
/project
2.3.6效果展示与说明 112 113为连接到zookeeper集群的主机作为服务中心 114为客户端调用rmi服务 检查114的输出可以看到服务能正常调用 代码其他解释 zk集群中113状态为leader112follower同时运行服务端jar包均正常创建了rmi服务对象 ZK高可用 当一个zk节点宕机另一个仍可以正常运行。保证服务不中断 服务高可用 由于服务的端口号不一致因此当关闭一个服务时仍存在rmi服务供客户端使用 客户端选择 在上述代码中设置的时随机选择这个不重要自行设置即可