深度剖析线上应用节点流量隔离技术

作者:谢文欣(风敬)

为什么要做流量隔离

源于一个 EDAS 客户遇到的棘手情况:他们线上的一个 Pod CPU 指标异常,为了进一步诊断问题,客户希望在不重建此 Pod 的情况下保留现场,但诊断期间流量还会经过这个异常 Pod,导致影响服务质量,于是询问我们有没有办法可以把流入异常节点的流量摘除掉,形成一个隔离的诊断环境。经诊断后,如果异常可以修复,待修复完成后,再解除流量隔离,节点恢复正常工作。

除了在诊断场景需要对所有输入流量进行隔离外,在一些线上演练中还需对特定流量进行隔离以实现模拟演练效果。面对这类流量隔离问题时,我们首先考虑的是全链路流量控制。目前,EDAS 上的全链路流控能够在不重启应用节点的情况下控制流量走向。然而,全链路流控仅能控制微服务框架流量,无法满足隔离所有或特定流量的需求。

为此,我们进行了深入研究,实现了一套开箱即用的流量隔离工具,能够动态隔离特定流量,并在隔离后可随时恢复,以满足各种场景下的流量隔离需求。

隔离哪些流量

流量隔离的目的是阻断应用节点的流入流量,首先明确下微服务应用节点流入的流量有哪些。

流入微服务应用节点的流量大致可以分为两大类:服务流量、事件流量。以常见的微服务应用为例,其流量组成如下图所示。

在这里插入图片描述

服务流量指一个微服务应用的所有节点作为一个网络实体,对外提供一组服务,被其他系统、服务或用户发起请求产生的调用。对于服务流量,节点本身不直接决定流量的流入与否,而是由一套服务注册与发现机制维护流量路径的逻辑关系。节点经过注册,成为服务的一个端点。调用方对服务发起请求时,被调用方是服务的逻辑地址,经过转发和地址转换,请求被路由到服务端点的实体节点。隔离服务流量的一个可选方案是破坏服务调用的通信连接,但这种方法势必会影响服务质量。在保持服务整体功能正常运行的同时,一个更优雅的方案是破坏服务与实体节点之间的映射关系。这样,在路由过程中,流量将按照预期避开特定节点,而被引导至其他节点。服务流量主要涵盖了 K8s Service 以及使用 Nacos 等注册中心发布的由 Spring Cloud、Dubbo 等微服务框架构建的服务。

事件流量指应用内部的事件驱动架构产生的流量,包括由中间件传递至应用节点的事件或消息,这类通信通常是异步的,例如来自消息队列 RocketMQ 的消息流量,来自调度框架 SchedulerX 触发调度的事件流量。中间件和应用节点之间通常遵循 client-server 通信,因此可以考虑通过破坏通信连接来隔离中间件发来的消息或事件流量。

服务流量隔离

K8s Service

对于使用 K8s Service 暴露服务的应用,Service 声明的服务与应用 Pod 之间的映射关系由 Endpoints 对象维护。Endpoints 对象的 subsets 字段表示 Serivce 的一组端点,每个端点代表一个应用 Pod 的网络地址,即一个实际提供服务的 Pod 实例。subsets 字段包含了这些端点的详细信息,如 IP 地址和端口。Endpoints 控制器通过 API Server 监听 Pod 的变更情况,并随后同步更新 Endpoints 的端点列表。因此,要隔离 K8s Service 的流量,需要破坏 Endpoints 对 Pod 的指向,将待隔离的 Pod 网络地址从 Endpoints 的端点列表中移除。同时,需要通过 Informer 机制监听 Endpoints 对象的变化,以保证 Endpoints 在后续变更或控制器 Reconcile 过程中也能维持预期状态。

在这里插入图片描述

Dubbo

对于使用注册中心暴露服务的应用,注册中心负责管理服务节点。只要注册关系存在且应用节点存活,注册中心会将流量调度到该应用节点。而破坏服务注册关系的操作被称为服务注销,应用节点进行服务注销之后,注册中心便不会将流量导入到注销节点,也就形成了流量隔离。

要实现 Dubbo 微服务的动态注销,首先需要从源码级别了解 Dubbo 服务注册原理。以 Dubbo 2.7.0 为例,其服务注册模块的大致结构如下:

  1. Dubbo 应用中存在一个 AbstractRegistryFactory 单例,负责注册中心 Registry 的容器初始化。类属性 REGISTRIES 维护了微服务列表与注册中心实例的映射关系。
  2. AbstractRegistry 实现了 Registry 接口,作为一个模板,实现了特定的公共方法,如服务注册(register)、服务注销(unregister)等。它还维护了已注册服务 URL 列表。
  3. FailbackRegistry 基于 AbstractRegistry,提供了失败重试机制。同时,它提供了注册中心的 doRegister 和 doUnregister 抽象方法。当执行 register/unregister 时,会调用 doRegister/doUnregister 方法。
  4. 注册中心(如 NacosRegistry、RedisRegistry)实现了具体的服务注册(doRegister)和服务注销(doUnregister)逻辑。

