您现在的位置是:首页 > 正文

Chaos Mesh 在每刻科技实际场景的使用

2024-02-01 05:08:59阅读 2

引言

一个大型的 微服务 应用就像一个混沌的世界,保证系统的健壮性是一件非常重要但具有挑战的话题。在微服务系统中存在大量的服务实例,当部分服务实例出现问题时,微服务应用需要具有较高的容错性,通过重试,断路,自愈等手段保证系统能够继续对外正常提供服务。因此在应用发布到生产系统强需要对系统进行充分的健壮性测试。

对微服务应用进行健壮性测试的一个最大的困难是如何对系统故障进行模拟。在一个部署了成百上千微服务的测试环境中,如果想通过对应用、主机或者交换机进行设置来模拟微服务之间的通信故障是非常困难的。

每刻业务痛点

杭州每刻科技有限公司 是一家领先的智能云财务产品和解决方案服务商,内部业务逻辑十分复杂,又有很多外部对接的情况,所以遇到了多种测试场景无法构建、线上问题无法复现等痛点。于是考虑引进混沌测试,通过多方面的场景,去解决测试过程中的一些问题。

比如,每刻某个业务是使用 Java 异步方式实现的,如果遇到业务处理失败的情况,会进行业务异常处理,这时候如果恰巧遇到服务刚启动或者服务进程结束,又会进行业务回滚,由于某些原因,又可能会导致回滚失败。总得来说就是,访问接口失败,进入异常,异常处理又失败,导致回滚,回滚又失败,这个时候就会有一些脏数据需要去手动清理。虽然在业务代码中有对这种故障的应对措施,但无法通过常规的测试手段去验证是否按预期执行,这个时候就需要使用混沌工程的测试方法。

为什么选择 Chaos Mesh?

每刻之所以选择 Chaos Mesh 主要源于其能在以下 2 个方面,解决每刻在微服务健壮性测试过程中遇到的痛点:

故障的不可预测以及异常的不可复现性

在分布式计算的世界中,故障可能随时随地发生在你的集群上。传统情况下,我们使用单元测试和集成测试来确保系统可以投入生产。但是,随着集群规模的扩大,复杂性的增加以及数据量以 PB 级增长,这些测试无法涵盖所有内容。

Chaos Mesh 充分考虑了分布式系统可能出现的故障,提供全面、细粒度的故障类型,通过人为地在集群中注入故障来检测集群对故障的处理以及恢复能力。目前,Chaos Mesh提供的混沌实验分为基础资源类型故障(例如 NetworkChaos, HTTPChaos, StressChaos, KernelChaos)、平台类型故障(例如 GCPChaos)和应用层故障(例如 JVMChaos)三大类,几乎涵盖了分布式测试体系中基础故障模拟的绝大多数场景。详情可参考 Chaos Mesh 文档 

专为 Kubernetes 设计

在容器世界中,Kubernetes 是绝对的领导者。本质上,Kubernetes 是用于云的操作系统。Chaos Mesh 基于 Kubernetes CRD (Custom Resource Definition) 构建,原生支持 Kubernetes 环境,提供了强悍的自动化能力。

基于 Chaos Mesh 的混沌实验

针对上面常规测试手段无法覆盖的故障场景,基于 Chaos Mesh 得到了完美实现。首先,在平台上去注入对应的方法异常故障,这个时候逻辑走到了异常处理的代码块,再注入故障,进入回滚,继续对回滚逻辑注入故障,到此就完成了这个异常场景的可测性。

每刻的不少业务是使用 Java 异步方式实现的。因此 JVM 故障注入是我们使用最多的混沌实验类型。Chaos Mesh 通过 Byteman 模拟 JVM 应用故障,主要支持以下类型的故障:

  • Exception(抛出异常)

  • GC (Java 垃圾回收)

  • Latency (方法延迟)

  • Return

JVM 注入的流程是这样的:用户填写配置提交 -> 创建 crd 资源到 K8s 集群 -> Chaos Mesh 获取到实验配置,向应用所在服务器上部署的 chaos daemon 发送请求 -> chaos daemon 执行 bminstall 安装 byteman java agent, 执行 bmsubmit 命令注入混沌实验。

下面将通过几个 JVM 应用故障的注入案例来详细说明。在进入具体类型之前,需要在 Chaos Dashboard 里选择集群和 JVM 实验大类。

案例 1:Exception

参数:

  • Class:全类名的路径

  • Method:方法名称

  • Port:指 Byteman agent 使用的端口,要保证不和其他服务的端口冲突

参数:

提交之后如果正常注入后,会有如下展示:

调用接口后,查看到我们注入的方法报错,报错信息就是我们注入的报错:

可以发现, 在方法 com.maycur.dingtalk.facade.UnifyFormFacade.getApprovalUnifyForms 的位置抛出了前面注入的异常 java.lang.NullPointerException。

案例 2:Latency(方法延迟)

参数:

  • Latency:延迟时间(ms)

  • Class:全类名的路径

  • Method:方法名称

  • Port:指 Byteman agent 使用的端口,要保证不和其他服务的端口冲突

其他的参数填写跟例 1 中 Exception 一样

可以明显的看到,接口很明显的延迟了,说明我们的注入是成功的。

案例 3:修改接口返回

由于 Chaos Mesh 的 RETURN 故障注入方式,只支持基本数据类型以及 String 类型的返回值,但实际情况中,我们的数据返回的大多都是一个对象,甚至是 List 包了一层对象。这样就需要我们自己去写一段脚本了。

脚本是 Byteman 原生支持的规则配置文件,可以参考 Byteman 文档 。比如下图,待审批列表的,返回了一个 List 包了一个对象:

