SOFAMosn 无损重启/升级

说明,本文档基于 SOFAMosn 0.4.0 版本编写html

前言

SOFAMosn 是一款采用 GoLang 开发的 Service Mesh 数据平面代理,由蚂蚁金服系统部网络团队、蚂蚁金服中间件团队、UC 大文娱团队共同开发,功能和定位相似 Envoy,旨在提供分布式,模块化,可观察,智能化的代理能力;她经过模块化,分层解耦的设计,提供了可编程,事件机制,扩展性,高吞吐量的能力。git

——摘自《 SOFAMosn 的诞生和特性》github

概述

整体上看,链接迁移的流程以下图:编程

图片描述

  • MOSN 经过 forkexec 生成 New MOSN
  • MOSN 经过 domain socket 把 TCP fd 和请求数据发送给 New MOSN
  • New MOSN 转发请求到后端(PUB2)
  • 后端 回复响应到 New MOSN
  • New MOSN 经过 MOSN 传递来的 TCP fd,回复响应到 client

此后:后端

  • mosn 退出 readloop,再也不接受该 TCP 链接上的数据
  • New mosn 开始 readloop,接受该 TCP 链接上的数据

——摘自《SOFAMosn Introduction网络

具体实现

触发

在 MOSN 启动的时候,会加载包数据结构

github.com/alipay/sofa-mosn/pkg/server

在这个包加载的时候,该里面的 serverkeeper.go 这个文件中的 init() 函数被执行。这个函数会起一个协程在捕获 HUP 信号。dom

当 Mosn 接收到来自系统的 HUP 信号时,MOSN 首先会调用 stopStoppable 函数先让 Admin Server 中的全部 Listener 都关闭 。而后调用 reconfigure 函数来进行配置从新加载。socket

迁移过程

旧进程的退出

触发了 reconfigure 函数后,首先 MOSN 会设置两个环境变量分布式

_MOSN_GRACEFUL_RESTART=true
_MOSN_INHERITFD_FD=<number>
  1. _MOSN_GRACEFUL_RESTART 对应的包 github.com/alipay/sofa-mosn/pkg/types 中的 GracefulRestart 常量,用于告诉新启动的 MOSN(下简称 New MOSN) 这个是一个优雅重启。
  2. _MOSN_INHERITFD_FD 对应包 github.com/alipay/sofa-mosn/pkg/types 中的 InheritFd 常量,里面存储的是 ListenerFD 的数量(ListenerFD 就是每一个 Listener 调用 listen() 返回的 fd)。

准备好环境变量后,就调用 syscall 包的 ForkExec 按照当前 MOSN 的启动参数进行启动,并将环境变量和标准输入输出错误和 ListenerFD 都和 New MOSN 共享。而后,MOSN 会等 3 秒,让 New MOSN 启动起来。认为 New MOSN 启动完成后,它就会调用 StopAccept() 让全部的 Listener 中止 Accept 新的请求(已经 Accept 的请求不会结束,socket 的监听也不会断),而后调用 WaitConnectionsDone 函数根据 GracefulTimeout(默认是 30秒) 设置的优雅重启的超时时间让全部的链接都完毕。接着 MOSN 就进行 Metrics 的迁移,完成后就会退出进程。

在 WaitConnectionsDone 中,MOSN 设置了一个时间长度为 2 个 GracefulTimeout + 10秒 的时间的定时器。而后首先会 sleep 一个 GracefulTimeout 的时间,等待全部的链接主动关闭。而后关闭全部 server 中 connHandler 的 listeners 成员的 stopChan. 而后再 sleep 一个 GracefulTimeout + 10秒的时间,等待全部链接的迁移。时间过了以后,函数就会返回。此后,上层会调用 TransferMetrics 进行 Metrics 的调用 Exit 进行进程退出。

新进程的启动

继承 Listener 的获取

在 New MOSN 启动的过程当中,首先会调用 getInheritListeners。这个函数会从读取 Old MOSN 设置的环境变量 _MOSN_GRACEFUL_RESTART,若是为 true, 说明这是一个优雅重启,就会读取环境变量 _MOSN_INHERITFD_FD。因为 Listener 是最早使用 fd 的,因此 fd 老是从3 开始,那么全部 Listener fd 就是: 3, 4, ... , 3 + _MOSN_INHERITFD_FD。而后利用这些 fd 将 Old MOSN 的 Listener 恢复出来。从而获取到继承过来的 Listener。获取完以后,会对获取的 Listener 和配置文件进行比对,判断其合法性。若是不合法的,或者不能新的配置里面没有以至继承过来的 Listener 不须要复用,就会将其关闭。

完成了全部的初始化以后,就会启动两个 Unix Sock 的Server, 分别用与进行链接的迁移和 metrics 的迁移。用于链接迁移的 Unix Sock Server 会在 2 个 GracefulTimeout + 10 秒后自动关闭。

迁移过程当中,New MOSN 对每个 Unix Sock 请求都会分配一个协程去处理。

链接的迁移

当一个请求进来的时候,若是请求使用的协议不是 HTTP1 且不使用系统提供的事件循环的时候,MOSN 会启动本身的 ConnIo, 调用 startReadLoop 和 startWriteLoop 来开启针对这个请求的的读写循环。

读写数据迁移的协议

在发送请求的过程当中,首先会发送一个字节的数据, 这个字节表明了传输的是读数据迁移仍是写数据迁移。0 表明是使用读数据迁移协议。1 表明是使用写数据协议。若是是 0, 还会将该链接的 fd 以 out-of-band 的方式也发送出去。

读数据迁移协议

图片描述

