​ 把一些前后端概念性比较强的理念放在这里,本片涉及微前端、serverless、wasm、单元测试等

  

https://segmentfault.com/a/1190000038774393?utm_source=sf-related

WebAssembly

wasm并不是一种编程语言,而是一种新的字节码格式,目前,主流浏览器都已经支持 wasm。与 JavaScript 需要解释执行不同的是,wasm字节码和底层机器码很相似可快速装载运行,因此性能相对于 JavaScript 解释执行有了很大的提升。

WebAssembly(缩写为 Wasm)是基于堆栈的虚拟机的二进制指令格式。Wasm 被设计为可移植目标,用于编译高级语言(如 C / C ++ / Rust),从而可以在 Web 上为客户端和服务器应用程序进行部署。

WebAssembly 的开放标准是在 W3C 社区组(包括来自所有主要浏览器的代表)和 W3C 工作组中开发的。

WebAssembly 或称 wasm 是一个实验性的低端编程语言,应用于浏览器内的客户.WebAssembly 是便携式的抽象语法树,被设计来提供比 JavaScript 更快速的编译及运行。WebAssembly 将让开发者能运用自己熟悉的编程语言(最初以 C/C++ 作为实现目标)编译,再藉虚拟机引擎在浏览器内运行。

WebAssembly 的开发团队分别来自 Mozilla、Google、Microsoft、Apple,代表着四大网络浏览器 Firefox、Chrome、Microsoft Edge、Safari。

2017 年 11 月,以上四个浏览器都开始实验性的支持 WebAssembly。

WebAssembly 于 2019 年 12 月 5 日成为万维网联盟(W3C)的推荐,与 HTML,CSS 和 JavaScript 一起,成为 Web 的第四种语言。

WebAssembly 是一种新的编码方式,可以在现代的网络浏览器中运行 - 它是一种低级的类汇编语言,具有紧凑的二进制格式,可以接近原生的性能运行,并为诸如 C / C ++ 等语言提供一个编译目标,以便它们可以在 Web 上运行。它也被设计为可以与 JavaScript 共存,允许两者一起工作。

特点

高效快捷

Wasm 堆栈机设计为以节省大小和加载时间的二进制格式进行编码。WebAssembly 旨在通过利用广泛平台上可用的通用硬件功能,以本机速度执行。

对于网络平台而言,WebAssembly 具有巨大的意义——它提供了一条途径,以使得以各种语言编写的代码都可以以接近原生的速度在 Web 中运行。

在这种情况下,以前无法以此方式运行的客户端软件都将可以运行在 Web 中。

WebAssembly 被设计为可以和 JavaScript 一起协同工作——通过使用 WebAssembly 的 JavaScript API,你可以把 WebAssembly 模块加载到一个 JavaScript 应用中并且在两者之间共享功能。

这允许你在同一个应用中利用 WebAssembly 的性能和威力以及 JavaScript 的表达力和灵活性,即使你可能并不知道如何编写 WebAssembly 代码。

而且,更棒的是,这是通过 W3C WebAssembly Community Group 开发的一项网络标准,并得到了来自各大主要浏览器厂商的积极参与。

安全

WebAssembly 描述了一种内存安全的沙盒执行环境,该环境甚至可以在现有 JavaScript 虚拟机内部实现。当嵌入 Web 时,WebAssembly 将强制执行浏览器的同源和权限安全策略。

传统从 JS 代码,在浏览器端运行,是有被拿到源代码的可能(即使你加密了);

WebAssembly 使用二进制 (.wasm文件)的形式,在这方面跨出一步

开放且可调试

WebAssembly 旨在以文本格式漂亮地打印,以便手工调试,测试,实验,优化,学习,教学和编写程序。在 Web 上查看 Wasm 模块的来源时,将使用文本格式。

开放式网络平台的一部分

WebAssembly 旨在维护 Web 的无版本,经过功能测试和向后兼容的性质。WebAssembly 模块将能够调用和退出 JavaScript 上下文,并通过可从 JavaScript 访问的相同 Web API 访问浏览器功能。WebAssembly 还支持非 Web 嵌入。

可移植——能够在不同硬件平台和操作系统上运行。 保密——WebAssembly是一门低阶语言,是一种紧凑的二进制格式,具有良好的保密性。 安全——WebAssembly被限制运行在一个安全的沙箱执行环境中。像其他网络代码一样,它遵循浏览器的同源策略和授权策略。 兼容——WebAssembly的设计原则是与其他网络技术和谐共处并保持向后兼容。

历史

JavaScript一开始就是动态类型解释性语言,动态类型解释性语言的一大特点就是灵活和慢。

所以JavaScript和所有的动态类型语言一样,天生会比静态类型语言慢。

而随着网页应用越来越复杂,JavaScript的运行性能就必须跟着提高。

那么为什么动态类型语言,如Python、PHP、JavaScript就会比C/C++等静态类型语言慢呢?

JS慢

我们来看一个最简单的情况,实现c = a + b加法运算,如果是C/C++语言,实现步骤大致如下:

内存a里取值到寄存器 内存b里取值到寄存器 算加法 把结果放到内存c 如果是JavaScript大致会经历哪些步骤呢?

代码编译 当前上下文是否有变量a,没有的话去上一层寻找,直到找到,或到达最外层上下文 当前上下文是否有变量b,没有的话去上一层寻找,直到找到,或到达最外层上下文 判断变量a的变量类型 判断变量b的变量类型 变量a、变量b是否需要进行类型转化,并进行类型转化 算加法 运行结果赋值给c 我们可以看到,二者的整个流程还是有比较大的区别

JIT

JIT(just-in-time compilation):如果在执行c = a + b的时候,a和b几乎都是int类型,那么是否可以去掉类型判断,类型转化的步骤,用接近C/C++的方式来实现加法运算,并把执行代码直接编译成机器码,直接运行,不需要再次编译。

