gRPC 注册中心,常用的注册中心你懂了?AP 还是 CP (七)

看完有帮助的话,大家点赞,关注,收藏,作者需要你们的鼓励哦

gRPC是一个跨语言的微服务框架,但gRPC本身不支持微服务框架生态圈的一些功能,比如注册中心,限流,熔断等,今天我们就看看如何利用gRPC提供的接口实现简单的注册中心,文章将介绍什么是注册中心、注册中心方案,常用的注册中心实现方式,优劣,以及为gRPC-go实现一个注册中心

什么是注册中心

注册中心是微服务框架的一个标配,其核心作用是解耦服务提供者和服务消费者,服务提供者可以往注册中心注册自己提供了那些服务,服务消费者可以根据服务名称查询注册中心有哪些服务提供者,从而解耦了消费者和提供者之间互相通信协调,当一个服务有多个提供者时,消费者该选择哪个呢?这是注册中心还有一个作用负载均衡,注册中心会根据某种算法(随机,轮询,权重,流量)选择一个合适服务提供者供消费者使用,那万一这个服务提供者死亡了呢?这就又引出心跳检测概念了,意思是服务提供者和注册中心之间会定期通信(几十毫秒一次,几秒一次)检测服务提供者是否存活,注册中心会定期移除没有往注册中心检测心跳的服务提供者,以此保证给消费方提供的服务提供者是有效的。

注册中心的两种方案

集中式LB(Load Balance),也是代理模型(Proxy Model)

优点:这种方式比如nginx,F5等,本身就具备负载均衡算法,心跳检测,失败重试,失败转移等功能,不需要开发人员再次开发,服务提供者往注册中心注册自己的ip地址,消费者访问注册中心,注册中心通过算法处理后转发到服务提供者之后返回处理结果,注册中心再把处理结果返回给消费者,
缺点:比如,不支持动态的新增服务提供者,并发量大以后,代理会成为瓶颈,代理也有可能会成为单点等问题。

gRPC注册中心,常用的注册中心你懂了吗?AP还是CP(七)

进程内LB(Load Balance), 软负载

优点:这种方式解决了上面方式的缺点,注册中心本身支持高可用,可扩展,服务提供者启动后往注册中心注册自己,消费者启动后拉去注册中心服务提供者列表,缓存一份,请求到来时,可走本地缓存,也可查询注册中心重新获取,负载均衡算法,失效转移等在客户端实现,自控性强,少了一层代理,性能也会更好。

缺点: 需要一定的研发实力,开发起来复杂,如果公司使用多语言开发项目,需要为每种语言都实现一个客户端,如果服务端升级,势必也要求客户端升级重新发布等。

gRPC注册中心,常用的注册中心你懂了吗?AP还是CP(七)

常用的注册中心以及各自的优劣

  • 事实上,只要具备存储功能或者配置功能的一些软件或者硬件都可以作为注册中心来使用,就看这个注册中心的完备性,高可用性,可扩展性是否足够好。
  • 如nginx,redis,mysql,zookeeper,etcd,eureka,consul等都可以作为注册中心
nginx

在公司规模不大,要求不高的情况下的,采用http协议通信,可以选用nginx来作为注册中心,使用简单,不需要额外的开发工作,往nginx配置文件配置服务端ip地址,配置负载均衡策略,服务消费者只需要配置nginx的ip,就可以通过http请求形式访问到服务提供者后端,nginx做了一层代理,提供了负载均衡,失效转移,心跳检测,服务转发等功能,能让开发人员开箱即用,这其实就是上面的第一种方案,但缺点也很明显,如同上面所说,所以在公司发展初期(流量不大),是能快速提高研发效率,节省成本,专心发展业务的一种手段。

zookeeper

提到注册中心,zk就不得不说,dubbo开源时注册中心默认的方案就是zk,直到现在也有很多公司使用dubbo,也采用zk作为注册中心,zk能作为注册中心主要是zk提供了一种临时节点和节点监控的功能,服务提供启动时往zk某个目录下(一般是服务名称)创建临时节点,节点一创建就会通知监控该服务的消费者服务消费者就会获取到,然后服务提供者和zk之间维持心跳检测,因为该节点是临时节点,所以一旦心跳失败,zk就会删除临时节点,同时服务消费者也会收到通知,这样就摘除了不健康的节点。
问题:看上去很完美,没有问题,但真的是这样吗,zk是采用zab协议的来保证分布式中的一致性,zab全称(Zookeeper Atomic Broadcast)),zk在选举的过程中的为了保证最终一致性是不能写入和读取的,也就是说zk在选举过程中是不可用的,所以从cap理论来说zk属于cp,而注册中心往往的要求的是ap,同时,zk写入只能在leader节点操作,当有大量服务提供者同时启动时,有可能造成leader节点负载过大,从而死机,然后重新选举,死机,形成恶性循环,所以zk并不太适合作为注册中心,但这也仅针对一些大公司,一些中小公司并没那么大的业务量,为了节约成本,选用zk也是可以的。zk是使用Java语言开发的,所以天然的和Java无缝整合,有很多Java的zk客户端

