RPC

跟我一起写RPC-注册中心(二)

RPC

Posted by TBKK on June 19, 2019

为什么要有注册中心

注册中心很多时候都有听过,如何理解注册中心?在分布式系统中,倘若要找到一个服务的是哪里提供的是一件不容易的事情。在上千个节点中找到他光靠配置文件并不是一件靠谱的事情,假设是配置文件,某个节点异常挂掉了怎么办,这时候就需要用到注册中心了。

注册中心可以说是微服务架构中的”通讯录“,它记录了服务和服务地址的映射关系。在分布式架构中,服务会注册到这里,当服务需要调用其它服务时,就到这里找到服务的地址,进行调用。

为什么需要注册中心

要了解为什么需要注册中心,那我们就需要知道注册中心解决了什么问题。理解了下面这些问题就知道为什么要注册中心了。

  • 服务注册后,如何被及时发现?
  • 服务宕机后,如何及时下线?
  • 服务如何有效的水平扩展?
  • 服务发现时,如何进行路由?
  • 服务异常时,如何进行降级?
  • 注册中心如何实现自身的高可用?

常见的注册中心有哪些

  • Zookeeper(老牌注册中心,满足CP,后续详细介绍)
  • Redis(很少以Redis做注册中心,dubbo可以支持)
  • Eureka(spring cloud的注册中心,Eurkea 2.x分支不再维护,满足AP)

    市面上使用的最多,具体不细说了,各位可以baidu

  • etcd(灵感来源于Zookeeper,也是CP的)

    • 更轻量级、更易用
    • 高负载下的稳定读写
    • 数据模型的多版本并发控制
    • 稳定的watcher功能,通知订阅者监听值的变化
    • 客户端协议使用gRPC协议,支持go、C++、Java等,而Zookeeper的RPC协议是自定制的,目前只支持C和Java
    • 可以容忍脑裂现象的发生
  • Consul(K/V值存储)

    • docker 实例的注册与配置共享
    • coreos 实例的注册与配置共享
    • vitess 集群
    • SaaS 应用的配置共享
    • 与 confd 服务集成,动态生成 nginx 和 haproxy 配置文件

    • 使用 Raft 算法来保证一致性, 比复杂的 Paxos 算法更直接. 相比较而言, zookeeper 采用的是 Paxos, 而 etcd 使用的则是 Raft.
    • 支持多数据中心,内外网的服务采用不同的端口进行监听。 多数据中心集群可以避免单数据中心的单点故障,而其部署则需要考虑网络延迟, 分片等情况等. zookeeper 和 etcd 均不提供多数据中心功能的支持.
    • 支持健康检查. etcd 不提供此功能.
    • 支持 http 和 dns 协议接口. zookeeper 的集成较为复杂, etcd 只支持 http 协议.
    • 官方提供web管理界面, etcd 无此功能.
  • Nacos(Spring Cloud Eureka + Spring Cloud Config) 阿里开源的注册中心

开始我们的注册中心(zk)

  1. 准备工作,本地电脑上装个zookeeper(具体自己去baidu)
  2. 注册重要做什么事情,将其抽象成 抽象类或者接口
public abstract class RegisterCenter {

    @Setter
    private Map<String, String> registerParam = new HashMap<>();

    public void start()
    {
        start(registerParam);
    }

    /**
     * start
     */
    public abstract void start(Map<String, String> param);

    /**
     * stop
     */
    public abstract void stop();


    /**
     * register service, for mult
     *
     * @param keys      service key
     * @param value     service value/ip:port
     * @return
     */
    public abstract boolean register(Set<String> keys, String value);


    /**
     * remove service, for mult
     *
     * @param keys
     * @param value
     * @return
     */
    public abstract boolean remove(Set<String> keys, String value);

    /**
     * discovery services, for mult
     *
     * @param keys
     * @return
     */
    public abstract Map<String, TreeSet<String>> discovery(Set<String> keys);

    /**
     * discovery service, for one
     *
     * @param key   service key
     * @return      service value/ip:port
     */
    public abstract TreeSet<String> discovery(String key);

}
  1. 无论用什么注册中心,封装一下,实现这种接口就行了 基于zk作为存储介质,入口如下:
     //开启注册中心
     private void start(String zkAddress, String zkDigest, String env)
     {
        
         zkEnvPath = zkBasePath.concat("/").concat(env);
         yoyoZkClient = new YoyoZkClient(zkAddress, zkEnvPath, zkDigest, new ZkRegisterCenterWatcher(this));
         yoyoZkClient.initClient();
         refreshThread = new ZkRegisterCenterRefreshThread(this);
         refreshThread.setName("ZkRegisterCenterRefreshThread");
         refreshThread.setDaemon(true);
         refreshThread.start();
         log.info("yoyo-rpc, ZkRegisterCenter init success. [env={}]", env);
     }
    
       * ZkRegisterCenterRefreshThread:  * 服务信息异常兜底方案(开启刷新线程,定时刷新)
       * YoyoZkClient:  * zk的客户端操作封装,与服务信息的存储方式相关
       * ZkRegisterCenterWatcher:  * 远端服务信息发生了改变的一个Hook,刷新本地缓存。
    

具体代码可以在(https://github.com/tbkk/yoyo-rpc)看到

总结

这就是见到的注册中心,如果需要使用自研注册中心,实现以上接口即可。