JervyShi`s Blog

蚂蚁金服 Service Mesh 落地实践与挑战

本文整理自 GIAC(GLOBAL INTERNET ARCHITECTURE CONFERENCE)全球互联网架构大会,蚂蚁金服 Service Mesh 落地实践与挑战分享。分享基于 Service Mesh 的理念,结合蚂蚁金服内部实际场景,将中间件、数据层、安全层等能力从应用中剥离出来后下沉至独立的 Sidecar SOFAMosn 中,结合 Kubernetes 运维体系,提供应用无感知的情况下升级基础设施层能力的案例。

本次分享将从以如下次序展开进行:

图1. 分享大纲

蚂蚁金服当前的服务化现状

在看蚂蚁金服的服务化架构之前我们先从一个简单的服务化调用示例说起,下图是 SOFARPC 基本原理:

图2. SOFARPC 基本原理

我们从上图可以看出,构建一个服务化框架需要有服务注册中心,有服务定义,调用方和服务提供方使用相同的服务定义来互相通讯。通过服务注册中心,调用方可以直接订阅到服务提供方的地址,采用点对点的方式直接发起请求。客户端内可实现服务发现、路由寻址、负载均衡、限流熔断等能力来增强服务通讯能力。通过我们开源的 SOFARPC、SOFARegistry、SOFABoot,用户已经可以直接构建起微服务体系,助力业务发展。

蚂蚁金服发展至今,双 11 系统需要应对的交易洪峰逐年递增:

图3. 历年双 11 交易额与峰值数据

每秒 26.5 万笔交易是 2017 年双 11 的峰值数据,这个数据背后有非常复杂的架构支持,LDC 单元化架构是蚂蚁金服沉淀多年的核心架构,依靠这个架构实现每年峰值交易量飞速增长下系统依然能平滑渡过。我们来简要看下 LDC 架构:

图4. LDC 架构示例

上图摘自 金融级分布式架构 中的 素描单元化 一文,这里不详细展开。LDC 的单元化架构给应用的服务化带来更多的规范与抽象,服务路由中需要考虑单元间的调用,跨机房调用等更多场景。这里主要希望表达的是 LDC 架构给 RPC 调用带来更高的复杂度。

服务化痛点

中间件版本升级

在上面介绍背景时,有介绍到目前 LDC 架构下服务调用的复杂度,这些复杂度目前是直接体现在应用的代码中。对于业务同学来讲,一个应用的关注重点是如何实现业务逻辑,至于高可用、容灾等能力更多是整体架构层面会考虑的点。应用内通过引入 RPC 的 jar 包即可获得 LDC 架构下服务调用各种能力的支撑,带来便利的同时也可以看到这种模式的缺点:

图5. APP 业务与 SDK 组成部分

应用内除业务逻辑之外,由中间件的 SDK 引入大量外部依赖,来完成服务发现、路由寻址、负载均衡、限流熔断、序列化、通讯等能力,每个组件的引入都可能带来稳定性风险,以及更高的升级成本。

图6. SDK 内不同能力对应的依赖

根据目前 SOFARPC 在内部的版本举例,服务发现依赖 SOFARegistry 的客户端做 IDC 内的服务发现,依赖 Antvip 做跨 IDC 的服务发现,ZoneClient 集成 LDC 架构的单元化信息,路由寻址需要根据请求的入参计算目前 Zone 然后确定调用目标,限流熔断依赖 Guardian 组件,通讯协议与序列化协议相对稳定,变更较少。仅为了完成服务调用,应用需要额外引入 7+ 客户端包。

图7. SDK 升级数量

每年双 11 需要涉及到架构调整时:比如支持弹性架构,需要做很多中间件客户端的版本升级来支撑更优的架构,对于业务同学来讲,这些升级是很耗费精力的,拿 200 个核心应用举例,每个应用升级中间件版本经过研发、测试、再到部署预发、灰度、生产等环境需要 5个人日的话,200 个核心应用中间件升级需要耗费 1000 人日,如果这部分时间可以节省出来,每年架构升级可以节约大量人力资源。

跨语言通讯

蚂蚁金服发展至今,内部业务百花齐放,搜索推荐、人工智能、安全等各种业务使用到的技术栈非常多样化,跨语言的服务通讯能力也十分重要。早在几年前,Java 之外规模最大的就是 NodeJS 应用,为了让 Java 和 NodeJS 应用之间可以复用蚂蚁金服内部的各种中间件和基础设施,前端团队使用 NodeJS 逐步重写了各种中间件客户端,让整个 NodeJS 和 Java 体系可以完美互通。

图8. Java 与 NodeJS 互调

中间件 SDK 跨语言重写与维护成本极高,随着语言种类的增多,跨语言通讯的诉求也越来越多。

图9. 多语言场景

Java, NodeJS, Go, Python, C++ 等,5+ 语言,中间件 SDK 全部重写成本极高。这些问题不得不激励我们寻找更优的解法。

解决痛点

SDK 能力下沉

图10. SDK 瘦身并下沉能力至 Sidecar

依然以上述 RPC SDK 举例,SDK 中的能力我们可以根据稳定性与不可剥离等特性来看,哪些是可以从应用中抽出来的,尽量把 SDK 做薄,做的足够稳定无需变更,那么升级成本将不复存在。

下载不同服务器相同域名下日志文件

前言

最近遇到一个问题:我们的应用部署在多台服务器上,偶尔需要把日志下载下来分析就需要手动更改本机hosts文件来连接到不同的服务器一遍遍的下载日志。本着懒人原则,如何更高效的从不同服务器上的相同域名上下载日志文件已经成为必须解决的问题。

分析

过程:下载日志文件需要发出一次http请求,如url: http://jervyshi.me/logs/debug.log ,每次http请求都会根据url中的domain去做DNS解析获取到对应的服务器ip,如果我们指定hosts,则在做DNS解析前优先读取hosts中配置的ip,获取到ip之后与对应服务器建立连接然后获取数据。

方案一:如果能根据domain对应的已知服务器列表在每次请求日志文件时候更改hosts文件中此domain的配置可以实现此功能,之前手动操作完全是按照此方法来,缺点很明显下载一个文件改一次hosts。如果用程序实现,在程序下载日志时hosts频繁更改,影响正常浏览器访问,此方案不佳。

方案二:如果能在读取本地hosts文件之前就已经指定ip,显然会方便很多。其实在一次http请求发出时,请求头中会包含 Host 信息,如果url中domain不是ip那么会把domain截取出来放进http请求头 Host 中,如:http://192.168.1.100/logs/debug.log 然后将本次请求的header中增加 Host:jervyshi.me ,这样就可以使程序通过切换ip来访问不同服务器上的domain。

实现

根据方案二,我用python做了一个简单的实现,可以通过配置来达到从不同服务器上获取相同域名下的日志文件并且分类保存。具体代码参见: https://github.com/JervyShi/python-utils