Google 在 2009 年在 V8 中引入了 JIT 技术,JavaScript的执行速度瞬间提升了 20 - 40 倍的速度。 JIT的问题是并不是所有的代码都能得到很好的提升,因为JIT基于运行期分析编译,而JavaScript是一个没有类型的语言,所以当代码中的类型经常变化的时候,性能提升是有限的。

比如

function add (a, b) { return a + b } var c = add(1, 2); JIT 看到这里, 觉得好开心, 马上把 add编译成

function add (int a, int b) { return a + b } 但是,很有可能,后面的代码是这样的

var c = add("hello", "world"); JIT 编译器的可能当时就哭了,因为add已经编译成机器码了,只能推到重来

asm.js

2012年,Mozilla 的工程师 Alon Zakai 在研究LLVM编译器时突发奇想:许多 3D 游戏都是用 C / C++语言写的,如果能将 C / C++语言编译成 JavaScript 代码,它们不就能在浏览器里运行了吗?

于是,他开始研究怎么才能实现这个目标,为此专门做了一个编译器项目Emscripten。这个编译器可以将 C / C++代码编译成 JS代码,但不是普通的JS,而是一种叫做 asm.js的 JavaScript变体。

asm.js它的变量一律都是静态类型,并且取消垃圾回收机制。当浏览器的JavaScript引擎发现运行的是 asm.js时,就会跳过语法分析这一步,将其转成汇编语言执行。asm.js的执行速度可以达到原生代码的50%。

asm.js的一般工作流程为:

C/C++

LLVM

Emscripten

JavaScript

asm.js还是存在几个问题:

  1. 仅有FirFox的浏览器有良好的支持
  2. 代码传输还是与现有方式一样,传输源码,本地编译

Mozilla,Google,Microsoft, 和Apple 觉得 Asm.js 这个方法有前途,想标准化一下,大家都能用。 便诞生了WebAssembly。

有了大佬们的支持,WebAssembly比 asm.js要激进很多。 WebAssembly连编译 JS这种事情都懒得做了,不是要 AOT吗? 我直接给字节码好不好?(后来改成 AST 树)。对于不支持 Web Assembly的浏览器, 会有一段JavaScript把 Web Assembly重新翻译为 JavaScript运行。

2019年12月5日,万维网联盟(W3C)宣布 WebAssembly成为正式标准

Serverless

Serverless 是前端圈近两年比较火热的词汇,通过 Serverless 这种服务形态,用户在使用对应的服务时,不需要关心或较少关心服务器的硬件资源、软件资源、稳定性等等,完全托管给云计算厂商,用户只需要专注自己应用代码本身,上传执行函数到相应云计算平台,按照函数运行的时长按量付费即可

演进史

云计算诞生后,用户可以直接购买云主机(VM),把基础物理硬件和网络的管理都交由供应商管理,多用户租用一台物理机,减少了用户硬件管理成本,我们通常称之为 IaaS(Infrastructure-as-a-Service)。

随着软件的发展和容器技术的兴起,计算环境由 VM 发展到更小粒度的容器,在容器中可以运行不同的软件服务,PaaS(Platform-as-a-Service) 和 CaaS(Container-as-a-Service) 也开始映入眼帘。用户使用平台基础软件如 Database、消息等开发自己的应用,使用容器镜像构建和部署应用,最后托管给平台。

继续向前发展,应用的运行演变为更细粒度函数的运行,用户开发特定业务的处理函数,托管给函数平台,按需使用相关的后端服务,通过特定条件的触发完成开发者业务逻辑函数的计算。用户无需为应用持续付费,只需支付函数运行时产生的资源消耗费用,而这,就是 Serverless 服务的模型。

基本架构

Serverless 架构由两部分组成,即 Faas 和 BaaS。

Faas

FaaS(Function-as-a-Service)即为函数运行平台,用户无需搭建庞大的服务系统,只需要上传自己的逻辑函数如一些定时任务、数据处理任务等到云函数平台,配置执行条件触发器、路由等等,完成基础函数的注册。

Faas 运行函数的容器是无状态的,上一次的运行效果和下一次的运行效果是无关的。如果需要存储状态,则需要使用云储存或者云数据库。

Faas 函数如果长时间未使用,容器就会对其进行回收。所以函数在首次调用或长时间未使用时,容器就需要重新创建该函数的实例,这个过程称为冷启动,一般耗时为数百毫秒。

Faas是通过事件驱动的,当一个任务被触发时,比如 HTTP 请求,API Gateway 接受请求、解析和认证,传递对应参数给云函数平台,平台中执行对应回调函数,配合 DB、MQ 等 BaaS 服务在特定容器中完成计算,最终将结果返回给用户。函数执行完成后,一般会被 FaaS 平台销毁,释放对应容器,等待下一个函数运行。

既然有冷启动,就有热启动。例如容器刚刚调用完函数,过一会又有新的事件触发。这时由于函数仍未被回收,所以可以直接复用原有的函数实例,这被称为热启动。

Faas 如果单独使用的话,那它只适合部署一些工具类函数。因为它是无状态的,每次运行都可能是在不同的容器上,它不知道上一个函数的运行结果。所以如果要使用 Serverless 来部署整个应用,还得额外购买 OSS 云存储或者云数据库来提供数据存储服务(也就是需要配合 Baas 来使用)。

Baas

BaaS(Backend-as-a-Service)包含了后端服务组件,它是基于 API 的第三方服务,用于实现应用程序中的核心功能,包含常用的数据库、对象存储、消息队列、日志服务等等

Faas与Baas

Faas 其实是一个云计算平台,用户可以将自己写的函数托管到平台上运行。而 Baas 则是提供一系列的服务给用户运用,用户通过 API 调用。

其他不同点:

  • Faas 无状态,Baas 有状态。
  • Faas 运行的是函数,由开发者自己编写;Baas 提供的是服务,不需要开发者自己开发。

