(35)分布式应用系统服务器上下线动态感知程序开发

实现思路:

一个应用系统中,对外提供服务的服务器有多台,而服务器的数量动态地变化。

客户端每次只能请求一个服务器,因此服务器的变化(上下线)必须通知到客户端,客户端必须知道当前哪些服务器在,哪些服务器不在。

实现方法:

利用zookeeper集群。

服务器方面:服务器启动时到zookeeper上去注册,注册的节点必须为临时节点,因为产生临时节点的客户端一旦断开连接就会被zookeeper自动删除,进而产生事件被客户端感知。

客户端方面:客户端启动后,调用getChildren('/servers/', watch) 获取有哪些机器在线,选择连接数最小的服务器进行连接(做到负载均衡),并且注册监听,一旦有服务器宕机或上线,就会通知到客户端,客户端调用process()函数进行响应(重新获取服务器列表并注册监听)。

具体代码:

1.分布式系统的服务器

import org.apache.zookeeper.ZooKeeper;

public class DistributedServer{

       private static final String connectString="192.168.179.200:2181,192.168.179.201:2181,192.168.179.202:2181";

       private static final int sessionTimeout = 2000;

       private ZooKeeper zk = null;

       private static final String parentNode = "/servers";

       // 创建到zk的客户端连接

       public void getConnect() throws Exception{

               

                     zk = new ZooKeeper(connectString,sessionTimeout,new Watcher() {

                     @Override
                     public void process(WatchedEvent event) {

                            // 收到事件通知后的回调函数(事件处理逻辑)
                           System.out.println(event.getType() + "---" + event.getPath());

                           try {

                                zk.getChildren("/", true);    //再次绑定监听器,因为监听器只生效一次。这样做可以实现永久的监听
                           } catch (Exception e) {
                           }


                     }

              });

       } 

       // 向zk集群注册服务器信息

       public void registerServer(String hostname) throws Exception{

              String create = zk.create(parentNode + "/server",  hostname.getBytes() , Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);

              System.out.println(hostname + "is online.." + create);

       }

       // 业务功能

       public void handleBusiness() throws InterruptedException{

             Thread.sleep(Long.MAX_VALUE);

       }

       public static void main(String[] args) throws Exception{

              // 获取zk连接

              DistributedServer server = new DistributedServer();

              server.getConnect();

             // 利用zk连接注册服务器信息

      server.registerServer(args[0]);

             // 启动业务功能

             server.handleBusiness();

       }

}

 2.分布式系统的客户端

public class DistributedClient{  

       private static final String connectString="192.168.179.200:2181,192.168.179.201:2181,192.168.179.202:2181";

       private static final int sessionTimeout = 2000;

       private volatile List<String> serverList;

       private ZooKeeper zk = null;

       private static final String parentNode = "/servers";

       // 创建到zk的客户端连接

       public void getConnect() throws Exception{

               

                     zk = new ZooKeeper(connectString,sessionTimeout,new Watcher() {

                     @Override
                     public void process(WatchedEvent event) {

                            // 收到事件通知后的回调函数(事件处理逻辑)

                           try {

                                 getServerList();   // 更新服务器列表,并且注册了监听
                           } catch (Exception e) {
                           }


                     }

              });

       }  

       // 获取服务器信息列表

       public void getServerList() throws Exception{

              //获取服务器子节点信息,并对父节点进行监听

              List<String> children = zk.getChildren(parentNode, true);

              //先创建一个局部的list来存服务器信息

              List<String> servers = new ArrayList<String>();

              for(String child:children){

                     byte[] data = zk.getData(parentNode+"/"+child, false, null);

                     servers.add(new String(data));

              }

              // 把servers赋值给成员变量serverList,提供给各业务线程使用

              serveList = servers;

       }

     

       // 业务功能

       public void handleBusiness() throws InterruptedException{

             Thread.sleep(Long.MAX_VALUE);

       }

       public static void main(String[] args) throws Exception{

              // 获取zk连接

              DistributedClient client = new DistributedClient();

               client.getConnect();

              // 获取servers的子节点信息(并监听),从中获取服务器信息列表

               client.getServerList();

              // 业务线程启动

               client.handleBusiness();

       }

}

补充:volatile关键字

对象serverList在java的堆内存中。

java程序中有多个线程,每个线程有自己的工作栈空间,若多个线程都要对serverList对象进行修改操作,它们在使用对象serverList时,会对serverList中自己要操作的数据拷贝一个副本到自己的工作栈中,对副本进行相应操作,再把更改后的副本同步到堆内存的serverList对象上。这样可能会导致各个线程所看到的serverList对象是不同的。

如果给对象添加了volatile关键字,则对象不会再被拷贝到线程的工作栈中了,所有线程访问的都是该对象本身,且一个线程对对象操作完成之后才允许下一个线程进行操作。因此每个线程所观察到的serverList对象是同一个版本,这样就保证了该对象在所有线程上的一致性。

效果测试:

1.将DistributedServer.java和DistributedClient.java打成jar包:

找到以上文件所在的包,右击 - Export - Runnable JAR file - Launch configuration(DistributedServer/DistributedClient) - Export destination(C:server.jar) - finish

2.运行jar包:

进入jar包所在的目录 , 运行命令  java -jar server.jar (main方法的参数)

原文地址:https://www.cnblogs.com/paradis/p/11397780.html