SOFAMesh中的多协议通用解决方案x-protocol介绍系列(1) : DNS通用寻址方案

本文涉及的产品
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
简介: x-protocol 的定位是云原生、高性能、低侵入性的通用 Service Mesh 落地方案,依托 Kubernetes 基座,利用其原生的服务注册和服务发现机制,支持各种私有 RPC 协议低成本、易扩展的接入,快速享受 Service Mesh 所带来的红利。

小蚂蚁说:

2018年上半年,蚂蚁金服决定基于 Istio 订制自己的 ServiceMesh 解决方案,并在6月底正式对外公布了 SOFAMesh 。
在 SOFAMesh 的开发过程中,针对遇到的实际问题,我们给出了一套名为 x-protocol 的解决方案,本文将会对这个解决方案进行详细的讲解,后面会有更多内容,欢迎持续关注本系列文章。


前言

x-protocol 的定位是云原生、高性能、低侵入性的通用 Service Mesh 落地方案,依托 Kubernetes 基座,利用其原生的服务注册和服务发现机制,支持各种私有 RPC 协议低成本、易扩展的接入,快速享受 Service Mesh 所带来的红利。

具体解决的问题包括:

  • 多通讯协议支持问题,减少开发工作量,简单快捷的接入新协议
  • 尽量提升性能,提供更灵活的性能与功能的平衡点选择,满足特定高性能场景
  • 兼容现有 SOA 体系,提供通过接口进行访问的方式,实现不修改业务代码也能顺利接入 Service Mesh
  • 支持单进程多服务的传统 SOA 程序,可以在微服务改造之前,先受益于 Service Mesh 带来的强大功能

在本系列文章中,我们将对此进行详细的讲解,首先是“DNS通用寻址方案”。

SOFA 开源网站:

http://www.sofastack.tech/

背景和需求

SOA的服务模型

在 SOFAMesh 计划支持的 RPC 框架中,SOFARPC、HSF、Dubbo 都是一脉相承的 SOA 体系,也都支持经典的SOA服务模型,通常称为”单进程多服务”,或者叫做”单进程多接口”。(备注:由于服务一词使用过于频繁,下文都统一称为接口以便区分)

SOA 标准的服务注册,服务发现和调用流程如下:

image.png

1.在单个 SOA 应用进程内,存在多个接口

2.服务注册时,以接口为单位进行多次独立的服务注册

3.当客户端进行调用时,按照接口进行服务发现,然后发起调用

当我们试图将这些 SOA 架构的应用搬迁到 ServiceMesh 时,就会遇到服务模型的问题:微服务是单服务模型,也就是一个进程里面只承载一个服务。以 k8s 的服务注册为例,在单进程单服务的模型下,服务名和应用名可以视为一体,k8s 的自动服务注册会将应用名作为服务注册的标示。

这就直接导致了 SOA 模型和微服务模型的不匹配问题:

  • SOA 以接口为单位做服务注册和服务发现,而微服务下是服务名
  • SOA 是”单进程多接口”,而微服务是”单进程单服务”

一步接一步的需求

先上车后补票
最理想的做法当然是先进行微服务改造,实现微服务拆分。但是考虑到现有应用数量众多,我们可能更愿意在大规模微服务改造之前,先想办法让这些应用可以运行在 ServiceMesh 下,提前受益于 ServiceMesh 带来的强大功能。因此,我们需要找到一个合适的方案,让 ServiceMesh 支持没有做微服务改造依然是”单进程多接口”形式的传统 SOA 应用,所谓”先上车后补票”。

不修改代码
考虑到原有的 SOA 应用,相互之间错综复杂的调用关系,最好不要修改代码,即保持客户端依然通过接口名来访问的方式。当然,SOA 架构的客户端 SDK 可能要进行改动,将原有的通过接口名进行服务发现再自行负载均衡进行远程调用的方式,精简为标准的 ServiceMesh 调用(即走Sidecar),因此修改SDK依赖包和重新打包应用是不可避免。

支持带特殊字符的接口名
k8s 的服务注册,Service 名是不能携带”.“号的。而 SOA 架构下,接口名有时出于管理方便,有可能是加了域名前缀,如“com.alipay.demo.interface-2”。为了实现不修改原有代码,就只能想办法支持这种带特殊字符的接口名。

参考 Kubernetes 和 Istio

在进一步讨论解决方案之前,我们先来看一下kubernetes 和 istio 中的标准请求寻址方式。