eureka

eureka是开源微服务方案解决框架springcloud的一个组件,作为springcloud的注册中心,为什么springcloud要选择eureka作为注册中心呢?


Eureka是一个cs架构,提供基于rest服务的服务注册和服务发现组件,服务提供者启动后往EurekaServer端注册提供者信息,服务注册后,服务提供者会维护一个心跳用来持续告诉EurekaServer:"我还活着,"以防止Eureka Server的”剔除任务“将该服务实例从服务列表中排除出去,eureka多个server端互相同步服务提供者信息,也就是说只要有一个EurekaServer端存活,那么就满足高可用,一个EurekaServer集群各个节点是平等的,服务消费者引入client的jar包,启动后从server端获取服务提供者息,缓存本地(默认30秒更新一次),即便所有eureka的server死亡,在线的服务提供者,消费者也能正常使用。

etcd

etcd是一个采用go语言的开发的类似zk的分布式协调服务框架,是一个用于共享配置,服务发现的分布式,一致性kv存储系统,etcd在实现上,一致性协议选择上,运维上,安全上比zk要优秀好多,etcd提供HTTP+JSON, gRPC接口,跨平台跨语言,这一点要比zk好很多,对多语言开发者来说是福音,etcd也支持http所访问,安全性好。


etcd同zk一样也是cp系统,在leader节点选举时,读写不可用,下面我们gRPC-go也是采用etcd实现一个注册中心

gRPC-go实现注册中心

gRPC本身没有提供注册中心,但为开发者提供了实现注册中心的接口,开发者是要实现其接口可以了,而且gRPC也提供了一个DNS的demo,下面我们一起看一看这个接口。

  • 代码如下
    //接口所在路径如下,grpc版本是1.23.1
    google.golang.org/grpc@v1.23.1/resolver/resolver.go
    //存储调用对象的信息
    type Target struct {
    Scheme    string
    Authority string
    Endpoint  string
    }
    type BuildOption struct {
    //禁止服务的配置 
    DisableServiceConfig bool
    }
    type Builder interface {
    //构造一个Resolver
    Build(target Target, cc ClientConn, opts BuildOption) (Resolver, error)
    //返回命名空间字符串
    Scheme() string
    }
    type ResolveNowOption struct{}
    type Resolver interface {
    //GRPC将调用resolveNow来尝试解析目标名称
    ResolveNow(ResolveNowOption)
    //关闭这个解析器
    Close()
    }

接下来我们实现这些接口完成一个简单的注册中心,

  • 代码地址如下:

    https://github.com/sunpengwei1992/go_common/tree/master/grpc/helloworld_demo
  • 核心流程如下:

  • 定义服务信息的结构体

  • 定义注册服务和下线服务的接口,并实现

  • 实现gRPC提供的接口

    //服务描述信息
    type ServiceDescInfo struct {
    //服务名称
    ServiceName string
    //ip地址
    Host string
    //端口
    Port int
    //心跳间隔 秒
    IntervalTime time.Duration
    }
    //服务注册和下线的接口
    type RegisterI interface {
    //服务注册
    Register(serviceInfo ServiceDescInfo) error
    //服务下线
    UnRegister(serviceInfo ServiceDescInfo) error
    }

实现gRPC提供的接口,定义etcdBuild结构体,定义etcdResolver结构体

  • 代码如下
    type etcdBuilder struct {
    //etcd地址
    address     string
    //etcd客户端
    client      *clientv3.Client
    //服务的名称
    serviceName string
    }
    //etcd解析结构体
    type etcdResolver struct {
    clientConn              *resolver.ClientConn
    etcdBuilder             *etcdBuilder
    t                           *time.Ticker
    wg                        sync.WaitGroup
    rn                         chan struct{}
    ctx                        context.Context
    cancel                    context.CancelFunc
    disableServiceConfig bool
    }
    //具体的实现可以查看github代码

欢迎大家关注微信公众号:“golang那点事”,更多精彩期待你的到来

gRPC注册中心,常用的注册中心你懂了吗?AP还是CP(七)

本作品采用《CC 协议》,转载必须注明作者和本文链接
那小子阿伟
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!