在这里插入图片描述

由源码可见,Dubbo 的服务注册模块已经内置了可动态注销/重注册服务的方法。因此, Dubbo 微服务隔离可通过主动触发其注册中心对象的服务注销方法来实现。同理,如果需要恢复服务节点,主动触发服务注册方法,更新注册中心的服务映射关系。

在确定「触发注册中心对象的服务注销方法」这一技术方向之后,需要解决如何获取对象和触发方法这两个问题。在 Java 环境中,我们很容易想到使用 Agent 技术对进程行为进行干预。然而,常规的基于字节码埋点的 Agent 无法满足随时启用的需求,因为它依赖于应用代码的具体执行路径。只有当执行路径触及埋点时,Agent 代码才会被触发,从而从上下文中获取对象并通过反射调用相关方法。然而,与注册中心相关的埋点通常设置在程序启动初期,此时会执行注册中心初始化、服务注册等操作,比较容易找到合适的埋点。在程序对外提供服务期间,程序主动发起的注册中心操作较少,因此很难找到合适的埋点来获取预期的上下文。在需要隔离应用流量时,此时动态挂入 Agent,由于执行路径中没有能获取注册中心上下文的埋点,Agent 代码将无法生效。

因此,我们需要一个能够主动获取对象并触发对象方法的即开即用的 Agent 工具。在这里,我们引入了 JVMTI 技术。JVMTI(JVM Tool Interface)是一种虚拟机提供的原生编程接口,允许开发人员创建 Agent 以探查 JVM 内部的运行状态,甚至控制 JVM 应用程序的执行。JVMTI 能够从 Java 堆中获取特定类和对象信息,然后通过反射触发方法,完美地满足了我们的需求。

由于 JVMTI 是一套 JVM 原生编程接口,需要使用 C/C++ 进行编写。编译后的产物是动态链接库(.so 或 .dll 文件)。Java 运行环境通过 JNI(Java Native Interface)与 JVMTI 进行交互。整体作为一个 Java Agent,通过 Attach API 动态地挂载到目标 JVM 中。

在这里插入图片描述

得益于 JVMTI Agent 的强大功能,我们能够在 Java 应用内相对简便地实施某些控制逻辑。为实现 Dubbo 服务流量隔离,首先需要获取 AbstractRegistryFactory 类的静态属性 REGISTRIES,它包含应用当前已注册服务的服务列表以及相应的注册中心 Registry 实例。对于特定的微服务,仅需调用其注册中心 Registry 的 register/unregister 方法,便可实现服务的动态摘除和恢复。这一方案直接在较高抽象层级上操作,而无需依赖具体的注册中心 Registry 实现类,使其兼容所有注册中心。

在这里插入图片描述

Spring CLoud

Spring Cloud 服务流量隔离方法类似于 Dubbo,在了解 Spring Cloud 服务注册原理后,获得服务注册/注销方法路径,然后通过 JVMTI 干预应用的服务注册/注销行为。

Spring Cloud 的服务注册原理较为简单。在 Spring 容器启动时,AbstractAutoServiceRegistration 监听启动事件,并调用 ServiceRegistry 的 register 方法将 Registration(服务实例数据)注册到注册中心。例如,Nacos服务注册类 NacosServiceRegistry 实现了 ServiceRegistry 接口,通过重载 register/deregister 方法完成服务在注册中心的注册和注销。

// 服务注册类
public abstract class AbstractAutoServiceRegistration<R extends Registration>...{      
    // 注册中心实例
    private final ServiceRegistry<R> serviceRegistry;
    // 服务注册
  protected void register() {
    this.serviceRegistry.register(getRegistration());
  }
    // 服务注销
  protected void deregister() {
    this.serviceRegistry.deregister(getRegistration());
  }
}

在处理 Spring Cloud 服务流量隔离时,首先获取 AbstractAutoServiceRegistration 的服务注册实例,然后调用 register/deregister 方法以在注册中心上完成服务的注销和重注册。这种方法同样不依赖于某个特定注册中心的具体实现类,兼容所有注册中心。

在这里插入图片描述

事件流量隔离

应用节点和中间件通常采用 client-server 模式通信,像 RocketMQ 和 SchedulerX 使用了 Netty 作为底层网络框架完成客户端和服务端通信 。在此,我们以 RocketMQ 为例,来说明如何实现类似的事件驱动中间件的流量隔离。

