TCP-IP学习笔记十:RPC架构WebService加入Zookeeper集群的管理

来源:互联网 发布:重生网络女主播 编辑:程序博客网 时间:2024/06/05 18:33

TCP-IP学习笔记十:RPC架构WebService加入Zookeeper集群的管理

标签(空格分隔): TCP-IP Zookeeper


一、前言

    了解了Zookeeper的使用场景之后,可以对RPC架构的WebService加入到Zookeeper中进行管理。实现服务器上线自动注册,进而通知客户度知道可以调度哪些服务器,同时服务器下线,通知客户端哪些服务器不可用。这一点也是利用了Zookeeper可以监控每个节点数据的变化和子节点变化的特点。    我们可已经服务器的启动注册到Zookeeper中,Zookeeper相当于一个注册中心,监控每个节点的数据和子节点的变化,监控到变化就通知到各个客户端。客户端启动从Zookeeper动态获取可用服务器列表,Zookeeper检测到变化的时候可以动态的通知客户度哪些服务器不可用或者新的服务器加入。

二、具体的代码实施

  1. 服务器端的修改
    注入服务器IP:端口号,和Znode(应用所在的路径),在服务器启动的时候注入到Zookeeper中。
    在NIOServerBootstrap的线程启动下添加如下代码:

     client=new ZkClient(servers);    //判断节点是否存在,不存在则创建    if(!client.exists(serverPath)){        client.createPersistent(serverPath,true);    }    //注册服务    String tmpPath=serverPath+"/"+ InetAddress.getLocalHost().getHostAddress()+":"+port;    //判断节点是否存在,不存在则创建    if(!client.exists(tmpPath)){        client.createEphemeral(tmpPath);    }
  2. 客户端的修改
    添加InvokeLoadBalancer类,获得可用连接和负载均衡策略。
    负载均衡策略有很多实现方式(有很多算法),这里使用简单的随机数的方式实现。

    package com.motui.rpc.client;import org.I0Itec.zkclient.IZkChildListener;import org.I0Itec.zkclient.ZkClient;import java.io.Serializable;import java.util.ArrayList;import java.util.List;import java.util.Random;/** * Created by MOTUI on 2016/10/24. * *  获取可用连接和实现负载均衡 */public class InvokeLoadBalancer implements Serializable {    //存储可用的服务列表    private static List<HostAndPort> hostAndPorts;    private ZkClient zkClient;    //监听的节点路径    private String serverPath;    public InvokeLoadBalancer(String servers,String serverPath){        this.serverPath = serverPath;        this.zkClient = new ZkClient(servers);    }    /**     * 初始化方法     */    public void init(){        //判断节点是否存在,不存在则创建        if (!zkClient.exists(serverPath)){            zkClient.createPersistent(serverPath,true);        }        //第一次启动,需要获得服务器列表        hostAndPorts = new ArrayList<HostAndPort>();        //获得服务列表        List<String> children = zkClient.getChildren(serverPath);        for (String child:children) {            hostAndPorts.add(splitChild(child));        }        //监听节点的变化        zkClient.subscribeChildChanges(serverPath, new IZkChildListener() {            @Override            public void handleChildChange(String path, List<String> children) throws Exception {                //清空服务列表集合                hostAndPorts.clear();                //获得可用服务列表                for (String child: children) {                    hostAndPorts.add(splitChild(child));                }            }        });    }    /**     * 负载均衡算法     */    public HostAndPort getHostAndPort(){        if (hostAndPorts != null && hostAndPorts.size() > 0){            HostAndPort hostAndPort = hostAndPorts.get(new Random().nextInt(hostAndPorts.size()));            return hostAndPort;        }else{            throw new RuntimeException("hostAndPorts is null");        }    }    public HostAndPort getHostAndPort(HostAndPort hostAndPort){        //将该hostAndPort从集合中删除        hostAndPorts.remove(hostAndPort);        //再此获取一个HostAndPort        HostAndPort hap = getHostAndPort();        return hap;    }    /**     * 将String的child转化为HostAndPort对象     * @param child     * @return     */    public HostAndPort splitChild(String child){        String[] split = child.split(":");        HostAndPort hostAndPort = new HostAndPort(split[0], Integer.parseInt(split[1]));        return hostAndPort;    }}

    对我们的动态代理类进行修改
    客户端接口的remoteCall方法修改(将获得的HostAndPort和获得服务器列表的InvokeLoadBalancer进行传递):

    public Object remoteCall(MethodInvokeMeta methodInvokeMeta, int retry,HostAndPort hostAndPort,InvokeLoadBalancer invokeLoadBalancer);

    动态获得可用服务器的IP和端口号

     //动态获取HostAndPort HostAndPort hostAndPort = invokeLoadBalancer.getHostAndPort(); Object result = this.client.remoteCall(invokeMeta,10,hostAndPort,invokeLoadBalancer);

    客户端实现类,在出现连接异常进行重试的操作,有一点需要注意。在连接异常的时候从新获得可用的HostAndPort进行重新连接。
    异常处理

    //再此获取HostAndPostHostAndPort hap = invokeLoadBalancer.getHostAndPort(hostAndPort);return remoteCall(methodInvokeMeta, retry,hap,invokeLoadBalancer);

    服务器端配置文件:

    <?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"       xmlns:context="http://www.springframework.org/schema/context"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xsi:schemaLocation="http://www.springframework.org/schema/beans                           http://www.springframework.org/schema/beans/spring-beans-4.1.xsd                           http://www.springframework.org/schema/context                           http://www.springframework.org/schema/context/spring-context-4.1.xsd">    <!-- 配置服务启动类 -->    <bean class="com.motui.rpc.server.NIOServerBootstrap" init-method="start" destroy-method="close">        <constructor-arg index="0" value="8989"/>        <constructor-arg index="1" ref="serverRequestResponseHandler"/>        <!--<property name="servers" value="192.168.200.128:2181"/>-->        <property name="servers" value="192.168.200.128:2181,192.168.200.128:2182,192.168.200.128:2183"/>        <property name="serverPath" value="/motui/rpc"/>    </bean>    <!-- 配置请求处理类 -->    <bean id="serverRequestResponseHandler" class="com.motui.rpc.server.ServerRequestResponseHandler"/>    <!-- 扫描组件 -->    <context:component-scan base-package="com.motui.service"/></beans>

    客户端配置文件

    <?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xsi:schemaLocation="http://www.springframework.org/schema/beans       http://www.springframework.org/schema/beans/spring-beans-4.1.xsd">    <!-- 配置代理bean -->    <bean class="com.motui.rpc.client.BeanScanner">        <property name="beanInterfaces">            <map>                <entry key="demoService" value="com.motui.service.IDemoService"/>                <entry key="userService" value="com.motui.service.IUserService"/>                <entry key="testService" value="com.motui.service.ITestService"/>            </map>        </property>        <property name="clientName" value="client"/>        <property name="invokeLoadBalancerName" value="invokeLoadBalancer"/>    </bean>    <bean id="client" class="com.motui.rpc.client.RemoteRPCClient"/>    <bean id="invokeLoadBalancer" class="com.motui.rpc.client.InvokeLoadBalancer" init-method="init">        <!--<constructor-arg name="servers" value="192.168.200.128:2181"/>-->        <constructor-arg index="0" value="192.168.200.128:2181,192.168.200.128:2182,192.168.200.128:2183"/>        <constructor-arg index="1" value="/motui/rpc"/>    </bean></beans>

    说明:本示例使用的是Zookeeper的单节点和集群环境测试。源码中有测试代码,可供测试。
    参考文章:RPC原理及RPC实例分析
    源码地址: 源码地址

0 0
原创粉丝点击