This commit is contained in:
louzefeng
2024-07-11 05:50:32 +00:00
parent bf99793fd0
commit d3828a7aee
6071 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,85 @@
## 内容概要
本系列文章是微博从服务化改造到 Service Mesh实践整个过程的分享以微博自研 Service Mesh 解决方案 WeiboMesh 为例),主要是我们在这个过程中遇到的一些问题以及我个人关于服务化和 Service Mesh 的思考。
考虑到有的同学之前可能没有接触过 Service Mesh 这个概念,所以这里我先对 Service Mesh 做一个简单的介绍,作为后续内容的基础。
## 什么是 Service Mesh
Service Mesh 这个概念最早由开发 Linkerd 的 Buoyant, Inc 公司提出2016年9月29日的 SF Microservices MeetUp 上他们的 CTO Oliver Gould 在关于微服务的分享中首次公开使用了这个术语。
加入 Buoyant 之前, Oliver Gould 是 Twitter 的技术 Leader也是 Twitter RPC 框架 Finagle 的核心开发者,加入 Buoyant 后他创建了 LinkerdLinkerd 的 Github 创建日期为 2016年1月10日可见 Service Mesh 这个概念在 Buoyant, Inc 公司内部已经流传很久。
<img src="https://static001.geekbang.org/resource/image/a2/eb/a22cfe42468d7b9c369329152d1bbbeb.jpg" alt="" />
而 Service Mesh 这个概念的定义则是 Buoyant, Inc 公司的 CEO William Morgan 于 2017 年 4月25日在公司官网发布的题为&quot;Whats a service mesh? And why do I need one?&quot;的文章中给出的。下面我们来看一下定义的内容:
>
WHAT IS A SERVICE MESH?
>
A service mesh is a dedicated infrastructure layer for handling service-to-service communication. Its responsible for the reliable delivery of requests through the complex topology of services that comprise a modern, cloud native application. In practice, the service mesh is typically implemented as an array of lightweight network proxies that are deployed alongside application code, without the application needing to be aware. (But there are variations to this idea, as well see.)
原文翻译Service Mesh 是一个基础设施层用于处理服务间通信。云原生应用有着复杂的服务拓扑Service Mesh 保证请求可以在这些拓扑中可靠地穿梭。在实际应用当中Service Mesh 通常是由一系列轻量级的网络代理组成的,它们与应用程序部署在一起,但应用程序不需要知道它们的存在。
关于这个定义有以下两个值得我们关注的核心点:
1. Service Mesh 是一个专门负责请求可靠传输的基础设施层;
1. Service Mesh 的实现为一组同应用部署在一起并且对应用透明的轻量网络代理。
文中 William Morgan 对 Service Mesh 概念的补充说明也进一步明确了 Service Mesh 的职责边界。我们来看看这段说明原文:
>
The concept of the service mesh as a separate layer is tied to the rise of the cloud native application. In the cloud native model, a single application might consist of hundreds of services; each service might have thousands of instances; and each of those instances might be in a constantly-changing state as they are dynamically scheduled by an orchestrator like Kubernetes. Not only is service communication in this world incredibly complex, its a pervasive and fundamental part of runtime behavior. Managing it is vital to ensuring end-to-end performance and reliability.
原文翻译随着云原生应用的崛起Service Mesh 逐渐成为一个独立的基础设施层。在云原生模型里,一个应用可以由数百个服务组成,每个服务可能有数千个实例,而每个实例可能会持续地发生变化。服务间通信不仅异常复杂,而且也是运行时行为的基础。管理好服务间通信对于保证端到端的性能和可靠性来说是非常重要的。
结合前面的定义,用一句话总结:服务治理和请求可靠传输就是 Service Mesh 这个基础设施层的职能边界。
## 为什么是 Service Mesh
前面对 Service Mesh 这个概念做了一个简单的说明,可能对微服务了解得不深的同学不是很好理解,为什么是 Service Mesh之前没有 Service Mesh 的时候不也一样 OK 吗换句话说Service Mesh 解决了微服务架构的哪些问题呢?
要回答这个问题,我们先来回顾一下近些年大部分服务化架构演进的常见历程。
为了保证项目能快速成型并上线,起初的应用可能是一个大一统的单体服务,所有业务逻辑、数据层依赖等都在一个项目中完成,这么做的好处在于:开发独立、快速,利用资源合理,服务调用更直接、性能更高等等。
但缺点也不少,最致命的问题就是耦合,而且随着业务的不断堆叠这种耦合会越来越严重,项目越来越臃肿。
所以下一步通常都会往模块化的服务演进,比如 MVC 模式的使用、RPC 技术的引入等,主要解决业务或者功能模块之间耦合的问题。
随着业务量的增长和组织的扩大,性能和扩展性将是不得不面对的大问题,所以通常会按照 SOA 的思路进行面向服务的体系结构拆分。
随着云化技术的发展,容器技术的普及,考虑到集群的弹性运维以及业务运营维护成本、业务运营演进效率等问题,微服务架构就显得更贴近现实。
<img src="https://static001.geekbang.org/resource/image/6e/d9/6eaa78e4649cc321b4721ceebe4595d9.jpg" alt="" />
**图片来源于网络**
微服务对比之前的各种传统架构模式带来了一沓子的好处,比如降低了单体服务的复杂性,随即提升了开发速度,而且代码更容易理解和维护,开发团队对技术的选择变得更自由,每个微服务都独立部署所以能很方便调配部署的方式及规模等等。
尤其是一些新兴的互联网公司,创业初期基于运营成本考虑可能都不会去自建机房、买服务器上架,而是直接走云原生应用的路子,这样既能节省成本又能高质高效地应对快速增长的用户。
但也要考虑这个问题,随着微服务化的逐步发展和细化(这里我们暂且不考虑微服务拆分的问题,因为拆分的粒度往往没有规范可依,我个人认为适合自己的才是最好的,当然更不考虑拆分维度或者康威定律),越来越多微服务,每个微服务部署成倍数以上的微服务实例,这些实例数可能轻轻松松就会达到成百上千个,各服务间的依赖变成复杂的网状拓扑结构。
与传统基于 DNS 和中心化的 Server 端负载均衡来实现服务依赖和治理的形式相比,基于注册中心和一个去中心化的 Client 端负载均衡来实现服务注册发现和治理的微服务则是更加复杂的分布式应用,不管是部署形式还是服务的发现、服务依赖调用和请求传输等问题都变得非常难以处理。因为整个体系有太多的不稳定因素,比如网络的分区可能导致服务得不到有效治理。
这里的重点就是要面对微服务架构下服务的治理与请求的可靠传输等问题,这就是为什么需要有 Service Mesh 这样的一个基础设施层来对应用透明化地解决这些问题。
## 下一代的微服务 Service Mesh
随着云计算技术的成熟和普及以及容器技术的大量应用,为了更快地交付、降低试错风险、更快地拓展业务,云原生应用将会是未来服务演进的方向。像微博这种访问体量极大的公司,从容器技术发展前期的 2014、2015 年就开始着力发展混合云架构。另外,现在各种云计算厂商大力推广的云服务也不难佐证这一观点。
<img src="https://static001.geekbang.org/resource/image/0b/ac/0bf77f6cc89f1c81b5e7c2d049b3edac.png" alt="" />
同时,与 Kubernets 类似的容器调度编排技术的不断成熟,使得容器作为微服务的最小部署单元,这更有利于发挥微服务架构的巨大优势。并且在 Service Mesh 方向上不断涌现的各种优秀的项目比如 istio 和 ConduitConduit 目前仅支持 Kubernets 平台)的出现补足了 Kubernetes 等平台在微服务间服务通讯上的短板。
更关键的是,这些优秀项目的背后不乏 Google、IBM 等大公司背书,所以很容易相信未来微服务架构将围绕 Kubernetes 等容器生态来展开,这也就不难得出 Service Mesh 将是下一代微服务这样的结论。
通过本文,希望大家对 Service Mesh 有个初步的了解,后面的一系列文章我将分享我们在 Service Mesh 实践中的一些收获和感悟,希望能帮助大家搭上下一代微服务这趟快车。
阅读过程中你有哪些问题和感悟?欢迎留言与大家交流,我们也会请作者解答你的疑问。

