apache-shenyu之注册中心整合与元数据同步

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了apache-shenyu之注册中心整合与元数据同步相关的知识,希望对你有一定的参考价值。

参考技术A ####### 当然通过 shenyu的disruptor应用
了解到所有数据同步,实例以及元数据注册的发送,接收都使用了disruptor解耦并提高了可用性。

shenyu-register-center 作为注册中心模块,主要有三个模块需要了解

入口为ShenyuClientCommonBeanConfiguration类。通过spring.factories指定,然后初始化一个注册中心策略
ShenyuClientRegisterRepositoryFactory

可以看到我们的业务服务只要引入了apache-shenyu的pom文件,就会通过spring-boot自动注入一个ShenyuClientRegisterRepository通过如下配置类

我们只看nacos的实现

先来看位于shenyu-admin模块中的配置类

然后来看NacosClientServerRegisterRepository

上面关于业务服务的实例信息,接口元数据如何同步,通过查看其中一个nacos的实现,看到了业务服务如何通过shenyu-client如果注册到nacos,而shenyu-admin如何从nacos接收到数据

如果使用spring-boot,我们引入了对应的jar包,这里实际直接通过spring.factories & 配置方式注入

可以看到和之前client的区别,client的注入是根据配置针对性注入一个实现(当然其实也可以支持多实现,但逻辑上没有必要)也就是通过@Bean注入一个接口的实现,而数据同步的注入是分别在自己的模块通过 spring.factories & 配置注入,通过配置是可以同时存在多个数据同步方式的。

上述这些数据需要同步,如果分别是这些interface的方法入口,上述接口是抽象出来给shenyu-bootstrap使用,实现都是通过apache-shenyu的插件架构实现,而数据变化的生产者是通过DataChangedListener&DataChangedInit接口抽象在admin端进行发送,而bootstrap通过它进行订阅感知数据变化后的操作。而这些数据变化的消费者在对应数据同步的子类模块中如下

DataChangedInit 是在服务启动后初始化逻辑,没有websocket的子实现,其实就是在服务启动后的一个钩子,因为nacos,consul,etcd,zookeeper相当于都需要一个中间件服务来同步数据,需要每次启动会进行一次同步,而websocket则是直连,互相能感知到上下线,可以直接在上下线中处理

而所有数据变化的逻辑是通过spring提供的观察者机制,通过ApplicationEvent发布事件,由事件监听者来同步数据
分发事件的类为DataChangedEventDispatcher类

然后我们来看websocket关于DataChangedListener的实现
全部委托给WebsocketCollector处理

我们来看WebsocketCollector,它也是一个websocket客户端类

整合zk作为注册中心,实现更合理的rpc服务注册

前言

昨天我们对手写的rpc框架进行了整理优化,解决了代理类和sevice的绑定问题,自此之后我们就无需再进行手动绑定,确实方便了很多。

今天,我考虑把我们的rpc框架服务注册中心改为zk,至于为什么要改,原因很简单,redis严格来说属于数据库,对于多节点数据存放确实不方便也不够灵活,但是zk就是专门用来存储多节点数据的树形结构,不仅可以存放k-v这样的数据,同时还有父子节点这样的数据关系,确实比redis更灵活。

所以,我考虑把注册中心替换成zk,当然另外一个原因是,我也想更深入的了解下zk,而不是仅仅停留在会用的层面。之前一直有用zk作为服务注册中心,但也一直停留在使用层面,对于其内部原理一直是一知半解,直到今天真正用注册我们之前手写的rpc服务,踩了好多坑,才发现zk方面确实该好好补课了。

整合zk

引入客户端依赖

开始之前,我们先要引入zk的客户端,我引入的是一个第三方包,这个客户端是基于官方的客户端开发的,也算比较主流。

<dependency>
    <groupId>com.101tec</groupId>
    <artifactId>zkclient</artifactId>
    <version>0.11</version>
</dependency>

