瓜农老梁

一个想分享点干货的家伙,微信公众号「瓜农老梁」

0%

引言

在Nacos服务端分析服务注册逻辑,就绕不开Distro协议。该协议为临时一致性协议,数据存储在缓存中。阿里专门为注册中心而设计的。后面文章逐步还原该协议承担的职责,本文先分析寻址模式。

内容提要

寻址概念

  • 寻址是指如何发现Nacos集群中节点变化的,当检测到变化时能后及时更新节点信息。

寻址模式

  • Nacos支持两种寻址模式分别为「文件寻址」和「地址服务器寻址」
  • 默认为文件寻址,可以通过参数「nacos.core.member.lookup.type」设置取值为「file」或者「address-server」
  • 文件寻址路径默认为 「${user.home}/nacos/conf/cluster.conf」
  • 文件寻址cluster.conf配置文件的内容格式为「ip1:port,ip2:port」
  • 地址服务器寻址默认为:http://jmenv.tbsite.net:8080/serverlist;其中域名、端口、url均可自定义
  • 检测到集群节点变更时会更新缓存并发布MembersChangeEvent事件
  • 为防止新节点没有初始化好,当检测到新节点加入时先设置该节点状态为DOWN,该节点不参与通信
  • 过几秒通过节点之间通信将已初始化的新节点状态由DOWN设置为UP,该节点正式参与通信
阅读全文 »

引言

上篇文章分析了Nacos服务端启动的逻辑,本文分析启动时加载了哪些Handler,以及处理连接请求和注册请求的逻辑。

内容提要

加载RequestHandler

  • 容器启动时加载了13个RequestHandler由他们实际处理各种请求逻辑后面章节再细撸
  • 当然也加载了InstanceRequest->InstanceRequestHandler

服务端响应客户端连接

  • 处理客户端的连接请求由GrpcBiStreamRequestAcceptor#requestBiStream负责
  • 构建和缓存了GrpcConnection对象和Client对象

服务端处理注册请求

  • 处理注册请求的逻辑由InstanceRequestHandler执行
  • 建立起了client与service、instance的关系
  • 发布了三个事件ClientEvent.ClientChangedEvent、ClientOperationEvent.ClientRegisterServiceEvent、MetadataEvent.InstanceMetadataEvent
阅读全文 »

引言

本文从gRPC的.proto文件解读其暴露的服务,由此生成gRPC的客户端/服务端存根。进而分析服务端加载启动过程。最近家里事情较多,本文短了点,大伙随便看看。

内容提要

gRPC Service.proto解读

  • 暴露用于服务端到客户端流式RPC的服务RequestStream#requestStream
  • 暴露用于简单RPC调用的服务Request#request
  • 暴露用于双向流式RPC调用的服务BiRequestStream#requestBiStream
  • 三种方式入参均为Payload

Server启动流程

  • 定义了拦截器获取客户端的ip、port、connectId等
  • 装配了.proto定义的两种调用方式,用于接受客户端请求
    简单调用方式Request#request和双向流调用方式BiRequestStream#biRequestStream
  • 设置了服务启动端口、线程、接受消息的限制、压缩/解压缩类型
阅读全文 »

引言

上一篇客户端初始化没有撸完,这篇继续。Nacos从2.0以后增加了对grpc的支持,代码中HTTP的代理初始化还有保留,我们注册发现通常为临时节点,这部分已由gRPC接管。可以对比下新旧逻辑的实现差异。

内容提要

HTTP代理初始化

HTTP心跳检测器

  • HTTP心跳检测只适用于注册的节点持久节点,临时节点会使用grpc代理(HTTP的心跳检测默认废弃由grpc替代)
  • 在初始化时客户端注册代理NamingClientProxy时,初始化了一个HTTP心跳器用于向Nacos Server发起心跳
  • 在注册节点时通过向心跳执行器添加心跳任务addBeatInfo触发
  • 心跳执行器通过每隔五秒中向Nacos Server发起HTTP请求
  • 如果返回的server not found会向Nacos Server发起注册请求重新注册

UDP接受服务端推送

  • Client通过UDP接受到nacos server推动的消息
  • 如果服务端推送的为服务信息通过processServiceInfo处理逻辑见上篇,主要实例变更时的通知机制
  • 如果dump类型,则客户端发送服务信息serviceInfoMap的ack信息到服务端

gRPC代理初始化

gRPC初始化逻辑概览

  • gRPC 客户端代理的初始化主要逻辑为创建gRPC Client并启动
  • 并注册ServerRequestHandler用于处理Nacos Server推送的NotifySubscriberRequest请求
  • 注册ConnectionListener用于处理gRPC建立和断开连接事件
  • 请求超时时间可以通过「namingRequestTimeout」设置,默认为3秒