(备注:过程稍显复杂,涉及到k8s/istio的一些底层细节。但是了解这个过程对后续的理解非常重要,也可以帮助大家了解k8s和k8s的工作原理,强烈推荐阅读。)

k8s下的DNS寻址方式

在k8s下,如图所示,假定我们部署了一个名为 userservice 的应用,有三个实例,分别在三个 pod 中。则应用部署之后,k8s 会为这个应用分配 ClusterIP 和域名,并在DNS中生成一条DNS记录,将域名映射到ClusterIP:

image.png

当部署在 k8s 下的某个充当客户端的应用发起请求时,如图中的 HTTP GET请求,目标 URL 地址为 “http://userservice/id/1000221"。请求的寻址方式和过程如下:

  • 首先进行域名解析,分别尝试解析“userservice”/“userservie.default.svc.cluster.local”等域名,得到 ClusterIP
  • 然后客户端发出请求的报文,目标地址为ClusterIP,源地址为当前客户端所在的 pod IP(简单起见,端口先忽略)
  • 请求报文随即被 kube-proxy 拦截,kube-proxy 根据 ClusterIP,拿到ClusterIP 对应的多个实际服务实例所在的 pod ip,取其中一个,修改目标地址为这个pod IP
  • 请求报文最终就被发送到服务实例所在的pod IP

应答回来的方式类似,userservice发出的应答报文会被 kube-proxy 拦截并修改为发送到客户端所在的 pod IP。

我们详细看一下请求和应答全称的四个请求包的具体内容(简单起见继续忽略端口):

image.png

重点关注请求和应答报文的源地址和目标地址:

客户端发出的请求,为“客户端到 ClusterIP”

kube-proxy 拦截到请求后,将请求修改为“客户端到服务器端”

服务器端收到请求时,表现为“客户端到服务器端”,ClusterIP 被kube-proxy 屏蔽

服务器端发送应答,因为收到的请求看似来自客户端,因此应答报文为”服务器端到客户端”

应答报文被 kube-proxy 拦截,将应答修改为 “ClusterIP到服务器端”

客户端收到应答,表现为“ClusterIP 到服务器端”,服务器端 IP 被 kube-proxy 屏蔽

kube-proxy 在客户端和服务器端之间拦截并修改请求和应答的报文,联通两者,但各自屏蔽了一些信息:

在客户端看来它是在和 ClusterIP 交互,userservice 的具体服务器端实例对客户端是无感知的

在服务器端看来,客户端是直接在和它交互,ClusterIP 的存在对服务器端是无感知的

更深入一步,看 kube-proxy 在两个拦截和修改报文中的逻辑处理关系,即kube-proxy是如何在收到应答时正确的找回原有的 ClusterIP:

image.png

在拦截并修改请求报文之后,kube-proxy 会保存报文修改的5元组对应关系(5元组指源 IP 地址,源端口,协议,目的地 IP 地址,目的地端口)

在收到应答报文后,根据应答报文中的5元组,在保存的5元组对应关系中,找到对应信息,得到原有的 ClusterIP 和端口,然后修改应答报文