可以说 Faas 和 Baas 是两个不同的东西,但它们有一个共同点,就是无需自己管理服务器和资源的分配、整理,所以都属于 Serverless。

应用场景

Serverless 其实是通过事件驱动的,当一个任务被触发时,比如 HTTP 请求,API Gateway 接受请求、解析和认证,传递对应参数给云函数平台,平台中执行对应回调函数,配合 DB、MQ 等 BaaS 服务在特定容器中完成计算,最终将结果返回给用户。函数执行完成后,一般会被 FaaS 平台销毁,释放对应容器,等待下一个函数运行。

优缺点

优点

Serverless 最大的优点就是自动扩展伸缩、无需自己管理。

在以往部署一个应用时,需要经历购买服务器、安装操作系统、购买域名等等一系列步骤,应用才能真正的上线。后来有了云服务器,我们就省去了购买服务器、安装操作系统这些操作步骤。只需要在云服务器上搭建环境、安装数据库就可以部署应用了。

但是这仍然有个问题,当网站访问量过大时,你需要增加服务器;访问量过小时,需要减少服务器。如果使用 Serverless,你就不需要考虑这些,云服务商会帮你管理这一切。云服务商会根据你的访问量自动调整所需的资源。

缺点

当应用部署在云上,并且使用云存储或云数据库,那可能会让我们的应用访问速度变得比较慢。因为网络的访问速度比内存和硬盘差了一到两个数量级。

灰度发布/蓝绿部署/滚动发布

在一般情况下,升级服务器端应用,需要将应用源码或程序包上传到服务器,然后停止掉老版本服务,再启动新版本。但是这种简单的发布方式存在两个问题,一方面,在新版本升级过程中,服务是暂时中断的,另一方面,如果新版本有 BUG,升级失败,回滚起来也非常麻烦,容易造成更长时间的服务不可用。

蓝绿部署

所谓蓝绿部署,是指同时运行两个版本的应用,蓝绿部署的时候,并不停止掉老版本,而是直接部署一套新版本,等新版本运行起来后,再将流量切换到新版本上。但是蓝绿部署要求在升级过程中,同时运行两套程序,对硬件的要求就是日常所需的二倍,比如日常运行时,需要 10 台服务器支撑业务,那么使用蓝绿部署,你就需要购置二十台服务器。

滚动发布

所谓滚动升级,就是在升级过程中,并不一下子启动所有新版本,是先启动一台新版本,再停止一台老版本,然后再启动一台新版本,再停止一台老版本,直到升级完成,这样的话,如果日常需要 10 台服务器,那么升级过程中也就只需要 11 台就行了。

但是滚动升级有一个问题,在开始滚动升级后,流量会直接流向已经启动起来的新版本,但是这个时候,新版本是不一定可用的,比如需要进一步的测试才能确认。那么在滚动升级期间,整个系统就处于非常不稳定的状态,如果发现了问题,也比较难以确定是新版本还是老版本造成的问题。

为了解决这个问题,我们需要为滚动升级实现流量控制能力。

灰度发布

灰度发布(又名金丝雀发布,起源是,矿井工人发现,金丝雀对瓦斯气体很敏感,矿工会在下井之前,先放一只金丝雀到井中,如果金丝雀不叫了,就代表瓦斯浓度高)是指在黑与白之间,能够平滑过渡的一种发布方式。在其上可以进行A/B testing,即让一部分用户继续用产品特性A,一部分用户开始用产品特性B,如果用户对B没有什么反对意见,那么逐步扩大范围,把所有用户都迁移到B上面来。灰度发布可以保证整体系统的稳定,在初始灰度的时候就可以发现、调整问题,以保证其影响度。

灰度期:灰度发布开始到结束期间的这一段时间,称为灰度期。

在灰度发布开始后,先启动一个新版本应用,但是并不直接将流量切过来,而是测试人员对新版本进行线上测试,启动的这个新版本应用,就是我们的金丝雀。如果没有问题,那么可以将少量的用户流量导入到新版本上,然后再对新版本做运行状态观察,收集各种运行时数据,如果此时对新旧版本做各种数据对比,就是所谓的 A/B 测试。

当确认新版本运行良好后,再逐步将更多的流量导入到新版本上,在此期间,还可以不断地调整新旧两个版本的运行的服务器副本数量,以使得新版本能够承受越来越大的流量压力。直到将 100% 的流量都切换到新版本上,最后关闭剩下的老版本服务,完成灰度发布。

如果在灰度发布过程中(灰度期)发现了新版本有问题,就应该立即将流量切回老版本上,这样,就会将负面影响控制在最小范围内

总结

在新版本应用发布时,为了服务器不停机升级,使用灰度发布策略,在灰度发布开始时,使用 HTTP Header 匹配指定测试人员的流量到新版本上,然后当新版本内部测试通过后,可以再按百分比,将用户流量一点一点导入到新版本中,比如先导入 10% 观察一下运行情况,然后再导入 20%,如此累加,直到将流量全部导入到新版本上,最后完成升级,如果期间发现问题,就立即取消升级,将流量切回到老版本。

运用灰度发布,就再也不需要加班到深夜进行停机升级了,在白天就可以放心大胆地、安全地发布新版本

AB测试

前端测试

测试是保证代码质量的重要环节,web项目的单元测试虽然不能完全完成功能测试,但是却能保证底层单一模块的工作质量,并且在代码重构的时候保证对外接口不会发生变化。

经常会提到的敏捷开发,单元测试就是其中必不可少的一步。因此单元测试的需要,尤其是自动化单元测试不可忽略,而且应当作为整个团队的关键责任-而不仅仅是软件开发人员的责任。

  • 单元:相对独立功能模块,类、模块、方法。
  • 又称为模块测试,是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作。
  • 用来检验程式的内部逻辑,也称为个体测试、结构测试或逻辑驱动测试。

单元测试的重要性