gRPC Client启动逻辑

  • gRPC Client启动逻辑主要在于建立与nacos server的grpc连接,其中两个守护线程一直在运行
  • 守护线程1用于处理grpc连接的建立和关闭事件
  • 守护线程2用于与nacos server的心跳保鲜,并负责异步建立grpc连接
  • 守护线程2同时负责当nacos server的地址信息发生变更时重新与新server建立连接
  • nacos server的地址变更通过grpc通道由server推送ConnectResetRequest到client
  • grpc client只与nacos server集群中一台建立grpc连接。
阅读全文 »

引言

Nacos在业界注册中心的选型中举足轻重,值得去深入分析和研究。本文就注册和发现客户端的初始话逻辑从源码角度分析其做了什么事情,另外,其服务发现的设计架构可作为我们相似场景设计的模型作为参考。

内容提要

可配置参数

  • 默认命名空间public,可通过System.setProperty(PropertyKeyConst.NAMESPACE, “”)设置
  • 默认web根目录为/nacos/v1/ns,可通过System.setProperty(PropertyKeyConst.CONTEXT_PATH, “”)设置
  • 默认日志文件名称为naming.log,可通过System.setProperty(UtilAndComs.NACOS_NAMING_LOG_NAME, “”)设置
  • 支持通过动态刷新EndPoint获取server地址列表,EndPoint地址可通过properties.setProperty(PropertyKeyConst.ENDPOINT,””)设置,EndPoint刷新的频率是30秒
  • 支持直接传入Server地址properties.setProperty(PropertyKeyConst.SERVER_ADD,””)

服务发现逻辑

服务发现逻辑也就是当实例变更时通知给订阅者逻辑,详细逻辑如下:

  • 当我们开启订阅时subscribe时,会通过调度器生成一个UpdateTask;UpdateTask每个6秒钟(最长为1分钟)会从注册中心获取实例Instance列表,当检测到实例Instance列表有变更时会通过NotifyCenter.publishEvent发布实例变更事件
  • NotifyCenter是个门面类,对DefaultPublisher的操作,以及DefaultPublisher与关联事件的映射,例如:会绑定ChangeEvent与EventPublisher的关系;上面发布的实例变更事件实际为添加到DefaultPublisher的阻塞队列
  • DefaultPublisher中维护一个订阅者集合subscribers;DefaultPublisher中维护一个事件阻塞队列queue默认大小为16384;DefaultPublisher同时也是一个线程类初始化时通过for死循环从阻塞队列queue中获取Event,并循环回调订阅者subscribers执行该Event
  • subscribers执行Event,具体回调到InstancesChangeNotifier#onEvent,进而回调到我们订阅时提供的AbstractEventListener#onEvent,从而实现我们的发现逻辑。

故障转移逻辑

  • 在ServiceInfoHolder初始化初始化时,会生成本地缓存目录 ${user.home}/nacos/naming
  • 每10秒钟将ServiceInfo备份到缓存文件中
  • 故障转移开启生效实例化延迟5秒钟会从本地文件将ServiceInfo读入缓存serviceMap
  • 如果配置参数「namingLoadCacheAtStart」设置为true启动时会从本地缓存文件读取ServiceInfo信息,默认为false
阅读全文 »

偶然

在今年3月初的时候敏捷教练陈文博应该是接到大领导指示让去召集人去投稿全球架构师大会(ArchSummit),目的在于提升公司的影响力和增进技术交流。

文博发我一个大会议题链接说让投稿,老梁扫了一眼没发现没合适的主题。这家伙找益伟去了,益伟给了个列表,微服务架构方向让我试试看。领导说让试试,你还能说不?这个大会老梁以前作为听众参加过,被选中其实不太容易,不太确定的事花太多时间划不来,老梁花了十来分钟拟了个提纲发给了他。

过了几天反馈说被选中了,让继续细化提纲、突出难点、听众受益点,就这么稀里糊涂的被选中去参加了这个大会。

江湖很大,圈子很小,来去匆匆少了联系,再见依旧是故人。在这个过程中也跟业内优秀的专家们建立一些链接,特别感谢:

  • 感谢敏捷教练陈文博方方面面的支持和推动
  • 感谢组员李莹在PPT动画制作过程中的多次修改
  • 感谢领导鹏哥(杨鹏)、益伟在准备PPT过程中的指导和提点
  • 感谢大会主编薛梁和专题出品人蚂蚁资深技术专家黄挺(鲁直)的邀请

老梁参加大会主要两部分,第一天是一个现场分享、第二天是一个视频录制。下面是参加大会分享的内容,其实也没啥,大伙随便看看。

阅读全文 »

引言

当我们SOA的服务提供方S的某个资源(接口方法)想针不同的服务消费方(A与B)设置不同的限流阈值时,这时就需要用到针对调用来源的限流。那我们可以大规模去使用这种限流方式吗?

内容提要

1.应用场景