本来是打算直接使用官方客户端的,但是在使用的时候,发现确实不太好用,考虑到时间,最后选择了这个第三方客户端。后面有时间的话,考虑自己写一个zk客户端,目前考虑的是基于官方的客户端实现,这也算是研究学习zk的一种方式。

编写zk客户端工具类

然后编写zk工具类,目前的工具类还比较简单,而且有一些问题,比如节点创建不够灵活,目前都是写死的,只能创建三层目录的节点,例如/syske-rpc-server/io.github.syske.rpc.facade.HelloService/consumer,一部分原因是目前我对这个客户端了解的也比较少,所以也就用了一些简单的接口方法。

后面需要再优化下,考虑通过递归实现多层目录自动创建节点。

public class ZooKeeperUtil {
    private static final String ZK_ADDRESS = "127.0.0.1:2181";

    private static final String ZNODE = "/syske-rpc-server";

    private static ZkClient client;

    static {
        client =  new ZkClient(ZK_ADDRESS);
    }

    public static void writeData(String path, String data) {
        if (!client.exists(ZNODE)){
            //创建持久化节点 ,初始化数据
            String[] paths = path.split("/");
            client.createPersistent(ZNODE, "/" + paths[1]);
            client.createPersistent(ZNODE + "/" + paths[1], "/" + paths[2]);
            String chlid = client.create(ZNODE + path, data, CreateMode.PERSISTENT);
            System.out.println(chlid);
        }else {
            //修改节点数据,并返回该节点的状态
            String[] paths = path.split("/");
            client.createPersistent(ZNODE + path);
            Stat znodeStat = client.writeDataReturnStat(ZNODE + path, data, -1);
            System.out.println(znodeStat);
        }
    }

    public static <T> readData(String path) {
        //获取节点数据
        return client.readData(ZNODE + path);
    }
}

服务注册

编写服务注册工具类,这个工具类主要是用于服务注册和获取服务注册信息。

public class ServiceRegisterUtil {

    public static void registerProvider(RpcRegisterEntity entity) {
        ZooKeeperUtil.writeData(String.format("/%s/provider", entity.getServiceFullName()) , JSON.toJSONString(entity));
    }

    public static void registerConsumer(RpcRegisterEntity entity) {
        ZooKeeperUtil.writeData(String.format("/%s/consumer", entity.getServiceFullName()), JSON.toJSONString(entity));
    }

    public static <T> getProviderData(String path) {
        return ZooKeeperUtil.readData(String.format("/%s/provider", path));
    }

    public static <T> getConsumererData(String path) {
        return ZooKeeperUtil.readData(String.format("/%s/consumner", path));
    }
}

优化注册方式

然后把我们之前用redis注册代码改成我们刚写的工具类即可:

服务提供者

替换注册方法

整合zk作为注册中心,实现更合理的rpc服务注册
服务消费者

替换注册方法

整合zk作为注册中心,实现更合理的rpc服务注册

同时还有我们动态代理远程调用部分的代码,这里只需要替换获取方法即可:

整合zk作为注册中心,实现更合理的rpc服务注册

测试

然后我们分别启动服务提供者和服务消费者,看下效果,首先看下zookeeper服务注册情况:

整合zk作为注册中心,实现更合理的rpc服务注册

看了上面的注册结构是不是很熟悉呢?dubbo的服务注册不就长这样吗?

然后我们启动消费者端远程调用下看下:

先从zk中获取了服务的注册信息,然后远程调用,结果正常返回,是不是很简单呢?

总结

基于zk的服务注册,我们已经完整地实现了,虽然有很多地方都需要进一步的优化,但是整体流程是ok的,而且实际实现过程中,确实也发现了,对于zk我以前是真的只会用,数据结构都没好好研究过,后面得好好补课了。

另外,截至目前,除了数据结构方面的优势,我并没有发现zk作为服务注册中心的其他的优势,当然也有可能是我对zk了解的不够深入,后面我会通过手写zk客户端来弥补这一块知识的不足。

https://github.com/Syske/syske-rpc-server
- END -