总结,通过上述k8s下的寻址方式,客户端只需发送带简单寻址信息的请求(如 “http://userservice/id/1000221" 中的“userservice” ),就可以寻址到正确的服务器端。这期间有两个关注点:

通过 DNS,建立了域名和 ClusterIP 的关系。
对于客户端,这是它能看到的内容,非常的简单,域名、DNS 是非常容易使用的。

而通过 kube-proxy 的拦截和转发,又打通了ClusterIP 和服务器端实际的Pod IP
对于客户端,这些是看不到的内容,不管有多复杂,都是k8s在底层完成,对客户端,或者说使用者透明。

以客户端的视角看来,这个DNS寻址方式非常的简单直白:

image.png

Istio的 DNS 寻址方式

Istio的请求寻址方式和普通 kubernetes 非常相似,原理相同,只是 kube-proxy被 sidecar 取代,然后 sidecar 的部署方式是在 pod 内部署,而且客户端和服务器端各有一个 sidecar。其他基本一致,除了图中红色文本的部分:

image.png

iptables 在劫持流量时,除了将请求转发到 localhost 的 Sidecar 处外,还额外的在请求报文的 TCP options 中将 ClusterIP 保存为 original dest。

在 Sidecar (Istio 默认是 Envoy)中,从请求报文 TCP options 的 original dest 处获取 ClusterIP

通过TCP options 的 original dest,iptables就实现了在劫持流量到Sidecar的过程中,额外传递了 ClusterIP 这个重要参数。Istio为什么要如此费力的传递这个 ClusterIP 呢?

看下图就知道了,这是一个 Virtual Host 的示例, Istio 通过 Pilot 将这个规则发送给 Sidecar/Envoy ,依靠这个信息来匹配路由请求找到处理请求的cluster:

image.png

domains中,除了列出域名外,还有一个特殊的IP地址,这个就是 k8s 服务的 ClusterIP!因此,Sidecar可以通过前面传递过来的 ClusterIP 在这里进行路由匹配(当然也可以从报文中获取 destination 然后通过域名匹配)。

总结,Istio 延续了 k8s 的寻址方式,客户端同样只需发送带简单寻址信息的请求,就可以寻址到正确的服务器端。这期间同样有两个关注点:

通过DNS,建立了域名和ClusterIP的关系。

通过 ClusterIP 和 Pilot 下发给 Virtual Host 的配置,Sidecar 可以完成路由匹配,将 ClusterIP 和目标服务器关联起来
同样,对于客户端,这些是看不到的内容。

因此,以客户端的视角看来,Isito的这个DNS寻址方式同样的简单直白!

DNS通用寻址方案具体介绍

解决问题的思路

在详细讲述了 k8s 和 istio 的 DNS 寻址方案之后,我们继续回到我们的主题,我们要解决的问题:

如何在不修改代码,继续使用接口的情况下,实现在 Service Mesh 上运行现有的 Dubbo/HSF/SOFA 等传统 SOA 应用?

这里有一个关键点:k8s 的服务注册是以基于 Service 或者说基于应用(app name),而我们的客户端代码是基于接口的。因此,在 Virtual Host 进行路由匹配时,是不能通过域名匹配的。当然,这里理论上还有一个思路,就是将接口注册为 k8s Service。但是,还记得要支持接口特殊字符的需求吗?带点号的接口名,k8s 是不能接受它作为 Service Name 的,直接堵死了将接口名注册到 k8s Service 的道路。

这样,我们就只有一条路可以走了:效仿 istio 的做法,通过 ClusterIP 匹配!

而要将接口名(如”com.alipay.demo.interface-1”)和 ClusterIP 关联,最简单直接的方式就是打通DNS :

image.png

只需要在DNS记录中,增加接口到 ClusterIP 的映射,然后就可以完全延续Istio的标准做法!其他的步骤,如域名解析到ClusterIP,iptables拦截并传递ClusterIP,sidecar读取ClusterIP并匹配路由,都完全可以重用原有方案。

具体实现方案

实现时,我们选择了使用 CoreDNS 作为 k8s 的 DNS 解决方案,然后通过 Service Controller 操作 CoreDNS 的记录来实现 DNS 解析。

为了收集到 SOA 应用的接口信息,我们还提供了一个 Register Agent 给 Service Controller 收集信息。

image.png

详细的实现方案,不在本文中重复讲述,请参阅我们之前的分享文章 SOFAMesh 的通用协议扩展 中的 DNS 寻址方案一节。

(备注:暂时修改 CoreDNS 记录的方式是直接修改 CoreDNS 的底层数据,不够优雅。未来将修改为通过 CoreDNS 的 Dynamic updates API 接口进行,不过 CoreDNS 的这个API还在开发中,需要等待完成。)

单进程多接口问题的解决

上面的解决方案,在解决通过接口实现访问的同时,也将”单进程多接口”的问题一起解决了:

  • 原 SOA 应用上 k8s 时,可以注册为标准的 k8s Service,获取 ClusterIP。此时使用应用名注册,和接口无关。
  • 通过操作 CoreDNS,我们将该 SOA 应用的各个接口都添加为 DNS 记录,指向该应用的 ClusterIP
  • 当客户端代码使用不同的接口名访问时,DNS解析出来的都是同一个 ClusterIP,后续步骤就和接口名无关了

欠缺微服务改造带来的限制

需要特别指出的是,DNS 通用寻址方案虽然可以解决使用接口名访问和支持单进程多接口的问题,但是这种方案只是完成了“寻址”,也就是打通端到端的访问通道。由于应用没有进行微服务改造,部署上是依然一个应用(体现为一个进程,在 k8s 上体现为一个 Service)中包含多个接口,本质上:

  • 服务注册依然是以应用名为基础,对应的 k8s service 和 service 上的 label也是应用级别
  • 因此提供的服务治理功能,也是以 k8s 的 Service 为基本单位,包括灰度,蓝绿,版本拆分等所有的 Vesion Based Routing 功能
  • 这意味着,只能进行应用级别的服务治理,而不能继续细分到接口级别

这个限制来源于应用没有进行微服务改造,没有按照接口将应用拆分为多个独立的微服务,因此无法得到更小的服务治理粒度。这也就是我在2018年上半年,蚂蚁金服决定基于 Istio 订制自己的 ServiceMesh 解决方案,在6月底对外公布了 SOFAMesh,详情请见之前的文章: 大规模微服务架构下的Service Mesh探索之路 。

DNS通用寻址方案总结

我们将这个方案称为“DNS通用寻址方案”,是因为这个方案真的非常的通用,体现在以下几个方面:

  • 对使用者来说,通过域名和 DNS 解析的方式来访问,是非常简单直白而易于接受的,同时也是广泛使用的,适用于各种语言、平台、框架。
  • 这个方案延续了 k8s 和 istio 的做法,保持了一致的方式方式,对用户提供了相同的体验
  • 这个寻址方案,不仅仅可以用于 Dubbo、SOFA、HSF 等 RPC 框架往 Service Mesh 的迁移,也可以适用于基于 HTTP/REST 协议的 SOA 应用,甚至最传统的 web 应用(例如 tomcat 下部署多个 war 包)迁移到Service Mesh
  • 我们也在考虑在未来的 Serverless 项目中,将 Function 的寻址也统一到这套方案中,而无需要求每个 Function 都进行一次服务注册

概括的说,有了这套 DNS 通用寻址方案,不管需要寻址的实体是什么形态,只要它部署在 Service Mesh 上,满足以下条件:

有正常注册为 k8s Service,分配有 ClusterIP

为实体(或者更细分的子实体)分配域名或子域名,然后添加到 DNS,解析到 ClusterIP

那么我们的 DNS 通用寻址方案,就可以工作,从而将请求正确的转发到目的地。而在此基础上,Service Mesh 所有的强大功能都可以为这些实体所用,实现我们前面的目标:在不修改代码不做微服务改造的情况下,也能提前受益于 Service Mesh 带来的强大服务治理功能。

目录
相关文章
|
10天前
|
域名解析 存储 网络协议
深入解析网络通信关键要素:IP 协议、DNS 及相关技术
本文详细介绍了IP协议报头结构及其各字段的功能,包括版本、首部长度、服务类型、总长度、标识、片偏移、标志、生存时间(TTL)、协议、首部检验和等内容。此外,还探讨了IP地址的网段划分、特殊IP地址的应用场景,以及路由选择的大致流程。最后,文章简要介绍了DNS协议的作用及其发展历史,解释了域名解析系统的工作原理。
46 5
深入解析网络通信关键要素:IP 协议、DNS 及相关技术
|
6天前
|
前端开发 JavaScript 安全
深入解析 http 协议
HTTP(超文本传输协议)不仅用于传输文本,还支持图片、音频和视频等多种类型的数据。当前广泛使用的版本为 HTTP/1.1。HTTPS 可视为 HTTP 的安全增强版,主要区别在于添加了加密层。HTTP 请求和响应均遵循固定格式,包括请求行/状态行、请求/响应头、空行及消息主体。URL(统一资源定位符)用于标识网络上的资源,其格式包含协议、域名、路径等信息。此外,HTTP 报头提供了附加信息,帮助客户端和服务端更好地处理请求与响应。状态码则用于指示请求结果,如 200 表示成功,404 表示未找到,500 表示服务器内部错误等。
14 0
深入解析 http 协议
|
16天前
|
数据采集 存储 JSON
从零到一构建网络爬虫帝国:HTTP协议+Python requests库深度解析
在网络数据的海洋中,网络爬虫遵循HTTP协议,穿梭于互联网各处,收集宝贵信息。本文将从零开始,使用Python的requests库,深入解析HTTP协议,助你构建自己的网络爬虫帝国。首先介绍HTTP协议基础,包括请求与响应结构;然后详细介绍requests库的安装与使用,演示如何发送GET和POST请求并处理响应;最后概述爬虫构建流程及挑战,帮助你逐步掌握核心技术,畅游数据海洋。
47 3
|
20天前
|
消息中间件 安全 Kafka
Kafka支持SSL/TLS协议技术深度解析
SSL(Secure Socket Layer,安全套接层)及其继任者TLS(Transport Layer Security,传输层安全)是为网络通信提供安全及数据完整性的一种安全协议。这些协议在传输层对网络连接进行加密,确保数据在传输过程中不被窃取或篡改。
40 0
|
2月前
|
开发者 图形学 iOS开发
掌握Unity的跨平台部署与发布秘籍,让你的游戏作品在多个平台上大放异彩——从基础设置到高级优化,深入解析一站式游戏开发解决方案的每一个细节,带你领略高效发布流程的魅力所在
【8月更文挑战第31天】跨平台游戏开发是当今游戏产业的热点,尤其在移动设备普及的背景下更为重要。作为领先的游戏开发引擎,Unity以其卓越的跨平台支持能力脱颖而出,能够将游戏轻松部署至iOS、Android、PC、Mac、Web及游戏主机等多个平台。本文通过杂文形式探讨Unity在各平台的部署与发布策略,并提供具体实例,涵盖项目设置、性能优化、打包流程及发布前准备等关键环节,助力开发者充分利用Unity的强大功能,实现多平台游戏开发。
53 0
|
2月前
|
开发者 图形学 UED
深度解析Unity游戏开发中的性能瓶颈与优化方案:从资源管理到代码执行,全方位提升你的游戏流畅度,让玩家体验飞跃性的顺滑——不止是技巧,更是艺术的追求
【8月更文挑战第31天】《Unity性能优化实战:让你的游戏流畅如飞》详细介绍了Unity游戏性能优化的关键技巧,涵盖资源管理、代码优化、场景管理和内存管理等方面。通过具体示例,如纹理打包、异步加载、协程使用及LOD技术,帮助开发者打造高效流畅的游戏体验。文中提供了实用代码片段,助力减少内存消耗、提升渲染效率,确保游戏运行丝滑顺畅。性能优化是一个持续过程,需不断测试调整以达最佳效果。
62 0
|
2月前
|
消息中间件 Kafka Java
Spring 框架与 Kafka 联姻,竟引发软件世界的革命风暴!事件驱动架构震撼登场!
【8月更文挑战第31天】《Spring 框架与 Kafka 集成:实现事件驱动架构》介绍如何利用 Spring 框架的强大功能与 Kafka 分布式流平台结合,构建灵活且可扩展的事件驱动系统。通过添加 Spring Kafka 依赖并配置 Kafka 连接信息,可以轻松实现消息的生产和消费。文中详细展示了如何设置 `KafkaTemplate`、`ProducerFactory` 和 `ConsumerFactory`,并通过示例代码说明了生产者发送消息及消费者接收消息的具体实现。这一组合为构建高效可靠的分布式应用程序提供了有力支持。
85 0
|
2月前
|
Java Spring 监控
危机时刻,Spring框架如何拯救你的应用?深入探讨健康检查与自我修复功能
【8月更文挑战第31天】在现代软件架构中,应用的稳定性和可用性至关重要。本文介绍Spring框架中的健康检查与自我修复机制,通过Spring Boot Actuator的`/health`端点监控应用状态,并结合Spring Cloud Hystrix实现服务容错和断路器功能,提高应用健壮性。借助这些工具,开发者能轻松监控应用健康状况并在发现问题时自动采取措施,确保服务高可用性。要实现完善的机制,需根据具体应用架构和需求进行配置和扩展。
40 0
|
2月前
|
开发者 测试技术 Android开发
Xamarin 开发者的五大常见问题及解决方案:从环境搭建到性能优化,全面解析高效跨平台应用开发的技巧与代码实例
【8月更文挑战第31天】Xamarin 开发者常遇问题及解决方案覆盖环境搭建至应用发布全流程,助新手克服技术难关。首先需正确安装配置 Visual Studio 及 Xamarin 支持,设置 iOS/Android 测试环境。利用 Xamarin.Forms 和 XAML 实现高效跨平台开发,共享 UI 和业务逻辑代码。针对性能优化,采取减少 UI 更新、缓存计算结果等措施,复杂问题则借助 Xamarin Profiler 分析。
36 0
|
2月前
|
监控 网络协议 Java
Tomcat源码解析】整体架构组成及核心组件
Tomcat,原名Catalina,是一款优雅轻盈的Web服务器,自4.x版本起扩展了JSP、EL等功能,超越了单纯的Servlet容器范畴。Servlet是Sun公司为Java编程Web应用制定的规范,Tomcat作为Servlet容器,负责构建Request与Response对象,并执行业务逻辑。
Tomcat源码解析】整体架构组成及核心组件

相关产品

  • 云解析DNS
  • 推荐镜像

    更多
    下一篇
    无影云桌面