go语言高并发与微服务实战_图解微服务注册中心设计原理与思考并基于go语言实现核心架构...

微服务注册中心的现实例子

da582c467280eb094d4e00a33b5e97aa.png

在现实生活中,我们每个家庭都有一个户口本,我们会统一的去户籍中心,去注册自己家的信息,包括自己家的门牌号,家里几个人,如果有人找我们,就可以通过这个来定位,同理微服务中的注册中心也是一样,所有的服务实例都到注册中心去注册,后续大家如果需要查找别的服务,就到注册中心去查找即可

服务调用方式的

b22bf61c9f3324ae0f2ecc354e255065.png

服务调用方式主要是指微服务中服务之间调用的方式,主要分为两类:

基于负载均衡:通过负载均衡设备对外提供VIP地址来实现服务的调用

基于注册中心:微服务之间通过服务注册中心进行节点发现,然后在服务端直接调用对应的节点进行调用

基于负载均衡的服务调用

8542f5212c85a8e014bb931f56bca5a9.png

基于负载均衡的服务相互调用指的是通过基于Lvs、Haproxy、Nginx等负载均衡软件来构建一个负载均衡服务,所有的 服务调用都通过负载均衡器, 从负载均衡的这种模式下其实有两个主要的问题:

中心化:负载均衡设备成为微服务中心,后期可能会成为微服务的系统瓶颈

流量增倍:从上图可以看到,内网流量被放大1倍,即客户端到负载均衡,负载均衡到后端节点

负载均衡这种实现在早期,还是普遍适用也有很多好处,比如可以做一些负载均衡、长链接维护、分布式跟踪等,这不是本文重点

基于注册中心的服务调用

96920f9e7402b5a70ff57e7c47c3f8cd.png

所有的服务都启动后都通过注册中心来注册自己,同时把注册中心里面的服务信息拉回本地,后续调用,就直接检查本地的服务和节点信息来进行服务节点的调用, 是当前微服务架构中最普遍的做法, 也是后面的主要描述的内容

注册中心的核心架构设计

a43978e24a1b4d86b7e7ba413bd43c8d.png

注册中心单点实现的核心目标就是提供注册表的高并发访问,为了提高性能并且尽可能保持一致性,在设计上我们可以通过全量同步和增量同步,来减少拉取的数据量从而提高系统的吞吐,主要核心由两个:

注册表:注册表存储全量信息,并且提供hashcode用于做一致性效验

事件队列:存储最近变更事件,后续只需要增量同步即可保证一致性

注册中心中的注册表

cc992135b86265a3361465870e7f4835.png

每个服务节点都会来注册中心进行服务注册,那数据如何在服务端进行保存呢,其实就是注册表,其实等同于windows 里面的注册表,每个服务都来注册,把自己的信息上报上来,然后注册中心把注册表,返回给client端,那服务之间就知道要调用服务的节点啦,后续则只需要客户端直接进行调用即可

注册中心事件队列

ac5ee0358719a2a7655a53996b82a20d.png

微服务注册注册中心通常会大量的服务注册, 那不能每次客户端来请求的时候,服务端都返回全量的数据,在数据传输的设计中,通常会有一种增量同步,其实在注册中心中也类似

注册中心通过将最近的服务变更事件保存在一个事件队列中,后续每次客户端拉取只返回增量数据,这样服务端的忘了压力就会小很多,从而提高系统的吞吐

注册中心hashcode

ff892e71a8d53a351975df1250159be6.png

增量数据有一个问题就是,如果客户端错过啦某些事件,比如事件队列满了,则客户端与注册中心的注册表就会不一致, 所以eureka里面引入了一个hashcode的概念,通过比对hashcode是否相同, 如果不同则客户端需要重新全量拉取

实现一个简单的单点服务注册中心

系统架构设计

b96f4f6bbe932a54350695de17574f08.png

系统整体上分为两个端:客户端(Client)和注册中心(Server)

Server: 提供服务注册和获取注册表的接口, 同时本地把保存服务和节点的对应信息,变更事件写入eventQueue

Client: 调用server接口进行服务注册, 同时调用注册表拉取接口进行注册表拉取,保存到LocalRegistry

应用与节点信息

cba9d04d55df66bd455d1da4a131a24a.png

Server端的服务注册表里面的服务和节点的信息,我通过Application和lease来维护

Application: 代表一个应用,里面会包含服务对应的节点信息

Lease: 维护一个节点的信息,比如心跳信息

服务端注册表

注册表结构体

服务端注册表结构体Registry主要包含三部分信息:

Lock: 注册中心是典型的读多写少的应用,server端注册表可能同时提供给N个服务进行读取,所以这里采用读写锁

apps: 保存应用对应的信息, 其实后面写完发现,没必要使用,只使用基础的map就可以搞定

eventQueue: 每次注册表变更都写入事件到里面

c56c000b2162e2e9cda127ed226748a1.png

注册表服务注册

71c26d329aa41de2c2921ab7f06292b4.png

注册流程主要分为下面几部分:

  1. 从注册表获取对应的应用Application
  2. 调用Application的add接口添加节点
  3. 为节点创建一个Lease
  4. 保存节点信息到Application.Node里
  5. 将事件写入到eventQueue
dab8b1c1315f80d58d6382ba37ec8fa7.png

注册表拉取

全量拉取通过all接口拉取全量的返回的是服务对应的节点切片

增量拉取通过details接口返回增量的变更事件和服务端注册表的hashcode

4d3a0ee5e23bfd85f5c35f391c2f6580.png

hashcode

hashcode是一个一致性的保证,eureka里面主要是通过拼接所有的服务名称和节点的个数来生成的一个字符串,这里我们也采用这种方式,

10b44066d1ee99890b159311638ab779.png

客户端注册表数据结构

客户端本地注册表其实就比较简单了,只需要存储服务和节点的对应信息即可

70574f5f842d933593bf03d4cced5bf3.png

客户端逻辑架构

5e4d6497df9399a506183bda3dc0afe0.png

启动流程: 启动时客户端首先调用注册接口进行自我注册,然后调用poll拉取全量注册表

776cc6c1e542e272dcb877e1d06a2eaa.png

验证逻辑

17ca9e4fc90929708c22318da35a8f16.png

通过结果我们可以看出,节点增量拉取了注册表,同时如果发现与本地的hashcode不同就进行全量拉取,并最终达成一致

dbcf5b12388c19fbdc8fdf98250d3a93.png

总结

a99064ebee6cea1055178adfb6b9661f.png

微服务注册中心注册表的这种实现机制,到这基本上就明白了,注册中心通过增量、全量、hashcode三种机制来保证客户端与注册中心的注册表的同步, 并且我们基于 go语言实现了基础的单机的注册功能

其实一个工业级的注册中心还是很麻烦的,比如注册表中那个事件队列,我现在的实现只有一个节点能获取增量,其他的都会通过hashcode来触发全量拉取,后续文章里面会相信介绍下,这块缓存和定时器来实现增量数据的打包

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值