由于存在浏览器解析环境、用户操作习惯等差异,前端程序的许多问题是无法捕捉或重现的,现在前端程序的测试多是黑盒测试,即靠点击点击点击来寻找程序bug。这种方式既费时费力,又无法保证测试的覆盖面。同时,前端逻辑和交互越来越复杂,和其他编程语言一样,一个函数,一个模块,在修改bug或添加新功能的过程中,很容易就产生新的bug,或使老的bug复活。这种情况下,反复进行黑盒测试,其工作量和测试质量是可想而知的。

  • 反正都要手动测试,所以不如代码自动化。
  • 为了实现依赖接口编程,大型软件项目多人合作时必须要有的
  • 首先,得让你的代码能够测试
  • 增强代码自信

黑盒测试

  • bug无法捕捉、重现
  • 费力,工作量大
  • 覆盖面低
  • 反复出现bug

单元测试

  • 并不是所有的 js 都需要单元测试。中大型项目
  • 并不是所有的 js 都能够单元测试。良好的模块化和解耦

TDD与BDD、相关概念

TDD:测试驱动开发(Test-Driven Development)测试驱动开发是敏捷开发中的一项核心实践和技术,也是一种设计方法论。TDD的原理是在开发功能代码之前,先编写单元测试用例代码,测试代码确定需要编写什么产品代码。TDD的基本思路就是通过测试来推动整个开发的进行,但测试驱动开发并不只是单纯的测试工作,而是把需求分析,设计,质量控制量化的过程。TDD首先考虑使用需求(对象、功能、过程、接口等),主要是编写测试用例框架对功能的过程和接口进行设计,而测试框架可以持续进行验证。

BDD:行为驱动开发(Behavior Driven Development)行为驱动开发是一种敏捷软件开发的技术,它鼓励软件项目中的开发者、QA和非技术人员或商业参与者之间的协作。主要是从用户的需求出发,强调系统行为。BDD最初是由Dan North在2003年命名,它包括验收测试和客户测试驱动等的极限编程的实践,作为对测试驱动开发的回应。

测试套件”(test suite):describe (moduleName, testDetails)。可以嵌套使用,明白、易懂即可。describe块称为”测试套件”(test suite),表示一组相关的测试。它是一个函数,第一个参数是测试套件的名称(”加法函数的测试”),第二个参数是一个实际执行的函数。

测试用例”(test case):it (info, function)。具体的测试语句,可多个。it块称为”测试用例”(test case),表示一个单独的测试,是测试的最小单位。它也是一个函数,第一个参数是测试用例的名称(”1 加 1 应该等于 2”),第二个参数是一个实际执行的函数。

  • info,写期望的正确输出的简要一句话文字说明。info:当该it 的block内的test failed的时候控制台就会把详细信息打印出来。
  • 测试用例之中,只要有一个断言为false,这个测试用例就会失败,只有所有断言都为true,测试用例才会通过。
  • function,具体测试函数,一个测试用例内部,包含一个或多个断言(assert)。

断言指的是对代码行为的预期,会返回一个布尔值,表示代码行为是否符合预期。

  • 所有的测试用例(it块)都应该含有一句或多句的断言。
  • 断言是编写测试用例的关键
  • 断言功能由断言库来实现,Mocha本身不带断言库,所以必须先引入断言库。
  • 断言库有很多种,Mocha并不限制使用哪一种。

单元测试生命周期

每个测试块(describe)有4个周期函数:before、beforeEach、afterEach、after

周期函数 存在周期 主要功能
before() 在本区块的所有测试用例之前执行 用于同一的桩数据导入等功能
beforeEach() 在本区块的每个测试用例之前执行 用于清理测试环境,删除或回滚相关数据
afterEach() 在本区块的每个测试用例之后执行 可以用于准备测试用例所需的前置条件
after() 在本区块的所有测试用例之后执行 可以用于准备测试用例所需的后置条件

测试用例结构:

a. Setup: 准备好环境和数据,跑这个测试用例之前的准备

b. Execution:执行测试(测试用例的实现的主要代码)

c. Validation:验证结果

d. Cleanup:现场恢复,一般与a相反。不影响跑后面的测试用例。

TDD相关接口:

suite:定义一组测试用例。

suiteSetup:此方法会在这个suite所有测试用例执行前执行一次,只一次,这是跟setup的区别。

setup:此方法会在每个测试用例执行前都执行一遍。

test:具体执行的测试用例实现代码。

teardown:此方法会在每个测试用例执行后都执行一遍,与setup相反。

suiteTeardown:此方法会在这个suite所有测试用例执行后执行一次,与suiteSetup相反。

单元测试与集成测试、功能测试

三种测试的主要作用

  • 单元测试,单个组件正常工作,开发阶段;用来确保每个组件正常工作 —— 测试组件的 API 。
  • 集成测试,不同组件互相合作,中间阶段;用来确保不同组件互相合作 —— 测试组件的 API, UI, 或者边缘情况(比如数据库I/O,登录等等)。
  • 功能测试,主要测试界面,开发完成。 用来确保整个应用会按照用户期望的那样运行 —— 主要测试界面

三种测试在不同阶段的重要性

  • 开发阶段,主要是程序员反馈。这时单元测试很有用。
  • 在中间阶段,主要是能够在发现问题时立刻停下来。这时各种测试都很有用。
  • 在生产环境,主要是运行功能测试套件,确保部署的时候没有弄坏什么东西。

DDD驱动测试

e2e测试

即UI测试

Chaos混沌工程

https://cloud.tencent.com/developer/article/1622874

2014年,Netflix团队创建了一种新的角色,叫作混沌工程师(Chaos Enigneer),并开始向工程社区推广。项目目标、业务场景、人员结构、实施方式的不同导致了对于稳定状态行为的定义不太标准

多元化的业务场景、规模化的服务节点及高度复杂的系统架构,每天都会遇到各式各样的故障。这些故障信息就是最真实的混沌工程变量。为了能够更体感、有效率地描述故障,优先分析了P1和P2的故障,提出一些通用的故障场景并按照IaaS层、PaaS层、SaaS层的角度绘制了故障画像