RocketMQ client 端的主要实现类是 NettyRemotingClient。如下图所示,NettyRemotingClient 类中的属性 channelTables 存储了用于传输数据的 Channel,而 lockChannelTables 是用于控制 channelTables 更新的锁。与此同时,有几个 invoke 方法负责处理通信过程。

在这里插入图片描述

通信处理流程如下图。首先,尝试从 channelTables 中获取用于通信的 Channel。如果没有可用的 Channel,则重新连接 server 端以创建 Channel。为了保证线程间同步,新 Channel 更新到 channelTables 时需要获得 lockChannelTables 锁。如果在指定时间窗口内 lockChannelTables 一直被占用,将会抛出连接异常。

在这里插入图片描述

根据以上的原理分析,我们可以通过占用 lockChannelTables 锁来阻止 Channel 的建立,再把现存的 Channel 关闭,则 client 端在 lockChannelTables 被释放之前都无法与 server 端建立通信连接。若要恢复流量,仅需释放 lockChannelTables 锁,client 端将自动重建 Channel 并恢复通信。由于这种管控是在网络客户端层进行的,因此它不受应用消息模型的影响,既适用于同步消息也适用于异步消息;同时也与 client 角色无关,既适用于消费者也适用于生产者。

在这里插入图片描述

结语

目前流量隔离工具在 EDAS-云原生工具箱 可试用体验。如果对流量隔离以及更多云原生工具感兴趣,欢迎留言或加入钉群:21958624 与我们进行沟通与交流。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/36130.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

chatGPT如何开启 Browsing 功能,实现即时联网查询?

Openai 为每一个 chatGPT Plus 用户都开放了 Browsing 和 plugins 功能。 前者可以在 ChatGPT 觉得有必要的时候&#xff08;比如你问它今天的新闻&#xff09;&#xff0c;自动联网查询&#xff0c;后者是第三方开发者开发的插件&#xff0c;数量繁多&#xff0c;可以解决各种…

【Distributed】分布式ELK日志文件分析系统

文章目录 一、ELK 概述1. 为什么要使用 ELK2. 完整日志系统基本特征3. ELK 简介3.1 ElasticSearch&#xff08;ES&#xff09;3.2 Kiabana3.3 Logstash3.4 其它组件Filebeat缓存/消息队列Fluentd 4. ELK 的工作原理5. Linux 系统内核日志消息的优先级别 二、 部署 ELK 集群服务…

使用python调用ChatGPT API 简单示例

如果你已经获得了OpenAI的API密钥&#xff0c;并且想要使用Python发起ChatGPT对话&#xff0c;你可以使用OpenAI的Python SDK来实现。下面是一个简单的示例代码&#xff1a; 首先&#xff0c;你需要确保已安装OpenAI的Python SDK。你可以使用pip来安装&#xff1a; pip insta…

BaGet做了一个Nuget私有服务器,Nginx代理之后还是会请求被代理得地址

Nuget搭建和使用可以参考官网得文档 https://loic-sharma.github.io/BaGet/installation/docker/ 这是我用Nginx代理之后出现得问题&#xff0c;观察请求url和响应回来得配置。配置中得ip地址得url是我被代理得下游地址&#xff0c;所以是无法访问的。 我原本以为是要去server…

【案例教程】GPT模型支持下的Python-GEE遥感云大数据分析、管理与可视化技术及多领域案例实践实践技术

随着航空、航天、近地空间等多个遥感平台的不断发展&#xff0c;近年来遥感技术突飞猛进。由此&#xff0c;遥感数据的空间、时间、光谱分辨率不断提高&#xff0c;数据量也大幅增长&#xff0c;使其越来越具有大数据特征。对于相关研究而言&#xff0c;遥感大数据的出现为其提…

79. 单词搜索

79. 单词搜索 原题链接&#xff1a;完成情况&#xff1a;解题思路&#xff1a;参考代码&#xff1a; 原题链接&#xff1a; 79. 单词搜索 https://leetcode.cn/problems/word-search/ 完成情况&#xff1a; 解题思路&#xff1a; 参考代码&#xff1a; package 西湖算法题…

微信小程序使用animation.css

animation.css是一款纯css动画库&#xff0c;其中提供了丰富的动画效果 我们直接下载animation.css&#xff0c;即可使用其中的样式 其官网为&#xff1a;Animate.css | A cross-browser library of CSS animations. 1.下载 使用npm下载animation.css&#xff1a; npm inst…

构建工具——webpack、vite

文章目录 构建工具Webpack使用步骤配置文件&#xff08;webpack.config.js&#xff09;插件&#xff08;plugin&#xff09; ViteVite 也是前端的构建工具使用命令构建配置文件&#xff1a;vite.config.js 构建工具 当我们习惯了在 node 中编写代码的方式后&#xff0c;在回到…

python怎么获取免费代理IP

