手记

zk实战--rpc框架集群化

在看此篇内容时需要浏览下面内容
netty实战--手写rpc框架

前文功能简介以及功能扩充

利用netty来实现一个点对点的rpc调用。客户端和服务端都是靠手写地址进行socket同学的,无法1对多,也无法把服务拆分到不同的机器上进行压力分摊,做不到调用的时候水平扩展。现实的场景是机器单台作战能力比较差劲,但是机器多,可以把不同的运算交给不同的机器来做,这样达到机器的利用。所以有了如下的改造。

  1. 支持服务水平扩展。
  2. 支持不同的服务分布在不同的机器上。

这时候就需要引入zookeeper来做管理。

主体思路
  1. 上线的数据都写入到临时节点里
  2. 通过临时节点来确保,服务器停止之后,节点消失
  3. 自动发现服务的功能,依靠zk的Watcher来发现服务的上线和下线功能。
数据组织形式
zk数据
   /xp
    |------ip:port---services
    |    
    |------ip:port---services
    |       
    |------ip:port---services

zk的数据按照如上情况进行组织,xp为一个永久节点,下面的ip:port组成一个临时节点。分别代表服务的ip和port,当服务停止或者意外被杀以后,对应的ip:host就会消失。

xp是一个永久节点,我们对他进行watch,只要有新的服务启动或者停止都会收到事件。

client维护的结构
|-service---client
|
|-service---client

client维护的结构是service和client连接的对应,一般情况下都是一个server里有多个服务,水平扩展的时候启动多个server即可。所以组织的时候是按照service组织方便查找。

代码实现要点

客户端对永久节点进行watch,这里加入了一个callback,当发生了节点变化的时候重新更新消息。

    public List<String> getInofAndWatcher(final String path, final InfoCallBack callBack) throws Exception {
        List<String> nodeList = zk.getChildren(path, new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                if (event.getType() == Event.EventType.NodeChildrenChanged) {
                    try {
                        List<String> nodeList = zk.getChildren(path, false);
                        callBack.getLastList(nodeList);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                }
            }
        });
        return nodeList;
    }

发布到zk的信息,就是ip,port,service。

public class ServiceInfo {
    private String ip;
    private int port;
    private List<String> interfaces;
}

会把这些数据通过json格式写入到zk,这里需要写明文,方便查看zk下有哪些服务

    public void addService(ServiceInfo info) {
        zkUtil.create(CommonContext.PATH + "/" + info.toString(), JSONObject.toJSONString(info).getBytes());

    }

客户端在启动或者事件发生的时候进行service更替。

    public void getLastList(List<String> lists) {
        Map<String, Set<Client>> serviceTmp = new HashMap<>();
        if (lists != null && !lists.isEmpty()) {
            for (String node : lists) {
                String info;
                try {
                    info = new String(zkUtil.getData(CommonContext.PATH + "/" + node));
                    ServiceInfo parse = JSONObject.parseObject(info, ServiceInfo.class);
                    String address = parse.toString();
                    List<String> interfaces = parse.getInterfaces();
                    if (interfaces != null && !interfaces.isEmpty()) {
                        for (String service : interfaces) {
                            Set<Client> set = serviceTmp.get(service);
                            if (set == null) {
                                set = new HashSet<Client>();
                            }
                            if (!set.contains(address)) {
                                Client client = new Client();
                                client.connect(parse.getIp(), parse.getPort());
                                set.add(client);
                            }
                            serviceTmp.put(service, set);

                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }

        }

        services = serviceTmp;

    }

数据发送的时候选择随机找服务,这样不至于压力都在一个服务上。

    public static void send(RpcRequest request, String className) {
        Set<Client> set = services.get(className);
        if (set != null && !set.isEmpty()) {
            Client[] array = set.toArray(new Client[0]);
            //random 
            Client client = array[new Random().nextInt(array.length)];
            client.write(request);
        }
    }
测试使用

客户端

        new Observer("127.0.0.1:2181");
        ITest proxy = ProxyInterface.getProxy(ITest.class);
        String message = proxy.getMessage();
        System.out.println(message);

服务端

        ITest test= new TestImpl(); 
        Invoker.put(test);
        List<String> list = new ArrayList<String>(Invoker.getServices());
        ServiceInfo info = new ServiceInfo("127.0.0.1", 6161, list);
        Publisher pub = new Publisher("127.0.0.1:2181");
        pub.addService(info);
        Server.bind(6161);
总结

利用zk的注册功能,让服务互相发现,并且做到水平扩展。重点就是一个事件机制和临时节点的机制。代码如下
https://github.com/xpbob/lightrpc

原创首发于慕课网

5人推荐
随时随地看视频
慕课网APP