混沌工程的定义

根据 Netflix 的解释,混沌工程师通过应用一些经验探索的原则,来学习观察系统是如何反应的。这就跟科学家做实验去学习物理定律一样,混沌工程师通过做实验去了解系统。

混沌工程的典型代表 - 猴子。拜 Netflix 所赐,现在大部分的混沌工程项目都叫做 Monkey,也就是一只讨厌的猴子,在你的系统里面上蹦下窜,不停捣乱,直到搞挂你的系统。

然后,我们需要知道,为什么需要混沌工程。应用混沌工程能提升整个系统的弹性。通过设计并且进行混沌实验,我们可以了解到系统脆弱的一面,在还没出现对用户造成伤害之前,我们就能主动发现这些问题。

混沌工程其实是很重要的,但我之前一直以为混沌工程就是测试,但它们还是有区别的。虽然混沌工程跟传统测试通常都会共用很多测试工具的,譬如都会使用错误注入工具,但混沌工程是通过实践对系统有更新的认知,而传统测试则是使用特定方式对某一块进行特定测试。譬如在传统测试里面,我们可以写一个断言,我们给定特定的条件,产生一个特定的输出,如果不满足断言条件,测试就出错了,这个其实是具有很明确的特性。但混沌工程是试验,而试验会有怎样的新信息生成,我们是不确定的。譬如我们可以进行下面的这些试验:

  • 模拟整个 IDC 当掉
  • 选择一部分网络连连接注入特定时间的延迟
  • 随机让一些函数抛出异常
  • 强制 NTP 时间不同步
  • 生成 IO 错误
  • 榨干 CPU

这些试验到底会有什么样的结果,有些我们可以预料,但有些可能我们就不会预先知道,只有发生了,才会恍然大悟,有一种『喔,这也会出现!』的感叹。

原则

http://principlesofchaos.org/

在开始应用混沌工程之前,我们必须确保系统是弹性的,也就是当出现了系统错误我们的整个系统还能正常工作。如果不能确保,我们就需要先考虑提升整个系统的健壮性了,因为混沌工程主要是用来发现系统未知的脆弱一面的,如果我们知道应用混沌工程能导致显而易见的问题,那其实就没必要应用了。

虽然 chaos 有混乱的意思,但混沌工程并不是制造混乱。相反,我们可以认为混沌工程是用经验的方法来定位问题的一门实验学科。譬如,我们可以思考:『如果我们在系统里面注入混乱了,这个系统会怎样?』,或者『我们系统离混乱的边界还有多远?』。所以,为了更好的进行混沌试验,我们需要有一些原则来进行指导。

假定稳定状态

在一个复杂系统里面,我们有特别多的组件,有很多不同的输入输出,我们需要有一个通用的方式来区别系统哪些行为是可以接受的,而哪一些则是不合适的。我们可以认为当系统处于正常操作时候的状态就是稳定状态。

通常我们可以通过自己测试,来确定一个系统的稳定状态,但这个方法当然是比较低效的,另一种更常用的做法就是收集 metric 信息,不光需要系统的 metric,也需要服务自身的 metric,但收集 metric 需要注意实时性的问题,你如果收集一个每月汇总的 metric 信息,其实没啥用,毕竟系统是实时变化的。现在市面上面有很多不错的开源 metric 系统,譬如我们就在用的 Prometheus。

当我们能收集到信息之后,就需要用这些信息去描述一个稳定状态。这个难度比较大,因为不同的业务是不一样的,即使是同一个业务,不同时间也可能变化很大。但也有一些方法,譬如我们可以根据前面一段时间譬如上周的 metric 的曲线得到一个大概合理的稳定状态,也可以自己做很多压力测试,得到相关的数据。

当有了 metric 以及知道稳定状态对应的 metric 是怎样之后,我们就可以通过这些来考虑混沌实验了。思考当我们注入不同的事件到系统中的时候,稳定状态会如何变化,然后我们就会开始做实验来验证这个假设。

变更真实世界

在真实的世界中,我们可能遇到各种各样的问题,譬如:

  • 硬件故障
  • 网络延迟和隔离
  • 资源耗尽
  • 拜占庭错误
  • 下游依赖故障

做混沌试验的时候需要模拟这些故障,来看系统的状态。但从成本上面考虑,我们并不需要模拟所有的故障,仅仅需要考虑那些会比较频繁发生,而且模拟之后会很有效果的。在 TiDB 里面,我们主要就是模拟的网络,文件系统的故障,但现在看起来还是不够,后面会逐渐的添加更多。

在生产中进行实验

要看混沌试验有没有效果,在真实生产环境中进行验证是最好的方法。但我相信大部分的厂商还没这么大的魄力,这方面 Netflix 就做的很猛,他们竟然能够直接停掉 Amazon 上面的一个 Zone。

如果不能再生产环境中试验,一个可选的方法就是做 shadow,也就是通常的录制生产环境的流量,然后在测试中重放。或者模拟生产环境,自己造数据测试。

自动化持续执行

最开始执行混沌试验,我们可能就是人工进行,试验进行的过程中,看 metric,试验结束之后,通过收集的 metric 在对比,看系统的状态。这个过程后面完全可以做成自动化的,也就是定期执行,或者系统发布的时候执行等。

如果能做到自动化执行试验,已经很不错了,但我们可以做的更多,甚至有可能根据系统的状态自动化的生成相关的试验,

最小化影响范围

在进行混沌试验的时候,一定要注意影响的范围,如果没预估好,把整个服务搞挂了,导致所有的用户都没法使用,这个问题还是很严重的。

通常都会使用一种 Canary 的方法,也就是类似 A/B 测试,或者灰度发布这种的,在 Canary 集群这边做很多试验。也就是说,如果真的搞坏了,那也只是一小部分用户被搞坏了,损失会小很多。