示意图如下,针对A服务methodA1调用服务S的methodS1设置QPS限流300,针对B服务methodB1调用S服务的methodS1设置限流1000。也就是需要在S服务中对相同的资源(methodS1)针对不同的来源A与B设置不同的限流阈值。

2.实现原理

  • Sentinel在统计请求流量时会为每个调用来源构建统计信息(StatisticNode)

  • 在请求通过时获取调用来源origin对应的统计信息判决请求是否放行

3.为什么不能大量使用针对调用来源的限流?

备注:由于需要为每个调用来源origin的资源建立统计信息StatisticNode,大量使用会造成内存占用过多。这点官方faq中也给出了警示。**“注意 origin 数量不能太多,否则会导致内存暴涨,并且目前不支持模式匹配。” **

下面举例说明,下面的例子中针对来源限流是不针对来源限流内存占用的30倍。

不针对来源限流:S服务有15个对外提供的服务接口,如果不针对来源限流,只需要15个统计StatisticNode即可

针对来源限流:如果调用S服务的消费者有30个,那么需要统计的StatisticNode的数量=30 * 15 = 450个

在实际中如果支持了其实难以控制数量是否是太多的,所以这是一个权衡的过程。首先针对调用来源的限流这个场景是不是很普遍,如果只是偶尔出现,这个功能应该考虑被禁用。

阅读全文 »

引言

前面聊了大于8KB的内存分配,那小于8KB的呢?上一篇的平衡二叉树第十一层的叶子节点最小也是8KB,那比如要分配128B的缓存,直接分给8KB显然是不合适的,Tiny是小于512Byte,Small介于512B~8KB,Tiny和Small统称Subpage,本文就聊聊他们的内存分配情况,这块应该是整个netty最为复杂的部分了。

内容提要

下面是以分配128B为例的整体流程架构图,下面大体叙述下其流程。

  • 先从平衡二叉树的第11层选一个未分配的叶子节点大小为8KB的一个Page
    备注:本例中为memoryMap[2048]

  • 对该Page进行切割,假如要分配128B,整体会切割为64块
    备注:8192/128=64

  • 通过long类型二进制64位来标记分割成各个块的分配状态

    备注:0:未分配,1:已分配

  • 一个bitmap数组长度为8,每个元素都能对64块内存进行标记

  • 建立了二叉树节点与切分块之间的映射关系
    备注:memoryMapIdx ^ maxSubpageAllocs

  • 分配后建立二叉树叶子节点与标记位之间的关系,可以指向内存一块区域
    备注:0x4000000000000000L | (long) bitmapIdx << 32 | memoryMapIdx

阅读全文 »

前言

Zookeeper作为注册中心不是跑的好好的吗?为什么要替换,是不是闲的?在过去的一段时间替换它的优先级的确不高,然而腾出手来是要替换掉它的,只是因为Zookeeper不适合做注册中心。换句话说选择注册中心时有更好的选择。

CAP定理与举例

CAP定理

CAP定理已经深入每个开发者心里,我们一起回顾下,下面摘自维基百科定义。

CAP定理(CAP theorem),又被称作布鲁尔定理(Brewer’s theorem),它指出对于一个分布式计算系统来说,不可能同时满足以下三点:

  • 一致性(Consistency):等同于所有节点访问同一份最新的数据副本
  • 可用性(Availability)每次请求都能获取到非错的响应——但是不保证获取的数据为最新数据
  • 分区容错性(Partition tolerance)以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择

举例说明

例如:有5个节点被网络分区,分区1有三个节点,分区2有两个节点。

  • 如果保证两个分区节点都可用(AP),由于网络不通,那么无法保证两个分区数据一致性(CP)
  • 如果保证两个分区数据一致性(CP),由于网络不通,那么会导致至少一个分区不可用
  • 如果既想保证所有节点一致性,还要保证节点都可用(CA),那么网络就必须是好的,不能出现网络分区,失去了分区容错性(P)

因此,三者不可兼得。

阅读全文 »

大家好,我是老梁,一个想跟大伙分享点干货的家伙。

到了一周中最放松的时刻,老梁回到家,家人往往都睡了。

老梁就找点吃的喝的,今天喝了大半罐RIO强爽8°C白桃酒,有点晕晕乎乎的。

老梁坐在沙发上抱着电脑跟大伙聊聊天,今天聊点啥呢?

前些天公司请了头部公司研究员大咖来分享,大领导口干舌燥费了好大劲请来的。

大咖抽出宝贵的时间来分享,听的人不少,听进去的不多,听了去实践就更少了,大部分过几天就忘的差不多了。

这种情况领导们也知道,才要求组织者们去收集启发感受,沉淀下来点东西。

老梁也被拉进去要求写一篇,组织者说已经跟领导吹出去了,不写凑不够数量。

大咖分享的是质量和效能,下面是老梁瞎想的,不一点对,各位随便看看。

阅读全文 »