View File

@@ -0,0 +1,112 @@
## 内容概要
技术支撑着业务高歌猛进,业务增长反过来又驱动着技术不断向前演化,这是每个互联网公司发展过程中不变的旋律。作为全国最大社交媒体网站的微博更是如此。
从 2009 年上线至今,微博架构经历了从最初的单体应用到后面的 RPC 服务化、容器化、混合云架构以及现在的跨语言服务化和 Service Mesh 等诸多阶段,架构演变支撑着微博业务的一次次华丽转身,也见证了微博的飞速成长。
那么,微博架构是如何从一开始的单体应用一步步成长为今天的庞大规模?作为国内最早落地 Service Mesh 的公司,微博为什么要选择做 Service Mesh具体又是如何做的这是本系列文章将试图回答的问题。
首先,我会结合微博架构演进的历程向你展示当前微博架构的整体概貌。在这之后,我将聚焦于微博在 Service Mesh 方面的具体落地实践,为你详细讲解微博自研的服务网格 WeiboMesh 从 0 到 1 的成长历程。当然,其中也会有我自己对架构演进的一些思考。
**下面,我们进入正题。**
在业务发展的每个阶段,面临的问题都不尽相同,而问题又有各种优先级,这就难免为了解决某些较为迫切的问题而引入一些当时不 Care 的问题,这也是日常架构演化过程中难以避免的魔咒。
鱼和熊掌不可兼得,所以架构演化的真谛就在于各种方案评估中的利弊权衡,在以业务为重的前提下进行正向演化。
那么,微博各个发展时期的架构又是如何演化的呢?
## 业务初期:单体架构
微博发展初期,用户规模高速增长,伴随而来的还有不断涌现的新业务,因为你不知道哪个今天还名不见经传的业务明天就会摇身一变成为备受瞩目的核心业务,大家就像在白纸上疯狂试错。这个时候,快速开发上线才是当务之急。
为了达成这个目标,我们对整个系统做了优良的模块化设计,每个业务作为一个独立的模块,保障业务能独立开发、发布并快速上线。
同时我们自研了容器框架 Cedrus并在接入层实现了一些通用的逻辑比如提供统一的认证、频次控制、黑白名单、降级开关、配置服务等功能。平台服务内部则通过 Jar 包的方式依赖调用,对外暴露 API 接口。
在部署方面,我们采用大服务池整体部署的方案,这样一来我们可以更合理地利用资源,避免为每个项目每个模块单独配置资源。
这种单体架构的好处在于资源利用更合理,通过 Jar 包应用来完成的本地服务调用不仅更直接,性能也更高,模块之间开发也相互独立,很多通用的前置逻辑被统一剥离出来后,业务的同学只需要关注自己的逻辑实现就可以了。
当然,单体架构的缺点也很突出,最主要的就是耦合。项目之间强耦合带来了一系列问题,比如升级困难,回归测试非常难做,以及随着业务模块的增多,模块之间的依赖解决困难等问题,此外还有各种 Jar 包冲突,越往后功能越加臃肿,业务变更十分吃力。这时候,就必须考虑做拆分了。
<img src="https://static001.geekbang.org/resource/image/19/37/19e69e0dcc9183a35e17e4413123fa37.png" alt="" />
## 业务稳定期:服务化改造
要对当时规模已经十分庞大的微博平台做拆分是一件极具挑战的事情,好在当时微博业务的发展已进入稳定期。这就是我之前所说的,在每个发展阶段我们所面临的问题都不尽相同,如果说前期大规模的单体架构是为了解决当时业务的温饱问题,那么以系统拆分为出发点的服务化改造就是要做到不但要温饱,还要吃得好。
我们希望通过架构改造来达到保证服务高可用的同时实现业务解耦的目的,以便更好地支撑业务发展。如何做到这一点?
我们主要从业务模块拆分方面来考虑,基于我们之前模块化的单体应用架构,按业务模块拆分是最自然也最容易想到的方案。我们只需要把以往基于 Jar 包依赖的大一统平台按照业务模块做拆分然后独立部署,即可达到业务解耦的目的。<br />
<img src="https://static001.geekbang.org/resource/image/48/55/48fe8ec6cb1be0b64db119134710af55.png" alt="" />
## RPC 服务化
但是拆分之后服务之间依赖的问题如何解决?我们当时面临两种选择,一种是提供 HTTP 的 RESTful 接口,一种是使用 RPC 提供远端过程调用。
RESTful 接口的好处在于 HTTP 是明文协议开发调试比较方便RESTful 接口描述也足够简单。但缺点也很突出HTTP 协议本身比较臃肿,我们内部服务的依赖主要解决数据可靠性传输的问题,并不需要那么多无用的请求头。
相比之下, RPC 具有可编程特性,可以根据微博的业务特性定制化开发。同时,因为使用私有协议,所以能大大减小每个请求的体量,这对内部服务动辄过亿的依赖调用来说,能节省不少专线带宽,同时能收获更高的访问性能。所以我们决定采用 RPC 的方式来解耦服务间依赖。
另外,在技术选型方面,因为 RPC 框架会是我们今后服务依赖的核心组件,所以我们特别慎重。选择使用现成的开源方案还是走自研之路?这是当时摆在我们面前的一大现实问题。
我们最终决定自研 RPC 框架,原因在于,如果使用开源方案,很难找到一款完全适合微博场景的 RPC 框架,就算找到一个差不多能满足的,但要在线上生产使用,不摸个一清二楚我们也不敢上,这个熟悉的过程成本同样不低。
而且,开源软件的发展一般遵从于社区意志,不以微博的需求为转移。如果到时候出现不得不基于微博场景的分叉,离社区越来越远,还不如一开始就走自研的道路。所以 2013 年起我们开始了 RPC 服务化改造之路。
我们自研了微博自己的 RPC 框架 Motan结合注册中心实现了业务的解耦和服务的高效治理。之后 Motan 经历了多次热点事件和三节高峰的严峻考验,稳定性和可靠性都得到了实际场景的验证。<br />
<img src="https://static001.geekbang.org/resource/image/a5/21/a5214db530db7044da6162b4afdc0f21.png" alt="" />
## 容器化、混合云架构
然而好景不长,大量按业务拆分的服务独立部署使得服务的扩缩容操作缓慢,直接拉低了峰值流量的应对能力。
正好这个时候, Docker 提出的一整套围绕容器部署和管理相关的生态系统逐渐完善,于是微博率先在重点业务上尝试了容器化。
与此同时,虚拟化、云计算领域也在飞速发展。为了低成本高效率地应对各种极端峰值,我们在容器化的基础上探索了公有云和私有云混合部署的模式,研发了微博 DCP 混合云平台,实现了资源的动态扩缩容,结合 Motan RPC 的服务治理实现了对流量的弹性调度。
至此,微博平台在 Java 技术栈形成了配套完善的一整套服务体系,有完善的服务治理相关组件、明确的 SLA 指标、完备的 Trace、监控等体系保障微博平台的高性能高可用运转。
<img src="https://static001.geekbang.org/resource/image/ac/78/ac212f43049c04b0d9a3dd11ddb95e78.png" alt="" />
## 跨语言服务化之路
但微博整体技术栈比较多样化,异构系统一般通过 RESTful 接口进行交互。由于每个团队的服务部署都不尽相同,依赖的服务访问往往要经过层层转发,此过程中繁重的网络 I/O 拖长了请求耗时,影响了系统性能同时也使得问题排查变得更复杂。另外,每种语言都有一套自己的系统或者指标,这也带来了许多不必要的重复资源浪费。
如何解决跨语言交互,平衡各种语言间服务治理能力与标准各异的问题?如何对日常问题快速排查,使上下游业务更容易观测和联动?我们认为必须要有一套跨语言的服务治理方案来解决异构语言交互以及统一服务治理标准等问题。所以从 2016 年开始,我们开始探索跨语言服务化的道路。
Java 和 PHP 是微博内部使用最多的两种语言,所以我们起初的跨语言是立足于微博平台的 Java 体系,探索 Java 与 PHP之间的跨语言调用。我们最初在 Motan RPC 实现了 PHP RPC 框架 Yar 的协议,实现了服务调通,但是这只完成了 Java 和 PHP 之间的跨语言调用,而其他语言并没有 Yar 协议。
于是我们又调研了其他支持跨语言的 RPC 框架,发现 gRPC 可能与我们的需求更接近,于是我们希望通过在 Motan 中添加对 gRPC 协议的支持来达到跨语言的目的。
还是以 Java PHP 跨语言为起点,除了跨语言服务调通外,更为重要的是实现服务化的核心——服务治理功能。这时我们发现用 PHP 实现服务发现不太方便,因为通常 PHP 是以 PHP-FPM 的形式运行在前端服务器,每个 FPM 进程相互独立,并没有一个统一常驻内存的地方来存取服务发现回来的结果以及每次服务请求的状态等基本信息。
我们还尝试了本地守护进程和 OpenResty 的 Timer 来实现服务发现,但也只能实现最基础的节点发现功能。而对于复杂的服务治理功能,比如需要基于每次请求完成情况而实现的请求双发或者快速失败等常用服务治理策略就比较吃力。
另外实现了基础服务发现功能的 PHP 通过 gRPC 调用的性能也并没有 gRPC 宣称的那么强悍。有时改造后的效果跟之前 RESTful 接口的访问性能差不多。因为在微博场景下,比如取一个 Feed 列表,里面每条微博的 proto 文件就有百十个字段,每次会请求回来大量数据, 而PHP 在 PB 反序列化方面耗时非常大,这就直接抵消了 RPC 直连带来的性能优化。
<img src="https://static001.geekbang.org/resource/image/c2/5c/c27037b966cea1ce4f5a5def608b5d5c.png" alt="" />
## 从跨语言服务化到 Service Mesh
抛开大 PB 反序列化带来的性能损失,类似 PHP 这种原生没有常驻内存控制能力的语言,实现服务治理都会面临同样的问题,就算能很自然地实现服务治理功能,难道需要每种语言都实现一套重复的服务治理功能吗?显然不是这样的。
所以我们就希望引入一个 Agent来统一解决服务治理的问题Client 只需要实现 Motan 协议解析,直接通过本机 Agent 调用远端服务即可。这便是 WeiboMesh 的雏形,也就是目前被大家所熟知的 SideCar 模式代理的 Service Mesh 实现。
<img src="https://static001.geekbang.org/resource/image/11/5e/11f53b0b189bf28b7a0d422eb25d455e.png" alt="" />
那么,我们是如何从跨语言服务化走到 Service Mesh 这条路的呢?要解答这个问题,只要弄清楚 Service Mesh 是什么,搞清楚 Service Mesh 解决问题的边界,答案就一目了然了。
Service Mesh 是什么?这个词最早是由开发 Linkerd 的 Buoyant 公司提出Linkerd 的 CEO William 最早给出定义服务网格Service Mesh是一个基础设施层功能在于处理服务间通信职责是负责实现请求的可靠传递。在实践中服务网格通常实现为轻量级网络代理与应用程序部署在一起但是对应用程序透明。
我们在解决跨语言服务化中服务间调用和统一服务治理所引入的 Agent 就是这个 Mesh 层。虽然 Service Mesh 是个新词,但它描述的问题却是一个固有问题,微服务发展到一定阶段,当服务间的调用、依赖、服务治理复杂到一定程度后,都会面临这个问题。所以 Service Mesh 是服务化必经之路,这就是为什么我们的跨语言服务化最终会落脚到 WeiboMesh。
我们基于 Motan-go 实现了客户端服务端的双向代理,基于微博注册中心 Vintage 实现了对 Agent 的动态指令控制,完成了传输与控制的重新定义,并结合 OpenDCP 平台实现了动态流量调度和弹性扩缩容,保障了服务的高可用。
目前已经有很多核心业务完成了基于 WeiboMesh 的升级改造,比如大家经常使用的微博热搜、热门微博等。我们从 2016 年开始起步,一路摸索,走到现在与 Service Mesh 理念完美契合,完成了 WeiboMesh 的主体建设。
接下来的几篇文章我会就 WeiboMesh 的具体实现过程与探索中的经验做一些总结和探讨。
阅读过程中你有哪些问题和感悟?欢迎留言与大家交流,我们也会请作者解答你的疑问。