在 Canary 里面还有一个好处,因为我们知道整个系统的稳定状态,即使不做混沌测试,也可以观察 Canary 里面的状态是不是跟之前的稳定状态一致的,如果不一致,那也可能有问题。

实践

上面我们说了相关的原则,那么如何开始进行一次混沌试验呢?其实很简单,只要做到如下步骤就可以:

  1. 选择一个假设
  2. 选择试验的范围
  3. 明确需要观察的 metric 指标
  4. 通知相关的团队
  5. 执行试验
  6. 分析结果
  7. 增大试验的范围
  8. 自动化

混沌成熟度模型

Netflix 总结了两个维度,一个是复杂度,一个就是接受度。前者表示的是混沌工程能有多复杂,而后者则表示的是混沌工程被团队的接受程度。

复杂度分为几个阶段:

  • 初级
  • 试验没有在生产中进行
  • 进程被收工管理
  • 结果只反映系统 metric,没有业务的
  • 只有简单的事件进行试验
  • 简单
  • 试验可以在类生产环境中进行
  • 能自动启动执行,但需要人工监控和终止
  • 结果能反应一些聚合的业务 metric
  • 一些扩展的事件譬如网络延迟可以进行试验
  • 结果可以手工汇总和聚合
  • 试验是预先定义好的
  • 有一些工具能进行历史对照
  • 复杂
  • 试验直接在生产环境中进行
  • 启动,执行,结果分析,终止都是自动完成
  • 试验框架集成在持续发布
  • 业务 metrics 会在实验组和控制组进行比较
  • 一些组合错误或者服务级别影响的事件可以进行试验
  • 结果一直可以追踪
  • 有工具可以更好的交互式的对比试验和控制组
  • 高级
  • 试验在每个开发步骤和任意环境都进行
  • 设计,执行和提前终止这些全部都是自动化的
  • 框架跟 A/B 或者其他试验系统整合
  • 一个事件譬如更改使用模式和返回值或者状态变更开始进行试验
  • 试验包括动态作用域和影响,可以找到突变点
  • 通过试验结果能保护资产流失
  • 容量预测可以通过试验分析提前得出
  • 试验结果可以区分不同服务的临界状态

而接受度也有几个阶段:

  • 在暗处
  • 相关项目不被批准
  • 很少系统被覆盖
  • 很少或者没有团队有意识
  • 早期接受者不定期的进行试验
  • 有投入
  • 试验被被官方批准
  • 部分资源被用于实践
  • 多个团队有兴趣并投入
  • 少部分关键服务不定期进行试验
  • 接受
  • 有专门的 team 进行混沌工程
  • 应急响应被集成到框架,从而可以创建回归试验
  • 多数关键系统定期进行混沌试验
  • 一些试验验证会在应急响应或者游戏时间被临时执行
  • 文化
  • 所有关键服务都有频繁的混沌试验
  • 大多数非关键服务定期进行
  • 混沌试验已经是工程师的日常工作
  • 默认所有系统组件都必须参与,如果不想进行,需要有正当的理由

Netflix工程师创建了Chaos Monkey,使用该工具可以在整个系统中在随机位置引发故障。正如GitHub上的工具维护者所说,“Chaos Monkey会随机终止在生产环境中运行的虚拟机实例和容器。”通过Chaos Monkey,工程师可以快速了解他们正在构建的服务是否健壮,是否可以弹性扩容,是否可以处理计划外的故障。 2012年,Netflix开源了Chaos Monkey。今天,许多公司(包括谷歌,亚马逊,IBM,耐克等),都采用某种形式的混沌工程来提高现代架构的可靠性。 Netflix甚至将其混沌工程工具集扩展到包括整个“Simian Army(中文可以译为猿军)”,用它攻击自己的系统。

混沌工程属于一门新兴的技术学科,行业认知和实践积累比较少,大多数IT团队对它的理解还没有上升到一个领域概念。阿里电商域在2010年左右开始尝试故障注入测试的工作,希望解决微服务架构带来的强弱依赖问题。 混沌工程,是一种提高技术架构弹性能力的复杂技术手段。Chaos工程经过实验可以确保系统的可用性。混沌工程旨在将故障扼杀在襁褓之中,也就是在故障造成中断之前将它们识别出来。通过主动制造故障,测试系统在各种压力下的行为,识别并修复故障问题,避免造成严重后果。

它,被描述为“在分布式系统上进行实验的学科,目的是建立对系统承受生产环境中湍流条件能力的信心。”。

它也可以视为流感疫苗,故意将有害物质注入体内以防止未来疾病,这似乎很疯狂,但这种方法也适用于分布式云系统。混沌工程会将故障注入系统以测试系统对其的响应。这使公司能够为宕机做准备,并在宕机发生之前将其影响降至最低。

如何知道系统是否处于稳定状态呢?通常,团队可以通过单元测试、集成测试和性能测试等手段进行验证。但是,无论这些测试写的多好,我们认为都远远不够,因为错误可以在任何时间发生,尤其是对分布式系统而言,此时就需要引入混沌工程(Chaos Engineering)。

与故障注入、故障测试的区别

混沌工程、故障注入和故障测试在关注点和工具中都有很大的重叠。

混沌工程和其他方法之间的主要区别在于,混沌工程是一种生成新信息的实践,而故障注入是测试一种情况的一种特定方法。当想要探索复杂系统可能出现的不良行为时,注入通信延迟和错误等失败是一种很好的方法。但是我们也想探索诸如流量激增,激烈竞争,拜占庭式失败,以及消息的计划外或不常见的组合。如果一个面向消费者的网站突然因为流量激增而导致更多收入,我们很难称之为错误或失败,但我们仍然对探索系统的影响非常感兴趣。

同样,故障测试以某种预想的方式破坏系统,但没有探索更多可能发生的奇怪场景,那么不可预测的事情就可能发生。 混沌工程以实验发现系统性弱点。这些实验通常遵循四个步骤:

1.定义并测量系统的“稳定状态”。首先精确定义指标,表明您的系统按照应有的方式运行。 Netflix使用客户点击视频流设备上播放按钮的速率作为指标,称为“每秒流量”。请注意,这更像是商业指标而非技术指标;事实上,在混沌工程中,业务指标通常比技术指标更有用,因为它们更适合衡量用户体验或运营。

2.创建假设。与任何实验一样,您需要一个假设来进行测试。因为你试图破坏系统正常运行时的稳定状态,你的假设将是这样的,“当我们做X时,这个系统的稳定状态应该没有变化。”为什么用这种方式表达?如果你的期望是你的动作会破坏系统的稳定状态,那么你会做的第一件事会是修复问题。混沌工程应该包括真正的实验,涉及真正的未知数。

3.模拟现实世界中可能发生的事情,目前有如下混沌工程实践方法:模拟数据中心的故障、强制系统时钟不同步、在驱动程序代码中模拟I/O异常、模拟服务之间的延迟、随机引发函数抛异常。通常,您希望模拟可能导致系统不可用或导致其性能降低的场景。首先考虑可能出现什么问题,然后进行模拟。一定要优先考虑潜在的错误。 “当你拥有非常复杂的系统时,很容易引起出乎意料的下游效应,这是混沌工程寻找的结果之一,”“因此,系统越复杂,越重要,它就越有可能成为混沌工程的候选对象。”

4.证明或反驳你的假设。将稳态指标与干扰注入系统后收集的指标进行比较。如果您发现测量结果存在差异,那么您的混沌工程实验已经成功 - 您现在可以继续加固系统,以便现实世界中的类似事件不会导致大问题。或者,如果您发现稳定状态可以保持,那么你对该系统的稳定性大可放心。

如果你是一名系统架构师、测试人员、SRE 人员,那你需要关注混沌工程。

满足一下部分特征的企业,都适合实施混沌工程:

对于资损或 SLA 有极高要求的行业(金融、电商、医疗、游戏、公共等) 有较大的系统改造(比如:系统重构、微服务化、容器化、迁云) 业务发展快,稳定性积淀少 人员流动快,或 SRE 团队规模较小

chaos-engineering的一些开源工具

kube-monkey、PowerfulSeal、ChaosIQ,提供了一些容器层面的故障注入能力。https://github.com/dastergon/awesome-chaos-engineering

阿里chaos blade:https://github.com/chaosblade-io/chaosblade

ChaosBlade 是阿里巴巴开源的一款遵循混沌工程原理和混沌实验模型的实验注入工具,帮助企业提升分布式系统的容错能力,并且在企业上云或往云原生系统迁移过程中业务连续性保障。

Chaosblade 是内部 MonkeyKing 对外开源的项目,其建立在阿里巴巴近十年故障测试和演练实践基础上,结合了集团各业务的最佳创意和实践。

ChaosBlade 不仅使用简单,而且支持丰富的实验场景,场景包括

  • 基础资源:比如 CPU、内存、网络、磁盘、进程等实验场景;
  • Java 应用:比如数据库、缓存、消息、JVM 本身、微服务等,还可以指定任意类方法注入各种复杂的实验场景;
  • C++ 应用:比如指定任意方法或某行代码注入延迟、变量和返回值篡改等实验场景;
  • Docker 容器:比如杀容器、容器内 CPU、内存、网络、磁盘、进程等实验场景;
  • 云原生平台:比如 Kubernetes 平台节点上 CPU、内存、网络、磁盘、进程实验场景,Pod 网络和 Pod 本身实验场景如杀 Pod,容器的实验场景如上述的 Docker 容器实验场景;

Chaos Mesh

Chaos Mesh 是云原生计算基金会 (CNCF) 托管的项目。它是一个云原生混沌工程平台,可在 Kubernetes 环境中编排混沌。在当前阶段,它具有以下组件:

  • Chaos Operator:混沌编排的核心组件。完全开源。
  • Chaos Dashboard:用于管理、设计、监控混沌实验的 Web UI。

Chaos Operator 以可管理的方式将混沌注入应用程序和 Kubernetes 基础设施,为混沌实验和自动编排提供简单的自定义定义。有三个组件在起作用:

  • Controller-manager:用于调度和管理 CRD 对象的生命周期。
  • Chaos-daemon:作为守护进程运行,具有特定节点的网络、Cgroup 等特权系统权限。

Chaos Operator 使用 CustomResourceDefinition (CRD) 来定义混沌对象。目前的实现支持几种类型的CRD对象进行故障注入,分别是DNSChaos、PodChaos、PodIOChaos、PodNetworkChaos、NetworkChaos、IOChaos、TimeChaos、StressChaos和KernelChaos,分别对应以下主要动作(实验):

  • pod-kill:选定的 pod 被杀死(可能需要 ReplicaSet 或类似的东西来确保 pod 将重新启动)。
  • pod-failure:选定的 pod 在指定时间内不可用。
  • container-kill:选中的容器在选中的 pod 中被杀死。
  • netem chaos:延迟、重复等网络混乱。
  • network-partition:模拟网络分区。
  • IO chaos:模拟文件系统故障,如I/O延迟、读/写错误等。
  • time chaos:选定的 pod 将被注入时钟偏差。
  • cpu-burn:模拟所选pod的CPU压力。
  • memory-burn:模拟所选pod压力的内存。
  • memory-burn:选定的 pod 将被注入(slab、bio 等)错误。
  • dns chaos:选中的pod会被注入dns错误,如error、random。

接口工具

互联网公司常用的接口文档管理平台

Swagger