什么是免费代理IP 免费代理IP是指可以免费使用的代理服务器的IP地址。代理服务器充当客户端和目标服务器之间的中间人&#xff0c;通过转发请求和响应来实现网络数据的传输。使用代理IP可以隐藏真实的客户端IP地址&#xff0c;实现匿名访问网络资源。 免费代理IP通常由个人或组…

实战:k8s证书续签-2023.6.19(测试成功)

实战&#xff1a;k8s证书续签-2023.6.19(测试成功) 目录 推荐文章 https://www.yuque.com/xyy-onlyone/aevhhf?# 《玩转Typora》 1、前言 k8s集群核心的证书有2套&#xff0c;还有1套非核心的(即使出问题也问题不大)。 ⚠️ 如果是kubeadm搭建的k8s集群&#xff0c;其有效期为…

Spring系列4 -- Bean的作用域和生命周期

目录 1. 案例 2. 作用域定义 2.1 Bean的6种作用域 2.2 设置作用域 3. Sring的执行流程 4. Bean的生命周期 思考: 为什么不是先进行初始化然后再进行设置属性呢? 1. 案例 假设现在有⼀个公共的 Bean&#xff0c;提供给 A ⽤户和 B ⽤户使⽤&#xff0c;然⽽在使⽤的途中…

HTML5中一些酷炫又有趣的新特性代码整理汇总

HTML5中一些酷炫又有趣的新特性代码整理汇总 文章目录 HTML5中一些酷炫又有趣的新特性代码整理汇总前言一、详情标签< details>二、内容可编辑三、标记内容< mark>四、data-* 属性五、输出标签六、数据列表< datalist>七、Meter八、Inputs 前言 HTML5 是 Hy…

Apikit 自学日记:测试数据集

测试数据集 添加数据集的变量 在测试用例详情页面中&#xff0c;您可以点击上方的 测试数据 标签&#xff0c;进入用例的数据管理页面。在这里您可以添加多组测试数据&#xff0c;以及每组测试数据的变量。 在添加数据集前&#xff0c;我们需要设置数据集中存在什么变量。可以…

IPV6使用越来越广,您会配置吗?

前面针对IPv6写过一篇文章&#xff0c;但是好多网友反映没有读懂&#xff0c;今天再给大家把内容浓缩一下&#xff0c;教给大家如何配置。 IPV6的推出主要是为了解决地址空间的不足&#xff0c;从而进一步的促进互联网的发展。IPV6地址空间大到惊人&#xff0c;有人比喻地球上…

Springboot设置并访问静态资源目录

目录​​​​​​​ 静态文件 application设置方法 配置详解 编写配置 优缺点 设置配置类方法 配置详解 编写配置 优缺点 总结 静态文件 静态资源&#xff0c;一般是网页端的&#xff1a;HTML文件、JavaScript文件和图片。尤其是设置图片的静态资源&#xff0c;尤其重…

BERT论文解读及实现(一)

BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding 1 论文解读 1.1 模型概览 There are two steps in our framework: pre-training and fine-tuning. bert由预训练模型微调模型组成。 ① pre-training, the model is trained on unlabele…

基于simulink处理监控视频以选择包含运动的帧(附源码)

一、前言 此示例演示如何处理监控视频以选择包含运动的帧。安全问题要求使用摄像机对重要位置进行持续监控。为了有效地记录、查看和存档这些海量数据&#xff0c;您可以减小视频帧大小或减少录制的视频帧总数。此示例说明了后一种方法。在其中&#xff0c;相机视野中的运动会…

使用GitHub Actions 来进行项目远程服务器部署

由于项目源码是托管在github的&#xff0c;而部署是放在远程服务器上&#xff0c;并且使用nginx部署。 现在的部署流程时&#xff0c;需要更新时&#xff0c;在本地切换到master分支&#xff0c;执行构建操作&#xff0c;拿到构建出的dist目录&#xff0c;将其上传到远程服务的…

【HTTP】HTTP协议 HTTPS 协议

目录 一、HTTP &#x1f337;1、HTTP是什么&#xff1f; &#x1f337;2、HTTP的工作过程 &#x1f337;3、 抓包工具Fiddler的使用 &#x1f337;4、HTTP的协议格式&#xff08;重点&#xff09; &#x1f337;5、HTTP请求 5.1 请求地址URL 5.2 方法method 5.3 认识 …

常见面试题之垃圾收回

1. 简述Java垃圾回收机制&#xff1f;&#xff08;GC是什么&#xff1f;为什么要GC&#xff1f;&#xff09; 为了让程序员更专注于代码的实现&#xff0c;而不用过多的考虑内存释放的问题&#xff0c;所以&#xff0c;在Java语言中&#xff0c;有了自动的垃圾回收机制&#x…