服务网格工作原理
培训视频
观看以下视频,快速了解服务网格工作原理:
MOSN 形态现状
目前 MOSN 属于数据面的产品,以 Sidecar 的模式和应用部署在同一个 Pod 或者在虚拟机中,属于独立进程。MOSN 最早支持基于轻量 SDK + Mesh 的方式接管网格流量。目前主要支持 3 种流量劫持方式:
轻量级 SDK 端口欺骗的方式接管流量。
基于修改配置 IP+Port -> 127.0.0.1 的方式接管流量。
基于透明劫持 iptables 接管流量。
目前自研 MOSN 已经支持国内主流框架,比如蚂蚁的 SOFA RPC、社区 Dubbo 和 Spring Cloud 技术栈 。服务发现继续使用经典的注册中心机制,内部注册中心产品是 SOFARegistry。
数据面 MOSN 产品目前支持虚拟机和容器场景部署,应用接入 MOSN。作为服务提供方,MOSN 会将自身端口号写入注册中心。作为消费方,MOSN 会从注册中心订阅服务,也就是拿到了服务方 MOSN 的 IP+Port,然后完成服务通信。
MOSN 配置关联关系
为了帮助您梳理清楚 MOSN 的一串配置关系,这里从链路关系逐一讲解。以静态配置为例,目前配置主要分为几大类,如 listeners、routers 和 clusters 等。配置截图如下:
listeners 主要是 TCP、UDP 等端口监听配置,包括制定端口和协议绑定关系等。
RPC 类别,主要支持 request-response 等通信模型,比如 Dubbo、SOFA、Spring Cloud 等私有协议。
MQ 场景,处理消息框架通信场景。
routers 主要保存和协议相关的路由配置映射,匹配 Header 中 Service 信息到具体的 Cluster。
clusters 主要保存 Cluster 中真实的服务地址列表。针对 RPC 场景,一般 Cluster 的 name 是接口,根据接口对应地址列表的映射关系。
上图中的数字代表流程进度:
客户端 MOSN 监听 30530 端口,等待客户端应用 App 调用。
在 MOSN 配置中,我们指定 30530 端口给私有协议 SK 使用,因此协议叫做 SK。
MOSN 静态配置中指定路由配置映射关系 name 为
egress_sk_router
,一般规则为egress_协议名_router
,如果是服务提供方,一般规则为ingress_协议名_router
。可以理解为这个配置映射关系是数组,里面会存储很多 Header 中包含 Service 的不同值(一般值是接口名),并且根据不同值映射到具体的 Cluster。值.*
是特殊值,代表只要 Header 中有 Service 这个 Key,则直接转发到目标 Cluster 对应的名称。请求 Header 包含 Service 任意值,转发到 skClientToWsServer 这个集群。
集群 skClientToWsServer 中包含的地址列表都会被 MOSN 选择调用。
如果理解静态配置和寻址关联关系,MOSN 在运行时,也会动态构造这些 Cluster 信息,因为路由都是在客户端处理的。当服务提供方上线时,MOSN 客户端会收到地址列表推送,MOSN 会去根据推送的 dataId(一般是接口名)去找到 Cluster,并且把地址列表动态更新。
值得一提的是,listeners 中 SOFARPC 目录包含较多私有协议扩展,MOSN 启动的时候默认都会加载并且开启对应端口监听,输出到专有云一般无用的协议,我们会通过 Dockerfile 文件删除无用的协议。
MOSN 调用关系
为了通俗易懂的理解 MOSN 调用链路,需要彻底弄懂 ingress 和 egress 的概念和区分。
站在 Sidecar 视角去理解 egress 和 ingress,比如 MOSN 接收流量后转发给远端还是本地:
egress:出口流量,MOSN 接收流量需要转发给远端的服务提供方。
ingress:入口流量,MOSN 接收流量无脑转发给本地的服务提供方
MOSN 调用关系图示如下:
以上入为例,MOSN 的调用链路关系如下:
服务提供方 MOSN 会将自身 ingress 的协议端口写入到注册中心。
调用方 MOSN 会从注册中心订阅地址列表。
第一次订阅也会返回全量地址列表,端口号是服务方 ingress 绑定的端口号。
注册中心会实时推送地址列表变更到客户端(每次都是全量地址列表)。
客户端 App 发起业务 RPC 请求,请求会被 SDK 转发到本地客户端 MOSN 的 egress 端口号上。
客户端 MOSN 将 RPC 请求通过网络转发,将流量通过负载均衡转发到某一台服务方 MOSN 的 ingress 端口处理。
最终服务到了服务端 ingress listener,会直接转发给本地 App Server 应用。响应返回时,会根据原来的 TCP 链路反向转发。
Mesh 核心转发流程
MOSN 作为数据面,整体分为 4 层:
Network/IO 主要负责网络读写。
Protocol 主要负责协议编解码。
stream 主要负责 request、response 等模型转发和协调。
proxy 主要负责路由等功能。
原始的 MOSN 简介,请参见 MOSN 架构简介。
为了便于理解,本文将 MOSN 的流程简化为 12 个步骤,如下图所示:
MOSN 在启动期间,会暴露本地 egress 端口接收客户端 App 的 RPC 请求。MOSN 会开启 2 个协程,分别死循环去对 TCP 进行读和写处理。MOSN 会通过读协程获取到请求字节流,进入MOSN的协议层处理。
MOSN 会通过 streamconn 实现类中循环解码,直到解析到完整的请求报文。一旦解析到请求报文,会创建和请求 frame 关联的 xstream。
这里的 xtream 用来保持和客户端 App 的 TCP 关联,后续用来用于响应 response。
MOSN 需要将请求转发到服务集群的某一台机器,会到达 proxy 层创建 downstream。
此步骤实现目的如下:
执行 filter 请求/响应链。
执行路由匹配。
执行负载均衡。
在选择服务集群的某一台后,MOSN 会首先初始化选中 IP 对应 host 的连接池。
此时 MOSN 的角色变成了中间人的角色。一方面需要承担客户端 App 的服务端,另一方面需要承担远程服务方的客户端。
upstreamrequest 对象起到关键作用:
保持着对客户端 App 的 TCP 引用。
保持着对转发服务端 TCP 引用,转发客户端 App 请求以及响应服务端 response 时的通知。
在 upstream 的 appendHeaders + appendData 阶段,会用第 4 步骤中选择的 host 创建 sender xstream。
这个 xstream 是客户端的流对象,主要有 2 个目的:
充当 Client 角色,初始化客户端请求信息,将待转发的请求创建对应的 stream 绑定关系。
在真正转发前,需要替换请求 ID 信息,用来解决连接 IO 复用导致请求互相覆盖的问题。
说明出现请求相互覆盖问题的原因:客户端 App 有多个 TCP 连接,MOSN 转发到服务端只有 1 条 TCP 连接。如果客户端 2 个 TCP 连接同时有个 id=1 的request,MOSN 会通过同一条 TCP 转发给服务端,因此响应回来时,MOSN 没办法区分 id=1 的 response 属于哪个客户端 App 的 TCP 连接。
这里的解决办法是 MOSN 转发到服务端的 1 条 TCP 连接,会重新修改请求的 ID 成全局,就不会混淆请求和响应关联关系了。
因为 MOSN 在转发之前修改了请求 ID,因此会重新 encode 请求。
一般优化手段不会 encode 完整报文,只会修改协议头的个别几个字节。
一旦客户端 xstream 准备转发就绪(endOfStream),就会通过第 4 步骤中选择下游 host 直接发送。
此时请求的携程会被阻塞。
MOSN 转发给服务端 host 时,会新建 TCP 连接。此时,每个 TCP 连接也会有 2 个携程去处理读写。
MOSN 客户端 xstream 的会通过读 IO,收到响应 byte 字节流,并且交付上层 protocol 去解码。
一旦完整解码成 response 对象,会通知 upstream request 对象。
upstream request 持有客户端请求的 downstream,唤醒 downstream 阻塞的协程。
对应步骤 2 中 MOSN 作为服务方 xstream 被唤醒,会将收到的响应 response,重新替换回正确的 reqest id,并能且去调用协议层重新 encode 成字节流。
xstream 中持有客户端 App 请求时的 TCP 连接,直接将响应写会客户端,并且销毁 MOSN 中所有请求相关的资源。