Swagger是一个大型的API开发者的工具框架,该框架提出了一个编写OpenAPI的规范(命名为OAS),并且Swagger可以跨整个API生命周期进行开发,从设计和文档到测试和部署。 Swagger框架三核心:

  • 提供了一个编写API文档的规范 ,称为OAS ,在规范中明确API的格式和一些编写要素;
  • 提供相关的工具,对API文档的编写提供辅助。主要是这么几个项目 Swagger Editor、SwaggerUI、Swagger Codegen、Swagger Inspector;
  • 提供对各种流行语言和框架的集成,例如集成SpringMVC 的 springfox 框架;

Yapi

YApi 是高效、易用、功能强大的 api 管理平台,旨在为开发、产品、测试人员提供更优雅的接口管理服务。它可以帮助开发者轻松创建、发布、以及维护API。除此之外,YApi 还为用户提供了优秀的交互体验,开发人员只需利用平台提供的接口数据写入工具以及简单的点击操作就可以实现接口的管理。特性:

  • 基于 Json5 和 Mockjs 定义接口返回数据的结构和文档,效率提升多倍
  • 扁平化权限设计,即保证了大型企业级项目的管理,又保证了易用性
  • 类似 postman 的接口调试
  • 自动化测试, 支持对 Response 断言
  • MockServer 除支持普通的随机 mock 外,还增加了 Mock 期望功能,根据设置的请求过滤规则,返回期望数据
  • 支持 postman, har, swagger 数据导入
  • 免费开源,内网部署,信息再也不怕泄露了。

Eolinker

Eolinker是国内企业级IT研发管理解决方案服务品牌,在线API接口管理服务供应商,致力于满足各行业客户在不同应用环境中对研发管理全生命周期的个性化需求,提供API开发管理(AMS)、开发团队协作、自动化测试、网关(AGW)以及监控(AMT)等服务。 特性:

  • 接口信息的录入与导出
  • 在线测试
  • 团队协作管理
  • 支持数据字典的录入
  • 用户常用到的小工具
  • 对状态码进行管理

ShowDoc

ShowDoc一个非常适合IT团队的在线API文档、技术文档工具。 随着移动互联网的发展,BaaS(后端即服务)越来越流行。服务端提供API,APP端或者网页前端便可方便调用数据。用ShowDoc可以非常方便快速地编写出美观的API文档。

DOClever接口管理工具

DOClever是一个可视化接口管理工具 ,可以分析接口结构,校验接口正确性, 围绕接口定义文档,通过一系列自动化工具提升我们的协作效率。 特性:

  • 1 接口快照回滚,项目版本控制
  • 2 兼容最新版Swagger,PostMan等平台数据
  • 3 接口文档自动在线生成
  • 4 Restful,Query,Header,Body,Raw信息一应俱全,独有的proxy技术加持为您冲破内网的束缚

RAP2接口管理工具

阿里妈妈前端团队出品的开源接口管理工具RAP第二代,RAP通过GUI工具帮助WEB工程师更高效的管理接口文档,同时通过分析接口结构自动生成Mock数据、校验真实接口的正确性,使接口文档成为开发流程中的强依赖。有了结构化的API数据,RAP可以做的更多,而我们可以避免更多重复劳动。基于RAML的接口定义、文档生成、Mock Server完成了定义和使用的分离,通过一套规范完成的接口定义,可以用不同的工具得到适应不同API管理系统的输出,有更多的可能性,同时保持了核心定义不变。RAP较之于RAML,前者更加集中,所有的定义、文档、mock都在同一个服务中完成,并且实时生效,方便快捷,如果只考虑方便易用,RAP是更好的选择,而RAML显得更加繁琐,更适合于公开的接口定义,方便在各个系统之间流转。

Apifox

https://zhuanlan.zhihu.com/p/140802042

npm私库搭建

npm install --globale verdaccio

前端CDN资源

表情/ICON资源

表情包资源:https://app.streamlinehq.com/emojis/kawaii-emoji

前端bundleless

随着业务的发展,前端代码的复杂度越来越高,构建方面展露新的问题:

过去需要打包的原因:

1.http1.1各浏览器有并行连接限制

2.浏览器不支持模块系统(如commonjs不能在浏览器直接运行)

3.代码依赖关系与顺序管理

可以开始不打包的原因:

1.http2.0多路并用

2.各大浏览器逐一支持ESM

3.越来越多的npm包拥抱ESM(尽管还有很多包不是)

目前构建主要分为两种:

1是基于服务的构建方式。通常服务于实际生产。可以再细分成本地服务构建与远端服务构建。本地服务构建就是我们常规的操作,基本被webpack统治,是bundle方案的代表,snowpack、Vite、Web Dev Server是目前比较火的Bundleless方案,发展迅猛。远端服务构建则是依托云能力的玩法,把构建过程放在服务端完成。

2是基于浏览器的构建方式。通常面向Demo的快速搭建或预览方案。Codesandbox、StackBlitz、CodePen和Riddel是业内比较出色的方案。整体是在浏览器端实现代码的编译、打包、构建和运行。

重构

原则

  1. 事不过三,三则重构。即不能重复写同样的代码,在这种情况下要去重构。
  2. 如果一段代码让人很难看懂,那就该考虑重构了。
  3. 如果已经理解了代码,但是非常繁琐或者不够好,也可以重构。
  4. 过长的函数,需要重构。
  5. 一个函数最好对应一个功能,如果一个函数被塞入多个功能,那就要对它进行重构了。(4 和 5 不冲突)
  6. 重构的关键在于运用大量微小且保持软件行为的步骤,一步步达成大规模的修改。每个单独的重构要么很小,要么由若干小步骤组合而成。

手段:

  1. 提取重复代码,封装成函数
  2. 拆分功能太多的函数
  3. 变量/函数改名
  4. 替换算法
  5. 以函数调用取代内联代码
  6. 移动语句
  7. 折分嵌套条件表达式
  8. 将查询函数和修改函数分离
如果你觉得我的文章对你有帮助的话,希望可以推荐和交流一下。欢迎關注和 Star 本博客或者关注我的 Github