首页 > 技术文章 > Spring生态系列文章 >

zookeeper应用:分布式服务器感知

更新时间:2018-08-19 | 阅读量(459)

现在我们项目开发一般会使用maven的聚合工程,把项目的service层和web层进行分离,然后打包为两个应用部署分别部署到服务器,这里我们就以一个订单模块为例,假设我们打包的应用名称分别叫 orderServer.jar(service层业务)和orderClient.war(web层处理逻辑),这样他们的部署应该是这样的: ![服务器之间RPC调用01.png](https://upload-images.jianshu.io/upload_images/8090675-8c9927ba447991c8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 对于上面这个简单的一个RPC调用过程,那么我们的orderClient这个应用肯定是需要知道我们的orderServer.jar是部署在哪个服务器上的,这样我们才可以通过具体的主机地址+端口号找到对应的应用程序调用并且返回结果,所以在我们的orderClient应该是有一个**配置或者说是在程序启动的时候传递orderServer的相关信息**去找到我们对应的OrderServer.jar这个应用 ​ 但是系统业务的增长以及对系统的可靠性的要求,我们会面临下面两个问题 - 请求数量增多,现在一个orderServer.jar已经不能支撑业务系统的要求 - 如果服务器A或者说应用OrderServer.jar程序内存溢出,整个系统都会出现无法访问的情况 为了解决上面两个问题,我们需要使用分布式应用的部署模式,即对于orderServer.jar和orderClient.war都需要在多个服务器上部署应用,那么这个时候我们的应用部署应该是这样的: ![服务器之间RPC调用02.png](https://upload-images.jianshu.io/upload_images/8090675-46c658b0d528abec.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 对于上述这个部署环境来说,对于所有的orderClient.war的应用程序,我们需要获取到orderServer.jar的相关信息(主机地址和端口)不能再写死,我们需要动态的去获取所有的服务端的应用程序列表 - 当我们在增加orderServer.jar的部署机器的时候,对于orderClient可以实时的添加新增加的orderServer.jar的地址信息 - 当我们某一个服务器,比如说服务器A宕机的时候,不能再提供服务的情况下,我们的orderClient可以实时的获取到相关信息,在orderClient.war的程序中及时的剔除服务器A的地址信息 如果要实现对于客户端orderClient.war实时的获取orderServer.jar的地址信息,我们可以使用zookeeper完成我们这个简单的需求,其中主要的流程图如下 ![服务器之间RPC调用04.png](https://upload-images.jianshu.io/upload_images/8090675-e2a2ad03017547af.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 如果要实现上面流程图的这个效果,我们在orderServer.jar和orderClient.war的程序中需要做如下的操作 orderServer.jar - 创建一个zkClient 连接到zookeeper集群 - 启动orderServer.jar的时候往zookeeper集群中的/Servers目录下创建**序列的临时节点** - 在创建节点的时候设置值为当前服务器的地址信息(主机名:端口) orderClient.war - 创建一个zkClient连接到zookeeper集群 - 启动orderClient.war的时候,需要获取/Servers目录下面所有的节点信息 - 在创建zkClient的时候绑定一个监听NodeChildrenChanged类型的事件,当/Servers目录下的节点信息发生改变的时候,可以及时的做出响应,获取最新的地址列表 **下面是具体的实现代码,如果可以比较好的理解这些内容,说明对于zookeeper的应用基本上已经掌握** 服务端 OrderServerRegister ```java package cn.wolfcode.zookeeper.server; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; //服务器启动注册 public class OrderServerRegister { //zkServer的连接地址信息,多个连接地址之间使用逗号隔开 private String connectStr="lab171:2181,lab172:2181,lab173:2181"; //客户端连接zkServer的超时时间 private int sessionTimeOut=2000; //zk客户端 实例对象,用户连接zk集群 private ZooKeeper client=null; //指定服务器地址信息存放的目录 private String serverAddr="/servers"; //指定节点名称,因为使用的是EPHEMERAL_SEQUENTIAL,所以不会存在节点重复的情况 //并且在断开连接后,该节点会自动删除 private String serverName="orderServer"; public OrderServerRegister() throws Exception { //实例化zk客户端 client=new ZooKeeper(connectStr,sessionTimeOut,null); //判断存放的地址是否存在,如果不存在,创建一个对应的目录 Stat stat = client.exists(serverAddr,null); if(stat ==null){ client.create(serverAddr,"".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT); } } // 注册地址信息的方法 public void register(String host,String port) throws Exception{ //创建一个临时的序列节点 client.create(serverAddr+"/"+serverName,(host+":"+port).getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL); } public static void main(String[] args) throws Exception { //设置ip地址和端口 通过启动参数传递进来 String host=args[0]; String port=args[1]; OrderServerRegister server = new OrderServerRegister(); server.register(host,port); System.out.println("服务端"+host+":"+port+"启动成功"); //模拟服务程序一直处于运行状态 Thread.sleep(Long.MAX_VALUE); } } ``` 客户端 OrderClientDiscovery ```java package cn.wolfcode.zookeeper.server; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; import java.util.ArrayList; import java.util.List; //客户端发现服务端信息 public class OrderClientDiscovery { //zkServer的连接地址信息 private String connectStr="lab171:2181,lab172:2181,lab173:2181"; //客户端连接zkServer的超时时间 private int sessionTimeOut=2000; //zk客户端 private ZooKeeper client=null; //服务器信息存放地址 private String serverAddr="/servers"; //存放服务器信息地址列表的信息 private List orderServerList=new ArrayList<>(); public OrderClientDiscovery() throws Exception { //实例化zk客户端,这里需要指定事件监听 client=new ZooKeeper(connectStr, sessionTimeOut, new Watcher() { @Override public void process(WatchedEvent event) { //连接成功状态 if(event.getState()==Event.KeeperState.SyncConnected){ //对于指定路径的事件监听 if(serverAddr.equals(event.getPath())){ //监听子节点改变状态,子节点的数量增加或者减少 if(event.getType()==Event.EventType.NodeChildrenChanged){ try { //调用业务处理方法 getOrderServerList(); } catch (Exception e) { e.printStackTrace(); } } } } } }); } public void getOrderServerList() throws Exception{ //获取到所有的子节点列表, 其中true是保持监听状态,使用创建zkClient的默认事件监听程序 List nodeList = client.getChildren(serverAddr,true); //临时列表,用于存放对应的服务器信息 List temp=new ArrayList<>(); for (String node : nodeList) { //获取节点的数据, 地址+端口 byte[] data = client.getData(serverAddr+"/"+node,null,null); temp.add(new String(data)); } //更新成员变量服务器列表信息 orderServerList=temp; System.out.println("更新后的服务器列表为:"+orderServerList); } public static void main(String[] args) throws Exception { OrderClientDiscovery orderClient = new OrderClientDiscovery(); orderClient.getOrderServerList(); System.out.println("客户端启动完成,随时监听服务器列表的改变信息"); Thread.sleep(Long.MAX_VALUE); } } ``` 完整代码下载地址:https://gitee.com/heshengjun/demo_zookeeper001.git
叩丁狼学员采访 叩丁狼学员采访
叩丁狼头条 叩丁狼头条
叩丁狼在线课程 叩丁狼在线课程