View File

@@ -0,0 +1,78 @@
## 内容概要
本文主要分析 WeiboMesh 在跨语言方面的探索。
第一篇文章我们梳理了 WeiboMesh 的演进历程。从单体应用到 RPC 服务化、容器化,到后期的混合云应用以及跨语言支持,再到目前的 WeiboMeshMotan RPC 在其中扮演了至关重要的角色。
本文我将从 Motan 的服务治理和跨语言两个方面对 Motan RPC 做一个简单介绍。帮助大家更好地理解我们在 WeiboMesh 选型和落地过程中的一些取舍。
## Motan RPC
Motan 从 2013 年上线至今,经历了无数次流量洪峰的检验,效果有目共睹。作为一个长于服务治理、轻量、具备良好扩展性、易于二次开发的高性能 RPC 框架, Motan 在微博的服务通信、微服务化拆分以及分布式服务治理等诸多场景中被大量使用。
RPC 其实是一门比较有历史的技术,其思想最早可以追溯到 1976 年的“信使报”Courier这是一个计算机通信协议。顾名思义RPC 全称为Remote Procedure Call中文译作远程过程调用它主要解决远程服务通信的问题。
RPC 协议通常包括 stub、通信协议、RPC 消息解析这几个部分,一般的 RPC 服务框架,除了实现 RPC 协议规定的点对点通信外还包含服务发现与治理等相关功能。Motan 就是一个比较有代表性的服务治理型 RPC 框架。
<img src="https://static001.geekbang.org/resource/image/c7/cc/c78505dbf8e00d3d496fb8385c27d2cc.png" alt="" />
## Motan 服务治理
Motan 最初基于 Java 开发,当时微博迫切的服务化改造需求促成了 Motan 项目的启动,诞生之初 Motan 就在服务治理、扩展性和高可用方向做足了工作,实现了完备的服务发布、订阅机制;失败重试、快速失败、异常隔离等高可用策略;低并发度优先、一致性 Hash、随机请求、轮询等负载均衡策略以及 SPI 扩展机制、调用统计、流量控制等。
<img src="https://static001.geekbang.org/resource/image/a9/8e/a98e56fd63a8f611b5773d26fdb8ba8e.png" alt="" />
**图片来自 Motan 社区爱好者**
结合微博自研的 Vintage 注册中心(类似开源的 zookeeper 、etcd 等服务端启动时会向注册中心按照服务Service和分组Group注册自己的服务当然还有版本号、协议等其他信息并不断向注册中心汇报自己的健康状态保证注册中心有最新的集群状态信息。
客户端启动时根据服务名称及分组等信息订阅所依赖的服务, 同时将服务发现线程实时发现回来的最新可用节点组成 Cluster并使用所配置的负载均衡策略获取最终访问的服务节点然后使用同样配置化的高可用策略对服务端发起点对点的请求。当服务端状态发生变化时注册中心能够实时同步对应变化并通知客户端更新相关的服务信息。
Motan 在服务治理方面通过对服务分组,解决了服务环境隔离、微服务切分、就近访问等问题,引入 Cluster 对服务发现的结果进行抽象,屏蔽了底层对节点的操作,并使用配置化可插拔的负载均衡及高可用策略,完成了对服务端的均衡、高可用调用,注册中心实时维持整个集群的最新状态,提供了自动化的可用性保障,基于注册中心实现的流量控制与指令系统对集群状态的干预提供了支持,也为后来 WeiboMesh 的动态流量调度建立了可编程的操作机制。
<img src="https://static001.geekbang.org/resource/image/df/de/dfa46923f9191518b0c587a10b11c8de.png" alt="" />
扩展性方面,最初的 Java 版 Motan 基于 Java 的 SPI 机制对相关功能做了良好的解耦和模块化拆分,支持对过滤器、高可用策略、负载均衡策略、序列化、传输协议、注册中心、通信机制等进行扩展或二次开发。
Java 版 Motan 作为 WeiboMesh 的先驱,在高压的生产环境下经历了多年的检验和打磨,沉淀了丰富的服务治理和高可用相关经验,形成了事实的微博服务化规范,也为后面的跨语言服务化和 WeiboMesh 奠定了坚实的基础,尤其是跨语言服务化,只需要语言各自使用最适合自己的姿势来实现这些规范即可,服务治理等相关理论和经验完全得到了复用和升华。
## Motan 跨语言
跨语言服务化是 WeiboMesh 探索实践过程中关键的一环,虽然前面 Motan RPC 沉淀的服务治理等相关理论、经验、规范为我们的服务化能力加持不少,但理论、经验、规范都是语言无关的,而跨语言服务化更核心的问题在于与语言特性息息相关的跨语言服务调用和治理。
你可能会问,这里不是在讨论 WeiboMesh 吗?为什么一直扯着跨语言不放?因为在微博场景下,异构系统间服务的通信都是基于语言中立的 HTTP 协议实现 RESTful 接口来完成的,而除此之外微博平台内部还有大量的 RPC 服务也希望能直接暴露给业务方,而不用在前面又包装一层 RESTful 接口。
对于有历史沉淀的团队来说,这些沉淀不一定是包袱,架构取舍的过程中,可能有的部分真是无法放弃或至少是无法立即放弃的,我相信这样的情况不止在微博的场景下会遇到。
Service Mesh 刻画的是一个语言中立的通信和控制层,介于应用层和传输层之间,而 WeiboMesh 始于跨语言服务化,解决了跨语言服务化问题的同时也具备传统 Service Mesh 的服务治理能力,当然也兼顾了微博已有的技术积累。
跨语言服务调用的关键在于解决跨语言通信的问题以及解决异构语言的沟通问题,即如何让语言间互相了解对方。
这里包括通信协议和序列化协议两个方面。
通信协议(比如 gRPC、thrift解决的是服务调用的问题以哪种协议封包传输关注协议的结构及传输、解包效率。
序列化协议则关注相关协议是否为一种语言无关的协议(比如 gRPC 使用的 PB 序列化协议),或者如果是跨语言协议的话,是否对跨语言足够友好,支持足够多的语言数据类型,更多的语言原生的数据结构。
为了以尽可能低的成本达到跨语言服务化改造的目的,我们选择了将第一代通信协议 Motan-1 升级为对跨语言更友好的 Motan-2 协议,由于 Motan 最初基于 Java 开发,默认使用对 Java 对象结构支持更好的 Hessian 序列化协议,当时为了达到更高的传输解析性能, Motan-1 协议与序列化协议有些耦合的地方,现在为了对跨语言支持更友好,在 Motan-2 中我们将其与序列化协议做了彻底的解耦,并且将序列化协议升级为语言中立的 Simple 序列化协议。
Motan-2 使用经典的 header-payload 消息模型来描述请求,而 Simple 序列化通过对简单的数据类型以及多参数支持来满足请求 body 的序列化需求。
<img src="https://static001.geekbang.org/resource/image/0b/e0/0b5eb04c4661da6abfb3feebe9f22be0.png" alt="" />
跨语言 RPC 的另一个核心在于跨语言服务的治理,以往异构系统的服务依赖 RESTful 接口,服务发现和治理由部署在集群前端的四、七层组件来保障,到达集群的请求并不知道自己应该由哪个结点处理。就像你去医院看病挂号,挂完号后并不知道要去哪个科室找医生,而是把号交给分诊台,分诊台的护士帮你分配医生,告诉你去哪个科室。
这种层层转发的请求处理形式,属于 Server 端的服务治理范畴。而基于 RPC 的系统则不然Client 依赖哪些服务事先是明确的,启动时已经订阅了这些服务,运行过程中会有服务发现进程(或线程)实时从注册中心将所依赖服务的最新集群信息同步过来。
这就像你去某个地方旅游,怎么去,出门前已经查好,买好了票到时候直接去就可以。直接发起点对点通信,是 Client 端的服务治理范畴Motan Java 版 Client 实现了完备的服务治理功能,但是不是所有语言实现起来都如 Java 这样方便。
抛开语言特性带来的复杂度不说,每种语言都需要实现一遍业务无关的服务治理逻辑,显然太粗犷,太浪费。
这就是为什么需要把这些业务无关而所有业务又都需要的通用逻辑单独提出来的根本原因,而 Service Mesh 更巧妙的一点在于不但把这部分逻辑从业务代码中移出去,而且是在原有的传输层和应用层之间抽象了一层,将这部分功能以基础设施的角色独立提供出来,并且对业务透明化,这也是 Service Mesh 理念的精华所在。
我们在微博跨语言服务化改造过程中深谙这种理念,所以 Motan 在服务治理和跨语言方面的积累最终得以演化成了 WeiboMesh。至此大家应该对 WeiboMesh 落地过程中的一些架构取舍有了更多的认识。
下一篇文章中,我将对 Service Mesh 的实现规范以及 WeiboMesh 核心的 Mesh 层组件实现做一些简要分析,希望能帮助你基于 WeiboMesh 对 Service Mesh 理念有一个更全面的理解。
阅读过程中你有哪些问题和感悟?欢迎留言与大家交流,我们也会请作者解答你的疑问。

View File

@@ -0,0 +1,74 @@
## 内容概要
本文主要分析 WeiboMesh 在 agent 方面的实现,如何结合各种注册中心来实现 WeiboMesh 的 agent。
前面我们谈到 WeiboMesh 源自我们对跨语言服务化的探索,最终我们通过在传输层和应用层之间增加对业务透明的 Mesh 层来解决大规模异构系统服务通信和治理的问题。
本文我将对这层抽象的核心基础设施层进行简要分析,希望能帮助你基于 WeiboMesh 对 Service Mesh 理念有一个更全面的理解。
## 水到渠成的 Service Mesh
在本系列文章的写作过程中,我详细调研并试用了业界几个重要的 Service Mesh 实现,发现大家基本都是在 2016 年开始做这个事情,由于都是在摸索中前进,所以实现的方式可能不尽相同,但思路和方向却出奇一致。
要知道,从传统应用演化到微服务不容易,从服务化演化出 Service Mesh 方案更不是一蹴而就的事情。
我有幸在微博亲历了这个过程,微博在微服务和服务治理方面已经有多年的技术和经验积累,即便如此,在 WeiboMesh 的演化过程中,依然经过了一个漫长曲折的过程。大家最终都相会于 Service Mesh我认为这绝非巧合。
<img src="https://static001.geekbang.org/resource/image/53/99/533a4079197e2dc00c8d00a8a5906599.png" alt="" />
随着近年来容器、服务编排技术的不断成熟以及云计算技术的迅速普及,越来越多的团队和组织开始基于微服务化和云化的方式构建自己的应用,云原生应用发展方兴未艾,微服务架构将复杂的系统切分为数十个甚至上百个易于被小型团队所理解和维护的小服务,极大简化了开发流程,使得服务的变更、升级成本大大降低,机动性得到极大提高。
但是微服务及云化技术并不是解决分布式系统各种问题的银弹,它并不能消除分布式系统的复杂性,伴随这种架构方式改变而来的是大量微服务间的连接管理、服务治理和监控变得极其复杂等问题。
这也是为什么 2013 年以来微博完成了基于 Motan RPC 的微服务化改造后,在服务治理方面下了如此大的功夫,最终完成了 Motan 这个服务开发、治理平台的原因。
我们实现了各种负载均衡、服务发现、高可用、动态路由、熔断等功能,花很大力气解决了微服务治理的问题,最后发现在跨语言服务化中同样需要解决这些问题,于是演化出 WeiboMesh 这一层作为统一解决这些问题的最终方案。
结合微博的经历和社区其他 Mesh案例不难得出微服务的下一步就是 Service Mesh 这样的结论,同时 Linkerd 和 Envoy 等 Service Mesh 产品加入 CNCF Cloud Native Computing Foundation 云原生计算基金会)的事件,也从另一个角度证明了社区对 Service Mesh 理念的认可。
<img src="https://static001.geekbang.org/resource/image/47/b2/47aead7f5b5c1bdcfb251ce0e2784ab2.jpg" alt="" />
**图片来自 Service Mesh 社区敖小剑老师**
现在社区有观点把 Envoy、Linkerd 这种 SideCar 模式的代理实现看作第一代 Service Mesh把 Istio、Conduit 看作第二代 Service Mesh第二代 Service Mesh 除了使用 SideCar 代理作为数据面板来保障数据可靠性传输外,还通过独立的控制面板做到对服务的遥测、策略执行等精细控制。
这里我们先不关心目前是第几代,因为从生产效果来看,虽然 ServiceMesh 给我们带来诸多好处,但目前各家仍处于一个比较初级的阶段。我们更应该关注 Service Mesh 实现功能的边界也就是Service Mesh 到底要解决哪些问题?
Service Mesh 通过在传输层和应用层间抽象出与业务无关、甚至是对其透明、公共基础的一层解决了微服务间连接、流量控制、服务治理和监控、遥测等问题。
社区对这个边界的认知是基本一致的。不管是 Istio、Conduit还是华为的 ServiceComb、微博的 WeiboMesh实现方案中都有数据面板和控制面板这样的概念这已经形成了 Service Mesh 的事实规范。
<img src="https://static001.geekbang.org/resource/image/38/b2/38c150993c199bfc8235a658351b07b2.png" alt="" />
数据面板主要以 SideCar 模式与 Service 部署在一起,保障请求数据在 Service 间进行可靠传输,提供服务治理以及对请求状态数据的提取与检测等。而控制面板主要控制服务间流量流向、请求路由,提供一种对数据面板访问策略实施的干预机制以及对服务依赖、服务状态的监控等功能。下面我们就从这两个角度来看看 WeiboMesh 是如何做的。
## WeiboMesh 的数据面板
Motan-go 的 Agent 作为 WeiboMesh 的数据面板,由 Server Agent 和 Client Agent 两部分组成Server Agent 作为服务提供方的反向代理使用Client Agent 则作为服务依赖方调用服务的正向代理,使用时只需配置对应的 Service所提供的服务和 Referer所依赖的服务即可两个角色的功能由同一个 Agent 进程提供,只是执行过程中使用不同的配置段而已,所以当需要更新升级的时候,可以完全独立于应用进行,发布的方式也比较灵活友好。
熟悉 Motan 的同学应该对这些配置项(比如 Service、Referer 等不陌生。Agent 以 SideCar 模式与 Service 部署在一起Agent 之间通过 Motan 协议进行通信(这里指 Motan-2 协议),而 Service 与 Agent 间可以使用自己喜欢的方式来进行通信,目前默认也是推荐的方式是 Service 通过 Motan-Client 与 Agent 通信,不过我们同样支持 gRPC、HTTP 等,如果需要支持其他协议也很简单,因为 Motan 中协议的扩展是特别方便的。
<img src="https://static001.geekbang.org/resource/image/d7/d3/d7eb09220ecf69ee6052974184f66bd3.png" alt="" />
这种通信模式下 Server Agent 相当于原来 Motan Server 的角色Client Agent 相当于原来 Motan Client 的角色,而 Agent 间调用就变成普通的 Motan 请求。
所以我们很自然地复用了 Motan 原有的服务治理体系,比如服务注册与发现、负载均衡、高可用、服务监控、跟踪、依赖关系刻画、指令调度系统等。如果以后需要扩展其他的策略或功能,也只需要在这一层扩展即可,这也就是 Service Mesh 的奥妙所在吧。
## WeiboMesh 的控制面板
WeiboMesh 的控制面板前身是 Motan 的指令控制体系为了实现对服务流量的切换、降级、分组以及支持各种发布方式Motan 原本就结合注册中心实现了一套完善的指令控制系统,以提供一种对流量进行干预的机制。
WeiboMesh 基于这种机制并配合决策系统实现了对流量的动态控制和策略执行,同时又扩展了 Filter 机制,实现了身份验证、安全管理、监控和可视化数据收集等,收集到的遥测数据实时反馈到决策系统作为决策依据。
这里我们以一次由 IDC-A 流量暴涨导致其超负荷运转而需要将 20% 的流量自动切换到 IDC-B 为背景来描述下 WeiboMesh 控制面板在流量自动调度这方面的运行方式。
WeiboMesh 启动时除了从注册中心订阅相关依赖的服务外,还会订阅各种控制策略执行所需的指令,双向 Agent 作为数据面板承接了所有服务的访问、请求的流入流出,掌握所有请求的实时数据、请求状态,并通过 Metrics 等组件将这些状态和数据推送到决策系统(包括动态流量调度和服务治理管理体系),决策系统通过对不同类型服务预先建立的容量评估模型,实时计算出集群当前容量状态,当发现 IDC-A 的容量超过容量预警时会自动生成相关的流量切换指令并将生成的指令同步到注册中心Client Agent 的服务发现模块将会实时同步更新该指令并触发 Cluster 更新所持的 Endpoint 列表,请求调用过程中负载均衡器就会根据指令将 20% 的 IDC-A 的流量自动路由到 IDC-B。
<img src="https://static001.geekbang.org/resource/image/73/17/73284623e7ffb2eb53b40173ab44ef17.png" alt="" />
WeiboMesh 就是这样通过 Agent、注册中心、决策系统在传输层和应用层间实现了对请求的高效稳定传输以及服务的统一治理。
下一篇文章中,我将从实践角度以一个请求处理为例,简要分析 WeiboMesh 在 Service Mesh 规范的实现中一些具体细节处理。希望能帮你更好地入门 WeiboMesh 以及 Service Mesh。
阅读过程中你有哪些问题和感悟?欢迎留言与大家交流,我们也会请作者解答你的疑问。

View File

@@ -0,0 +1,158 @@
## 内容概要
本文主要分析 WeiboMesh 在运行阶段请求路由的实现,对比现有的通用 Service Mesh (比如 Istio )在这方面的不同。
前面我们对 WeiboMesh 的演化、实现以及 Service Mesh 规范等做了简要分析和解读,希望能帮助你基于 WeiboMesh 更好地理解 Service Mesh 的架构理念。
这里我们从实践的角度来体会下当前最受关注的 Service Mesh 实现 Istio 和 WeiboMesh 在服务调用和请求路由方面的一些异同,希望对你入坑 Service Mesh 有所帮助。
WeiboMesh 源自于 Weibo 内部对异构体系服务化的强烈需求以及对历史沉淀的取舍权衡,它没有把历史作为包袱而是巧妙地结合自身实际情况完成了对 Service Mesh 规范的实现,与同时期的其他 Service Mesh 实现相较而言WeiboMesh 以最接地气的 Service Mesh 实现著称并不无道理。
如果你的团队拖着繁重的技术债务,又想尝试 Service Mesh 理念带来的诸多好处,那 WeiboMesh 应该能为你带来些许灵感。下面就让我们一起来体验一下。
## WeiboMesh 的服务注册与发现
由微博开源 Motan RPC 框架演化而来的 WeiboMesh 服务注册与发现功能扩展自 Motan 相关功能,机制和用法基本不变,流程同样是服务启动的时候将自己的分组、服务名称、版本等等注册到注册中心。
稍微有点区别的可能在于扩展了具体执行注册和发现操作的角色WeiboMesh 的服务由 Server Agent 完成注册,而通过 Client Agent 完成对依赖服务的订阅与发现。Mesh 层接管了服务进出的流量,那请求又是如何在 WeiboMesh 中流转的呢?
## 请求在 WeiboMesh 中的流转
我们以 WeiboMesh 的示例项目([github.com/motan-ecosystem/motan-examples中各语言的](http://github.com/motan-ecosystem/motan-examples%EF%BC%89%E4%B8%AD%E5%90%84%E8%AF%AD%E8%A8%80%E7%9A%84) HelloWorldService 作为服务网格中的各种相互依赖的异构服务,基于 zookeeper 注册中心,看下一个请求是如何在 WeiboMesh 中流转的。假定我们有个 Test 业务motan-examples/php/motan.php它依赖由 OpenResty 提供的一个 HTTP 服务 [http://10.211.55.3:9900/http_server](http://10.211.55.3:9900/http_server%EF%BC%89%E3%80%82)
<img src="https://static001.geekbang.org/resource/image/fb/3f/fb057dae851374fe4eb2ac04cc38423f.png" alt="" />
作为服务提供方HTTP 服务一侧的 Server Agent 将 HTTP 接口导出为 WeiboMesh 集群中的 HelloWorldService并将其注册到 zookeeper而依赖此服务的 Test 业务一侧的 Client Agent 订阅了这个 HelloWorldService 服务作为自己的一个 referer并将服务的节点实时发现回来Test 业务只需要对本地的 Client Agent 发起调用即可完成对 HelloWorldService 的依赖,而由 Client Agent 与对端的 Server Agent 发起点对点通信,下面我们通过抓包来演示请求的流转。
<img src="https://static001.geekbang.org/resource/image/2d/56/2dc5607bc836a9a907bc2b389c790856.png" alt="" />
图中 z2.shared 为 Test 业务部署的节点,而所依赖的 HTTP 服务 [http://10.211.55.3:9900/http_server](http://10.211.55.3:9900/http_server) 部署于 z.shared 节点,可以看出 Test 直接请求本机的 9981 Mesh 端口Client AgentClient Agent 又对远端的 Mesh 端口Server Agent9990发起直连调用。
下面是将 HTTP 服务导出为Motan RPC 服务,并暴露到 WeiboMesh 集群 的相关 Server Agent 配置,可以看到其将 HTTP 接口导出为 motan2 协议 的 Motan RPC 服务,并在 9990 端口提供服务。
```
http-mesh-example-helloworld:
path: com.weibo.motan.HelloWorldService
export: &quot;motan2:9990&quot;
provider: http
registry: &quot;zk-registry&quot; # registry id
HTTP_URL: http://10.211.55.3:9900/http_server
basicRefer: mesh-server-basicService
```
WeiboMesh 又是如何实现控制面板来对请求进行精细控制的呢?
目前主要由指令管理系统motan-manager提供包括 RPC 服务查询、流量切换、Motan 指令设置等功能)、决策系统(提供遥测数据搜集、自动化扩缩容并配合指令系统实现流量弹性调度等功能。
这部分为微博内部功能,目前暂无开源实现)还有各种过滤器(请求验证等功能推荐通过 Motan 过滤器机制来扩展实现)等模块来完成各种策略执行、验证、流量管理等功能。以流量切换为例,我们只需要执行如下指令就可以完成对流量的比例切换,由此可见 WeiboMesh 的使用成本是极低的。
```
{
    &quot;clientCommandList&quot; : [
        {
            &quot;index&quot;: 1,
            &quot;version&quot;: &quot;1.0&quot;,
            &quot;dc&quot;: &quot;yf&quot;,
            &quot;commandType&quot;: 0,
            &quot;pattern&quot;: &quot;\*&quot;,
            &quot;mergeGroups&quot;: [
                &quot;openapi-tc-test-rpc:1&quot;,
                &quot;openapi-yf-test-rpc:1&quot;
            ],
            &quot;routeRules&quot;: [],
            &quot;remark&quot;: &quot;切换50%流量到另外一个机房&quot;
        }
    ]
}
```
我们再来对比看看其他有代表性的 Service Mesh 实现是如何复杂的,比如 Istio。
众所周知 Istio 在 Service Mesh 领域可谓是一枝独秀,出身名门的光环使大家对它期待有加,虽然从 2017 年 5 月 24 日Google、IBM 联合 Lyft 公司共同宣布 Istio 项目以来Istio 尚未给出一个可以生产验证的版本,但是这并不影响社区对它的强烈兴趣。
不过 Istio、Conduit 等项目的出现是对 Service Mesh 理念的认可,也进一步促成了数据面板和控制面板作为 Service Mesh 主体功能这一事实规范的形成。
Istio 借助一个 SideCar 模式的 proxy 作为数据面板接管了容器中业务进出流量,而又实现了控制面板来对 proxy 进行易于扩展的细粒度控制,完成应用流量的切分、访问授权、遥测、策略配置等一系列服务治理功能。
下面我们就根据 Istio 的示例项目 bookinfo 来分析一个请求在 Istio 内是如何流转的Kubernetes 平台作为 Istio 默认使用的平台也是支持最完整的平台,所以本文下面的分析都基于 Kubernetes 平台进行,且 Kubernetes 为基于 kubeadm 在裸机部署)。
## Istio 服务的注册与发现
我们知道 Istio 假定存在服务注册表,以跟踪应用程序中服务的 Pod/VM它还假设服务的新实例自动注册到服务注册表并且不健康的实例将被自动删除。
诸如 KubernetesMesos 等平台已经为基于容器的应用程序提供了这样的功能。也就是说Istio 本身并不直接提供对服务的注册和发现功能,它依赖于部署的平台,那它又是如何依赖平台来实现的呢?
我们先来看看原生的 Kubernetes 平台是如何实现服务注册和发现的。
当我们执行 kubectl 命令部署服务时Kubernetes 会根据 Pod 调度机制在相应的节点上部署相关的 Pod并根据 Service 的部署规范描述部署对应的服务,同时新增部署的服务状态会通过 Kubernetes 的 kube-api-server 注册到中心的 Etcd 集群,又由 Kubernetes 的 List-Watch 机制将相关变更同步到各模块,实现机制是典型的发布订阅模型。
其默认的服务发现组件 kube-dns 中的 kube2sky 模块则通过 watch kube-api-server 来对 Kubernetes 集群中的 Service 和 Endpoint 数据进行更新和维护。请求调用时,对应的 DNS 请求被 kube-dns 中的 dnsmasq 接收,走 DNS 解析机制完成服务发现逻辑。
再看 Istio 的情况Istio 服务部署前需要执行 Istioctl kube-inject 在 Pod 中注入 Istio 相关支持,这里一个关键的目的就在于以 SideCar 模式注入 Envoy 代理,而这其中有一步基础的初始化工作就是在 SideCar 容器 Istio-proxy 中添加相关的 iptables 规则,将所有服务相关的进出流量都拦截到 Envoy 的处理端口,这是 Istio 服务注册和发现的关键一步。
Istio 服务部署过程中除了依赖 Kubernetes 平台将服务注册到注册中心,并将状态同步给相关组件,还将 Istio 集群所有的流量都归一到 Envoy 代理处理,由 Envoy 去请求所依赖的服务,代理过程中 Envoy 通过 Pilot 组件的各种服务发现接口来完成服务发现的逻辑。
而 Pilot 正是 Istio 对各平台的服务注册和发现功能做平台无关抽象的核心组件。服务网格中的 Envoy 实例依赖 Pilot 执行服务发现,并相应地动态更新其负载均衡池。
Istio 依赖平台完成服务注册及状态变更,请求过程中服务的发现依赖于 Pilot这是 Pilot 主要职能之一,这部分的实现是为了平台透明化而做的解耦,我的理解是一方面出于开源通用性方面的考虑,一方面也来自于大厂对云原生市场前景的看好与抢占吧。
而 WeiboMesh 作为微博生产的实现,目前并没有对那么多平台适配的需求,我们只需直接扩展对各个注册中心的支持即可。好处是更简洁,直接。而且现实场景下,除非是新启动的项目,否则很难完全做到云原生,不过大家都在往这个方向走是确定的,所以我们也计划后期对各种平台做支持,希望大家共同期待、共同参与进来。
## 请求在 Istio 集群中的流转
仍然以裸机 Kubernetes 平台部署 Istio 为例,我们在此前基础上部署 Istio 测试服务 bookinfo。下面是相关的部署情况节点概况、Pod 分布以及服务部署):
<img src="https://static001.geekbang.org/resource/image/e4/d1/e4e7a25cf6f837cc0cfe8244c1bdecd1.png" alt="" />
因为我们是裸机部署,没有 LoadBalance 的支持,所以我们根据 Istio-ingress 的设置,可以使用 NodeIP + NodePort 的方式Pod Istio-ingress-6ff959f7ff-8cz8x 所在节点为 kube-node2ip 为 10.211.55.76,而 Service Istio-ingress 80 端口服务对应的 NodePort 为 32370来访问测试服务 productpage [http://10.211.55.76:32370/productpage](http://10.211.55.76:32370/productpage) 下面我们看看这个请求在 Istio 集群中是如何流转的。
通过 Kubernetes ingress 机制,我们得以从集群外访问 Istio 集群的服务。在任意一个节点我们可以查看到 32370 端口相关的 iptables 规则,可知访问这个端口的流量最终会指向 10.244.1.158:80 而 10.244.1.158 是 Istio-ingress 所在的 Pod 节点,具体的实现细节属于 kube-proxy 模块的内容,感兴趣的同学可以详细了解一下。
<img src="https://static001.geekbang.org/resource/image/f9/43/f9526edd56af81efacee59c279c08e43.png" alt="" />
而请求到了 Istio-ingress Pod 10.244.1.158 对应的 Pod 是 Istio-ingress-6ff959f7ff-8cz8x我们进入这个 Pod 的容器可以查看到里面运行的 Pilot 和 Envoy 代理。
ingress Pod 中的 Envoy 代理与 Service 中的 Envoy 代理角色不同,这里 Envoy 的角色是 ingress-controller所以可以看到 Envoy 监听了 80 端口来直接接受请求80 端口接受外部请求转发到集群内,而 15000 端口与其他 Service SideCar 场景一样,作为管理端口)。
<img src="https://static001.geekbang.org/resource/image/41/5b/41173645767808153e62ad95e4c05b5b.png" alt="" />
Envoy 通过与 Pilot 的服务发现服务持续通信维持了一个实时可用的 Cluster如下图
<img src="https://static001.geekbang.org/resource/image/60/aa/60d45296c714c34c9e44cf8503a514aa.png" alt="" />
Envoy 通过与 Pilot 提供的各种发现服务通信,比如通过 RDSrds/v1/routes/80 获取 80 端口正真提供服务的集群,通过 CDScds/v1/clusters/istio-proxy/ingress~~istio-ingress-6ff959f7ff-8cz8x.istio-system~istio-system.svc.cluster.local 获取该集群提供服务的服务名,通过 SDSsds/v1/registration/productpage.default.svc.cluster.local获取服务对应的 Endpoint完成服务定向10.244.1.162:9080我们发现 10.244.1.162 正好是 productpage 服务所在的 Pod productpage-v1-d759956f4-xmhm5 节点 IP而 9080 端口正好是 productpage 服务提供的端口)。
当然在请求真正从 Istio-ingress 节点转发到 productpage Pod 的时候,并没有直接提供服务,入口流量打到 productpage 的 Envoy 代理Envoy 将请求数据首先发往 Mixer 组件中的 Check &amp; report 服务进行相关的验证和数据上报,通过后请求才会真正的被处理。
<img src="https://static001.geekbang.org/resource/image/37/d1/37ebdd873fcadac9ff06da9e76af3ad1.png" alt="" />
图中 Istio-ingress10.244.1.158)到 productpage10.244.1.162)的请求,先是发往 Mixer 的 check &amp; report 服务10.105.94.107:9091才由本 Pod 的9080真正的 productpage 服务)处理。
<img src="https://static001.geekbang.org/resource/image/59/cb/59f491da1e77c360b5f2055d91ea6bcb.png" alt="" />
现在请求已经到了基于 Python 开发的 productpage 服务,而这个服务依赖于 reviews、details 等服务,这部分请求又是如何流转的呢?我们这里以 productpage 对 details 服务的依赖为例进行说明。
productpage 和 detail 都是 Istio 网格中的服务,他们各自的 Pod 都以 SideCar 模式部署了 Envoy所有的进出流量都经过 Envoy 代理productpage 通过服务名称 [http://details:9080/details/0](http://details:9080/details/0) 访问 detail 服务detail 服务的发现是基于 kube-DNS 解析完成的。我们可以通过 nslookup 命令来复原这一点。
<img src="https://static001.geekbang.org/resource/image/2e/d1/2e0b35ebb5d4a031892a2a4504fea8d1.png" alt="" />
而 productpage 对 detail 服务的调用地址明确指出依赖 detail 服务的 9080 端口。
<img src="https://static001.geekbang.org/resource/image/74/56/7466a326fdb9802d2d249f317da9e156.png" alt="" />
所以 productpage 最终依赖的 detail 服务地址为 10.103.96.223:9080而往出的流量被 envoy 接管,所以对 detail 服务的调用由 envoy 代理发出并完成。从这些请求的分析大家不难窥探出 Istio 集群中服务间的依赖体系以及数据面板控制面板在这个过程中起到的关键作用。
我们这里从请求流转的角度分析了一个请求在 Istio 和 WeiboMesh 中是如何处理的,不难发现 WeiboMesh 在严格遵循 Service Mesh 的规范和理念的同时,是一个入门更方便而且使用更简单的 ServiceMesh 实现,它来自微博架构的实战和迭代演进。
而 Istio 我个人感觉相对较为复杂些因为面对繁荣的原生云市场Istio 为了支持各种平台、环境的部署,需要抽象和拟合的元素实在太多,这极大增加了整体复杂度,也对性能造成不少损失。
而且 Istio 对请求的控制依赖于每次与 Mixer 的交互,其网络消耗对系统整体性能影响极大,部署一组 Istio 集群就要部署一堆 Pod这些都是看得见的复杂度想满足大众的口味就难免众口难调这点大锅饭肯定比不上私房菜好吃。
但是 Istio 也在不断地更新,我们期待它在不远的将来能带来更精简的部署和更高的性能,不过这同样也是 WeiboMesh 所追求的,大家互通有无,一起成长。
通过上面的分析,你应该对这两款 Service Mesh 实现有了更深入的认识,不过你可能会有些疑问,为什么 WeiboMesh 的组件中SideCar 代理有两个确切的是说是一个代理的双重角色Server Agent 和 Client Agent还有 WeiboMesh 集群中Mesh 层是通过 Motan 协议来交互的,这样的设计的出发点是什么呢?下一篇中我们将围绕这些问题进行细节阐述。
阅读过程中你有哪些问题和感悟?欢迎留言与大家交流,我们也会请作者解答你的疑问。

View File

@@ -0,0 +1,74 @@
## 内容概要
本文主要分析 WeiboMesh 在改造过程中对历史积累的一些考量以及适配,还有我个人对面向未来架构的思考。
从 SideCar 模式初具雏形的 2016 年末开始WeiboMesh 就已经在生产环境逐步被摸索和打磨,并被大范围验证,这也是为什么 WeiboMesh 是目前最接地气的一个 Service Mesh 实现的原因所在。
它本身源自于微博内部对整体服务化的迫切需求而面对微博巨大的流量和各业务线千差万别的异构服务现状WeiboMesh 走出了一条适合自己也适合像微博这种在服务化进程中有着沉重历史包袱的团队。
## WeiboMesh 的独到之处
WeiboMesh 实现了当前 Service Mesh 的事实规范,在架构中抽象的一层实现了 Service Mesh 的数据面板和控制面板,二者很好地解决了请求的可靠传输和统一服务治理问题。
那 WeiboMesh 同其他 Service Mesh 实现相比又有哪些独到之处呢?我们下面从面临的问题和解决的思路两方面来简要分析。
伴随着微服务化和云化技术的普及,服务间的通信方式被重新定义,数据的可靠传输以及统一规范化的服务治理是大家必须面对的问题。如何应对这些问题?
微博多年来积累了一套基于 Motan RPC 和混合云的完备服务化体系,基于我们面临的问题和当时的现状考虑,解决思路很简单,通过 RPC 跨语言来完成数据的可靠传输及服务间的依赖调用,同时复用我们强大的 Client Side 服务治理体系来实现服务的统一规范化治理,以保障服务的高可用。
但是在演进的过程中发现跨语言 RPC 的支持其实没有那么大的困难,反倒是在各种语言的 Client 端都要实现 Client Side 的服务治理体系,难度超乎想象。于是我们削薄了集成了复杂治理功能的 RPC Client将这些通用的服务治理功能抽象到了 WeiboMesh 中统一实现。
<img src="https://static001.geekbang.org/resource/image/ce/0a/ceaab06e118f9ac24abfde9b4711160a.png" alt="" />
这样一来,我们兼顾了微博平台原有的 RPC 服务体系服务治理功能得到很好的复用Client 对依赖的服务通过 Mesh 层本地调用实现,我们在 Mesh 层支持了常见的协议比如 HTTP、gRPC 等,让不同的语言选择自己喜欢的方式来调用。
而且这样有个好处就是我们去除了中间的层层转发,因为业务对自己所依赖的服务是明确的,你只需要在 Mesh 的配置文件里添加相关的依赖,服务发现、调用、治理等事情都交给 Mesh 层统一完成就好。
而 MeshClient Agent 层又与所依赖的 Service 对端的 Mesh Server Agent通过 Motan RPC 完成请求调用,像我们以往擅长的那样。这种巧妙的 Motan-Client 调用实现就是我认为 WeiboMesh 独到的地方。
## Motan-Client
那这个削薄了 Motan-Client 是一定必要的吗?答案是否定的,我们奉行“适合自己的才是最好的”这样的原则。如果要让 WeiboMesh 支持其他 Mesh 实现的调用方式也很简单,只需要在框架层面对 Motan-Agent 进行简单封装即可,所以不一定需要这个 Client但我们的场景下这种方式是最适合的。
一方面主要是出于成本的考量,因为基于微博现状,我们平台内部更多的服务都是通过 RPC 服务来提供的,而对外提供的服务是 RESTful 接口,这样就会存在同样一个服务需要同时维护两个表现层,维护成本极高。
而 RESTful 接口的请求链路长,尤其是在我们升级为混合云架构以后,经常有请求在公私有云间穿梭的情况出现,取一条微博列表动辄上百 K 的数据,带着繁重的 HTTP 头,经过层层转发,性能损耗严重,带宽资源浪费,而且问题排查也很困难。
我们希望改变这种现状,统一只提供一种服务,面对 RPC 和 HTTP我们选择了前者。
精简的自定义协议,高度可编程和可定制化,另外 RPC 实现点对点的通信,去除了之前的层层转发,性能、服务可用性保障都有了质的飞跃,问题排查也更明确、简单。
为了更低的成本把原来的 RESTful 接口改造为 RPC 服务,我们需要一个更精简的 Motan-Client 来替换之前的各种语言所使用的 HTTP-Client。
另一方面,基于架构的考虑,虽然我们也可以做到不需要这个 Motan-Client因为这个精简的 Client 目前最大的作用就是用来发 Motan 协议的请求,我们完全可以在 Mesh 层来支持 HTTP 协议,这样之前使用 RESTful 接口的业务方就可以不改一行代码,而全部交由 Mesh 层处理。
但是我们只解决了眼下跨语言服务对业务服务依赖的问题,那资源层面呢?大家可以想象一下以往你的代码里面有多少各种各样的 Client
Memecache Client、Redis Client 各种语言都有多种实现,选择使用哪个 Client 是个问题,使用过程中的一些问题排查、检测等也都是问题。
如果都统一服务化呢?关注微博技术的同学可能知道,我们内部有 CacheService我们不去纠结你要用哪种你申请完直接用就是了。所以我们需要一个通用的 Client 来统一业务对各种服务的依赖调用,这就引出下一个话题,面向未来的架构。
## 面向未来 / 服务的架构
服务化和微服务之所以深得人心,是因为它给我们解决了很多之前头疼的问题,它完美适应了云计算时代的开发方式,改善了工程师的开发体验。
我们提倡面向服务的架构,把一切依赖的数据接口、资源都服务化后,未来在维护、运营方面就能极大的统一化、规范化,从而降低成本、提高效率,这也就是面向未来的架构。
我们希望能用一个 Client 来完成所有的服务、资源的依赖调用,而在 Mesh 层对这些资源、服务统一进行治理、认证、监控、遥测。把以往散落在各种业务代码里面那些与业务无关的通用逻辑都提到 Mesh 层来统一处理。这是我们面向未来的泛服务化构想,我们也在往这个方向上积极探索。
Motan-OpenResty 就是我们对泛服务化构想的一个探索之一,对于业务依赖接口的服务化,我们可以通过对 Mesh 进行注册和发现来解决,请求是直接的、点对点的,但是对于资源,比如对 Memcache 的一致性 HashMemcache 不会把自己注册到注册中心Mesh 作为业务无关的中间件不应该去关注你是一致性 Hash 还是用的 Crc32 取模 Hash这就需要有资源抽象的中间件作为资源的 edge 代理。
OpenResty 作为 Nginx 最强大的衍生版本提供了对四、七层友好的扩展能力Motan-OpenResty 基于 OpenResty Stream-Lua 模块开发,实现了对 Motan 协议的高效封装与代理。我们只需要基于此实现相关的资源逻辑以及资源协议转换就能轻松完成对资源的服务化。这就是泛服务化的妙趣所在。
## 结束语
到此,我们本系列的几个议题就讨论完了,由于个人能力有限,如果有不妥当的地方,还望指正.我们从微博单体服务到 WeiboMesh 的演进,从整体感观上给大家描绘了一个 WeiboMesh 的路线图,不管你是处于单体服务、服务化、微服务、容器化等任何阶段,希望这些探讨能与你产生共鸣,最好是能对解决你的问题有所帮助。
我们同样对 WeiboMesh 几个核心的点分别进行了探讨比如跨语言服务化的关键技术、WeiboMesh 对 Service Mesh 规范的理解、双向Server Agent、Client Agent代理的角色和功能、Motan-Client 在 WeiboMesh 中的关键作用和泛服务化的理念等。
我们也放眼业界,通过实例分析对比了 WeiboMesh 和 Istio 在处理请求过程中的一些区别,希望大家能通过这些议题的探讨对 Service Mesh 能有更深入的认识和了解,最好能直接上手体验下,也期待大家更多的关注 WeiboMesh ,让我们能得到更多社区的反馈,帮助我们做得更好。
希望大家一起努力,共同进步,感谢各位!
阅读过程中你有哪些问题和感悟?欢迎留言与大家交流,我们也会请作者解答你的疑问。