那么对应的 btm 脚本如下:

RULE return new info

CLASS com.maycur.dingtalk.facade.UnifyFormFacade

METHOD getApprovalUnifyForms

AT EXIT

BIND

Page:com.github.pagehelper.PageInfo=\$!;

UnifyFormList:com.maycur.dingtalk.dto.UnifyFormListDto=Page.getList().get(0);

IF true

DO

UnifyFormList.setInvoiceBagCode(\"123\");

return Page;

ENDRULE

可以看到接口返回已经被修改:

解释一下上图代码的意思:

  • CLASS:全类路径;

  • METHOD:方法名;

  • AT EXIT:表示这段脚本在函数 return 的时候被触发;

  • BIND:将上下文中的数据绑定到变量中, 以便后文使用;

  • Page:com.github.pagehelper.PageInfo=$!:定义了一个名为 Page 类型为com.github.pagehelper.PageInfo 的变量, 其值为$!, 即该函数的返回值;

  • UnifyFormList:com.maycur.dingtalk.dto.UnifyFormListDto=Page.getList().get(0):定义了名为UnifyFormList的变量, 类型为com.maycur.dingtalk.dto.UnifyFormListDto 值为 Page.getList().get(0);

  • DO:表示脚本被触发时执行的操作;

  • UnifyFormList.setInvoiceBagCode("123"):通过 setter 设置修改后的值;

  • return Page:返回 Page 对象;

简单来说,首先获取到该函数的返回值(一个列表),然后获取列表的第一个元素,然后通过该元素所属的类型提供的 set 方法修改它的值,从而达到修改函数返回值的目的。Byteman 提供了强大的功能,对于复杂的故障功能实现,具体可参考 Byteman 规则文档 

网站文章

  • 正在从以下位置加载符号 VS 调试非常慢

    正在从以下位置加载符号 VS 调试非常慢

    前两天突然出现了一个.net未知异常的调试询问? 是否启动VS实例进行调试, 我选择了调试. 然后我自己的项目调试一下就变得非常慢了. 一直出现 ”正在从以下位置加载xxxxxx符号” 一个个加载, 非常慢. 往常启动调试 几秒钟就把这些加载好了. 但是现在几分钟都加载不完. 网上查了一下, 禁用 Microsoft 符号服务器, 清空符号缓存 即可

    2024-02-01 05:08:53
  • MySQL常见错误及处理方法

    1205 - Lock wait timeout exceeded; try restarting transaction出现此错误是因为语句被锁住了,所以释放这个锁,首先查询被锁的语句select * from information_schema.innodb_trx;执行kill命令kill 141865400;

    2024-02-01 05:08:25
  • Hive 常用存储、压缩格式

    Hive 常用存储、压缩格式

    【代码】Hive 常用存储、压缩格式。

    2024-02-01 05:08:18
  • 一篇文章解析所有C语言里字符串的操作

    @函数名称: strtok 函数原型: char *strtok(char *s1, const char *s2) 函数功能: 分解s1字符串为用特定分隔符分隔的多个字符串(一般用于将英文句分解为单词) 函数返回: 字符串s1中首次出现s2中的字符前的子字符串指针 参数说明: s2一般设置为s1中的分隔字符 规定进行子调用时(即分割

    2024-02-01 05:08:10
  • Ubuntu的8080端口被占用

    Ubuntu的8080端口被占用

    因为最近学习web建站技术,弄了好多环境,今天开机启动服务的时候报了如下错误: 意思就是我的8080端口已经被别的进程占用了,导致服务启动异常。解决办法so easy, 找到占用8080端口的进程,直接kill掉就好啦。百度一下,第一步使用 netstat -ntl | grep 8080 查看8080端口的占用情况: 再使用命令 lsof -i:8080 就可以列出占用80...

    2024-02-01 05:07:41
  • Redis快速入门(基本操作 一)

    Redis快速入门(基本操作 一)

    Redis简介: Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 Nosql 泛指非关系型的数据库。随着互联网web2.0网站的兴起,传统的关系...

    2024-02-01 05:07:34
  • 华为工程师终于把困扰我多年的「操作系统和计算机网络」讲明白了

    华为工程师终于把困扰我多年的「操作系统和计算机网络」讲明白了

    计算机网络、计算机操作系统这两个“兄弟”是所有开发岗位都需要“结拜”的,不管你是 Java、C++还是测试。对于后端开发的童鞋来说,计算机网络的重要性不亚于语言基础,毕竟平时开发经常会和网络打交道,比如:抓个包等等。所以对这一块知识点的准备还是要抱着敬畏之心,不要放过任何一个漏网之题。

    2024-02-01 05:07:26
  • 跟着小刘一起入门一下Sharding jdbc

    跟着小刘一起入门一下Sharding jdbc

    文章目录sharding jdbc -缘起分库分表的出现小结分库分表带来问题Sharding JDBC 介绍快速入门 sharding jdbc -缘起 前段时间,在工作接手了一个新的项目,我问Bos...

    2024-02-01 05:07:18
  • SQL数据库

    SQL数据库

    SQL简介

    2024-02-01 05:06:48
  • Spring AOP 中的两种动态代理

    Spring AOP 中的两种动态代理

    Spring AOP 主要用到的 动态代理,在spring aop的实现中,采用了两种方式,一种是基于接口的动态代理,另外一种是基于类的动态代理。在谈动态代理前,先谈下设计模式中的代理模式:代理模式:为另一对象提供一个替身或占位符以控制对这个对象的访问。类图如下:1,基于接口的动态代理必须首先要定义接口:代码如下:package co...

    2024-02-01 05:06:41