首先是头部分:包括 8 个字节,前 4 个字节是 data 部分的长度,后 4 个字节是 TLS 部分的长度。body 部分:接下来 data length 个字节存储的是 readBuffer 数据。最后 TLS length 个字节存储的是 TLS 的数据。

写数据迁移协议

图片描述

头部分也是 8 个字节, 前 4 个字节存储了 data 部分长度,后 4 个字节存储的是 connection ID。body 部分:接下来的 data length 本身存储的是 writeBuffer 数据。

读数据的迁移

Old MOSN 发送

在 startReadLoop 中,MOSN 会捕获以前提到的 stopChan 被关闭的事件。捕获到这个事件以后,MOSN 会让这个连接等待一个随机的时间,而后开启链接迁移的过程。

首先 MOSN 会往链接中的 transferChan 发一个 transferNotify(值为1) 消息,告诉这个链接对应的写循环:要开始迁移链接了。而后调用 transferRead 开始迁移读链接,并返回一个connection ID,最后将这个 ID 再次发送给 transferChan。

在函数 transferRead 中:

  1. 和 New MOSN 先前提到的负责链接迁移的 transferServer 创建 unix socket 链接。
  2. 获取该链接对应的 fd
  3. 调用 transferSendType 将传输数据使用的协议类型(读数据迁移协议仍是写数据迁移协议)和链接的 fd 发送给 New MOSN。
  4. 调用 transferReadSendData 将 header 部分和 body 部分传输给 New MOSN。若是 TLS 握手还没结束,则 TLS length 为 0。
  5. 接收 New MOSN 处理完这些数据以后返回的 connection ID, 并返回

New MOSN 的接收

当 New MOSN 接受到来自 Old MOSN 的数据时:

  1. New MOSN 会调用 transferRecvType, 首先接受协议的类型(一个字节), 若是是读数据迁移的协议,还会去接受 oob(out-of-band)中的 fd,并利用个这个 fd 重建出一个链接,恢复监听。
  2. 调用 transferReadRecvData 得到本次请求中的 data 部分和 tls 部分的数据。
  3. 调用 transferNewConn, 首先根据重建出的链接,找到 NewMOSN 中对应的 Listener。若是这是一个 TLS 链接,还会利用 Old MOSN 传过来的 TLS 信息将链接重建成一个 TLS 链接。
  4. 而后该 Listener 从触发 Listener 的 OnAccept 事件开始,处理这个链接的请求。当 MOSN 用于封装链接的 connection 结构体创建完毕后,就标志着这个链接迁移完成,并将这个 connection 结构体存储在一个叫 transferMap 的数据结构中。
  5. 利用重建的 connection 的 id 生成一个本次迁移链接的 ID,回传给 Old MOSN。
写数据的迁移

Old MOSN 的发送

当写循环收到读循环从 transferChan 发过来的 transferNotify 消息时,会再读一次 transferChan, 获取到这一次链接传输的 ID,若是 ID 合法,则会开始监听两个 channel:

  1. internalStopChan: 从这个 channel 收到信号(这个 channel 被 close 了),认为这写数据迁移完成了,会直接退出。
  2. writeBufferChan: 这个 channel 传送过来的是须要写的数据,也就是须要传送的数据。收到后就会调用 transferWrite 开始迁移。当 writeLoop 要结束的时候,会 close 掉 internalStopChan 和 writeBufferChan。触发条件1。

在 transferWrite 中:

  1. 和 New MOSN 先前提到的负责链接迁移的 transferServer 创建 unix socket 链接。
  2. 调用 transferSendType 将传输数据使用的协议类型(读数据迁移协议仍是写数据迁移协议,此处是写数据迁移协议)
  3. 调用 tranferWriteSendData 将 writeBuffer 里面的内容连同从 New MOSN 返回的链接传输 ID 一块儿发送给 New MOSN

New MOSN 的接收

当 New MOSN 接受到来自 Old MOSN 的数据时:

  1. New MOSN 会调用 transferRecvType, 接受协议的类型(一个字节),判断是写数据迁移协议,进入接受写数据迁移的数据流程。
  2. 从 unix sock 中读出要写的 buffer, 和链接传输的 ID
  3. 根据 ID 从 transferMap 中 取出对应的 connection 结构体。并让将传输过来的数据扔到 connection 结构体中的 writeBufferChan 中,进入新的 writeLoop。

至此,链接迁移的过成就完成了。

Metrics 的迁移

Old MOSN 退出前的最后一件事,就是把 Metrics 数据托付给 New MOSN。

协议

Metrics 的传输协议很简单,包括两部分 header 和 body

  1. Header 长度为 4 个字节 存放的是 body length
  2. Body 的长度为 body length 个字节。存放着要传输的数据,即 Metrics 数据。
Old MOSN 的发送
  1. 首先他会调 makesTransferData, 将全部的 Metrics 数据都统一收集起来。
  2. 和 New MOSN 先前提到的负责 Metrics 迁移的 transferServer 创建 unix socket 链接。
  3. 前后把 header 和 body 发送给 New MOSN
  4. 若是设置了等待 New MOSN 的响应,会在一个超市时间内等待 New MOSN 1 个字节的返回。
New MOSN 的接收

当 New MOSN 接受到来自 Old MOSN 的数据时,会调 serveConn 函数去处理每个迁移请求:

  1. 读取数据中的协议头,并根据协议头读取出报文体。
  2. 将报文体的数据恢复成成 go-metrics 的数据,供 New MOSN 使用

至此,全部关于平滑重启的操做就完成了。

原文地址:https://blog.coordinate35.cn/...

相关文章
相关标签/搜索