• Zookeeper实现master-slave选举


    场景描述

       比如存在一个webservice服务 该服务用于提供 一个获取uuid的服务  这个服务调用的程序不多 但是需要考虑到单点故障 当其中一台挂掉后 另一台机器需要充当master提供服务

    实线流程

       图解:


    1.zookeeper集群 (这里模拟 我只开启了一台主机 58.1

    2. web服务器(两台服务器用于 master-salve模式)

     Web服务器就提供一个webservice服务 (jax-wx 必须jdk1.7支持)
            @WebService
    public class UniqueString {
    	@WebMethod
    	public String get(){
    		return UUID.randomUUID().toString();
    	}
    	public static void main(String[] args) throws Exception {
    		//获取当前服务器的ip
    		String curIp=getIp();
    		//发布主应用 主webservice
    		Endpoint.publish("http://"+curIp+":8801/getId", new UniqueString());
    		
    	}
    }

    web服务器(58.131,58.132都需要部署该程序)启动时 必须在zookeeper服务器上进行争抢注册/master节点

     创建为 /master=web服务器的ip】  该节点为临时节点

     假设 192.168.58.131争抢到了zookeeper上通过zkCli命令连接get /master查看/master的值就为192.168.58.131如果131挂了

     此时zookeeper131的连接中断session超时后 自动删除临时节点/master此时132监听到/master的数据删除事件 就需要开始争抢 

        master权注册/master=192.168.58.132

    两台服务 需要处理的动作为

       启动服务器 开始争抢/master注册  监听/master的删除(又要开始争抢)

       代码如下(这里使用zkclient


    package webserver;
    
    import java.net.InetAddress;
    import java.net.NetworkInterface;
    import java.net.SocketException;
    import java.util.Enumeration;
    
    import org.I0Itec.zkclient.IZkDataListener;
    import org.I0Itec.zkclient.ZkClient;
    import org.I0Itec.zkclient.exception.ZkException;
    import org.I0Itec.zkclient.exception.ZkInterruptedException;
    import org.I0Itec.zkclient.exception.ZkNodeExistsException;
    import org.apache.zookeeper.CreateMode;
    
    /**
     * 这里使用Master-Slave模式 使用一台主机(Master)作为对外提供服务的主机
     *                  其他服务器是备机(Slave ) 如果主机挂了 从机开始选举选择
     * 服务器的选举
     * 
     * /master  --   master的ip地址
     * @author jiaozi
     *
     */
    public class ServerSelector {
    	private String zookkerUrl="192.168.58.1:2181";//zookeeper的连接地址
    	private String masterNode="/master";//注册到zookeeper的节点名称   /master中存储的是服务器的ip地址
    	private String curIp;               //当前的ip地址
    	private String masterIp;            //master的ip地址
    	private ZkClient zk=null;           //zookeeper客户端
    	private IZkDataListener dataListener;//监听/master节点的数据变化
    	public ServerSelector(String curIp){
    		this.curIp=curIp;
    	}
    	public void start() throws Exception{
    		//连接zookeeper 设置5秒连接超时 
    		//session超时时间越长 假如服务器挂了 等待session超时时间 临时节点才删除
    		zk=new ZkClient(zookkerUrl,3000, 1000);
    		dataListener=new IZkDataListener() {
    			/**
    			 * 如果数据被删除了 可能有master挂了 此时必须争抢master
    			 */
    			@Override
    			public void handleDataDeleted(String arg0) throws Exception {
    				fightMaster();
    			}
    			//数据被修改了
    			@Override
    			public void handleDataChange(String arg0, Object arg1) throws Exception {}
    		};
    		//订阅master节点的数据修改节点
    		zk.subscribeDataChanges(masterNode,dataListener);
    		fightMaster();
    	}
    	/**
    	 * 当前主机争夺master
    	 */
    	public void fightMaster(){
    		//尝试去创建master节点  有可能别的服务器抢到了 可能会出现异常
    		try {
    			Object mastIp=zk.create(masterNode,curIp,CreateMode.EPHEMERAL);
    		} catch (Exception e) {
    			//节点已存在
    			if(e instanceof ZkNodeExistsException){
    				//由于不同的原因(处理过程 master挂了)导致没取到  重新争抢 直到找到master
    				Object masterIpObj=zk.readData(masterNode);
    				if(masterIpObj==null){
    					fightMaster();
    				}else{
    					masterIp=masterIpObj.toString();
    				}
    			}
    		} 
    		
    	}
    	
    	
    }

    修改web服务器启动的代码 启动服务后 开始选举


    package webserver;
    
    import java.net.InetAddress;
    import java.net.NetworkInterface;
    import java.net.SocketException;
    import java.util.Enumeration;
    import java.util.UUID;
    
    import javax.jws.WebMethod;
    import javax.jws.WebService;
    import javax.xml.ws.Endpoint;
    
    @WebService
    public class UniqueString {
    	@WebMethod
    	public String get(){
    		return UUID.randomUUID().toString();
    	}
    	//zookeeper选举类
    	static ServerSelector ss=null;
    	public static void main(String[] args) throws Exception {
    		//获取当前服务器的ip
    		String curIp=getIp();
    		//发布主应用 主webservice
    		Endpoint.publish("http://"+curIp+":8801/getId", new UniqueString());
    		//开始选举进程 传入当前ip
    		ss=new ServerSelector(curIp);
    		ss.start();
    	}
    	/**
    	 * 获取当前主机ip地址
    	 * 这里我挂的虚拟机 和window主机 ip都是58段
    	 * @return
    	 * @throws SocketException
    	 */
    	public static String getIp() throws SocketException{
    		Enumeration<NetworkInterface> interfs = NetworkInterface.getNetworkInterfaces();  
            while (interfs.hasMoreElements())  
            {  
                NetworkInterface interf = interfs.nextElement();  
                Enumeration<InetAddress> addres = interf.getInetAddresses();  
                while (addres.hasMoreElements())  
                {  
                    InetAddress in = addres.nextElement();  
                    if (in.getHostAddress().startsWith("192.168.58"))  
                    {  
                        return in.getHostAddress(); 
                    }  
                }  
            }  
            return null;
    	}
    }
    服务器代码编写完成 后  eclipse导出为jar包  选择mainclass为UniqueString

    将需要用到的jar包 放在lib目录下 


    上传到 58.131和58.132后 使用命令

      nohup java -Djava.ext.dirs=./lib -jar webserver.jar  查看目录下的nohup.out文件查看是否启动 

      也可以通过  ps-ef | grep java 查看启动的java程序 成功后 通过客户端

      zkCli.sh -server 192.168.58.1 查看 /master节点的值  

       

    此时 看到 /master=58.132  

    登录 58.132 关闭 服务 ps -ef | grep java 找到进程 kill 进程编号 

    通过客户端查看是否  /master=58.131 如果是 则表示服务器master-slave实现成功


    2. 客户端(客户端需要调用webservice 

    客户端调用之前 获取/master节点的服务器 通过服务器ip调用webservice

    同时需要监听zookeeper节点的数据变化事件 需要重新替换新的服务器ip

    这里调用webservice的代码(图中选中的)是通过eclipse上新建webservice client生成的代码


    package webclient;
    
    import java.rmi.RemoteException;
    
    import javax.xml.rpc.ServiceException;
    
    import org.I0Itec.zkclient.IZkDataListener;
    import org.I0Itec.zkclient.ZkClient;
    import org.I0Itec.zkclient.exception.ZkNoNodeException;
    import org.apache.zookeeper.ZooKeeper;
    /**
     * 客户端模拟根据zookeeper获取到master
     *   调用webservice获取id
     * @author jiaozi
     *
     */
    public class Test {
    	static String masterNode="/master";//master节点
    	static String masterIp=null;// 获取master的ip地址
    	static ZkClient zk=null;
    	static String zookkerUrl="192.168.58.1:2181";//zookeeper的连接地址
    	/**
    	 * 获取master的ip
    	 * @return
    	 * @throws InterruptedException
    	 */
    	public static String getMasterIp() throws InterruptedException{
    		Object ipObj;
    		try {
    			//读取master节点的ip值
    			ipObj = zk.readData(masterNode);
    		} catch (ZkNoNodeException e) {
    			//如果节点不存在 休眠10s 继续读取 直到读取到为止
    			Thread.sleep(10);
    			return getMasterIp();
    		}
    		//如果没有获取到ip 也继续去读取
    		if(ipObj==null){
    			Thread.sleep(10);
    			return getMasterIp();
    		}
    		return ipObj.toString();
    	}
    	public static void main(String[] args) throws Exception { 
    		zk=new ZkClient(zookkerUrl,3000, 1000);
    		//客户端调用时获取一次master的ip 以后就使用该ip缓存 监听zookeeper的数据变化
    		System.out.println("尝试获取master");
    		masterIp=getMasterIp();
    		//监听master节点数据的变化
    		zk.subscribeDataChanges(masterNode, new IZkDataListener() {
    			@Override
    			public void handleDataDeleted(String arg0) throws Exception {
    			}
    			
    			@Override
    			public void handleDataChange(String arg0, Object arg1) throws Exception {
    				masterIp=getMasterIp();
    			}
    		});
    		//每隔5s 循环调用webservice  看down掉其中任何一台服务器是否都可以获取到存在的服务器 并且连接webservice
    		while(true){
    			System.out.println("获取到的masterip是:"+masterIp);
    			String wsdlUrl="http://"+masterIp+":8801/getId";
    			
    			try {
    				UniqueStringServiceLocator u=new UniqueStringServiceLocator();
    				u.setUniqueStringPortEndpointAddress(wsdlUrl);
    				String id=u.getUniqueStringPort().get();
    				System.out.println(id);
    			} catch (Exception e) {
    				System.out.println("webservice无法连接");
    			}
    			Thread.sleep(5000);
    		}
    	}
    
    }

    这里有时还需要考虑个问题 就是  服务器  58.131和58.132中的master和服务器之间出现了网络的不稳定 此时和服务器之间因为超时 导致 master节点从zookeeper服务干掉了  但是 master对应的服务器 并没有真正挂掉  有可能一些对象的初始化在新服务器需要重新 处理 需要消耗资源  所以 可以让之前的master优先去抢  让之前不是master的节点 过几秒后再去抢  红色的部分为修改代码

    package webserver;
    
    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;
    
    import org.I0Itec.zkclient.IZkDataListener;
    import org.I0Itec.zkclient.ZkClient;
    import org.I0Itec.zkclient.exception.ZkNodeExistsException;
    import org.apache.zookeeper.CreateMode;
    
    /**
     * 这里使用Master-Slave模式 使用一台主机(Master)作为对外提供服务的主机
     *                  其他服务器是备机(Slave ) 如果主机挂了 从机开始选举选择
     * 服务器的选举
     * 
     * /master  --   master的ip地址
     * @author jiaozi
     *
     */
    public class ServerSelector {
    	private String zookkerUrl="192.168.58.1:2181";//zookeeper的连接地址
    	private String masterNode="/master";//注册到zookeeper的节点名称   /master中存储的是服务器的ip地址
    	private String curIp;               //当前的ip地址
    	private String masterIp;            //master的ip地址
    	private ZkClient zk=null;           //zookeeper客户端
    	private IZkDataListener dataListener;//监听/master节点的数据变化
    	public ServerSelector(String curIp){
    		this.curIp=curIp;
    	}
    	public void start() throws Exception{
    		//连接zookeeper 设置5秒连接超时 
    		//session超时时间越长 假如服务器挂了 等待session超时时间 临时节点才删除
    		zk=new ZkClient(zookkerUrl,3000, 1000);
    		dataListener=new IZkDataListener() {
    			/**
    			 * 如果数据被删除了 可能有master挂了 此时必须争抢master
    			 */
    			@Override
    			public void handleDataDeleted(String arg0) throws Exception {
    				ScheduledExecutorService ses=Executors.newScheduledThreadPool(1);//这里线程池建议定义在全局属性 我这里为了方便标色
    				//如果当前ip和之前的masterip是一样的直接开抢 
    				if(curIp.equals(masterIp)){
    					fightMaster();
    				}else{
    					//ip和master不一样 等待5秒后开抢 让之前的ip先抢 因为master没挂
    					ses.schedule(new Runnable() {
    						
    						@Override
    						public void run() {
    							fightMaster();
    						}
    					}, 5, TimeUnit.SECONDS);
    				}
    			}
    			//数据被修改了
    			@Override
    			public void handleDataChange(String arg0, Object arg1) throws Exception {}
    		};
    		//订阅master节点的数据修改节点
    		zk.subscribeDataChanges(masterNode,dataListener);
    		fightMaster();
    	}
    	/**
    	 * 当前主机争夺master
    	 */
    	public void fightMaster(){
    		//尝试去创建master节点  有可能别的服务器抢到了 可能会出现异常
    		try {
    			Object mastIp=zk.create(masterNode,curIp,CreateMode.EPHEMERAL);
    		} catch (Exception e) {
    			//节点已存在
    			if(e instanceof ZkNodeExistsException){
    				//由于不同的原因(处理过程 master挂了)导致没取到  重新争抢 直到找到master
    				Object masterIpObj=zk.readData(masterNode);
    				if(masterIpObj==null){
    					fightMaster();
    				}else{
    					masterIp=masterIpObj.toString();
    				}
    			}
    		} 
    		
    	}
    	
    	
    }
    

    最终运行结果:

     启动 58.131和58.132后 运行客户端 main  运行一会后停掉 master 58.132进程  发现自动开始调用 58.131的web服务了

     获取到的masterip是:192.168.58.132
    eb5743c3-d7f0-4265-97e4-05d21531b062
    获取到的masterip是:192.168.58.132
    843638ae-e7d1-43e3-ba3f-dab2916c7e77
    获取到的masterip是:192.168.58.132
    webservice无法连接
    获取到的masterip是:192.168.58.131
    7ee85b77-83f6-430e-8df3-cfd785e3bea1
    获取到的masterip是:192.168.58.131
    8fb278d1-0b6f-4b35-bbeb-1fa66e145c6c



  • 相关阅读:
    (转) tcp的注册端口
    [转] Android中C&C++源码库的初步研究
    (转)vim7.3中文乱码解决方法
    {转} Eclipse 高亮显示选中的相同变量
    libcurl 一个实现了client请求http,ftp的库
    c#操作文件夹
    OutputCache祥解
    非静态的字段、方法或属性“System.Web.UI.Page.ClientScript.get”要求对象引用
    IXMLDOMDocument 成員
    关于中日文和UNICODE之间编码的转换(2008725 15:05:00)
  • 原文地址:https://www.cnblogs.com/liaomin416100569/p/9331221.html
Copyright © 2020-2023  润新知