前端微服务在字节跳动的打磨与应用

本文探讨了服务发现在微服务架构中的作用,包括客户端和服务端的不同实现方式,以及其如何简化微前端的部署和维护。文章还重点讲述了运行隔离的重要性,通过沙盒技术和Serverless环境一致性工具,确保了开发和线上环境的一致性。
摘要由CSDN通过智能技术生成

“工程师”的任务不是说证明一个结构在理论上是可以存在的就完了,要有建造这个结构的过程。比如你拿化学键可以算出来任何可能存在的分子,画个小人都可以。但是到底怎么合成,按照什么路径能让这种分子被制造,哪种路径最快最便宜,这个是过程的可能性。通常这才是工程师的任务。

服务发现


服务发现的方面我们会首先讲一下在整个微服务模式里他作用是什么、有哪些方式。然后第二部分讲到底在解决什么问题,以及多说一些他能提供的新能力。我们很重视新能力因为我们的定位不是消防队,灭了火就完成任务,还有很多新目标、很多新好处可以探索实现。

最后是讲一下在字节跳动具体是怎么实现的。

1. 原理

“服务发现”就是原来的单体服务拆分之后,本来一个项目里的方法分开部署了,谁也找不到谁。需要有个统一的注册机构,把提供服务的各个部署都查到。

“发现”就是当你想访问一个微服务,你要怎么找到他。

这样就有两种构型,一个是以 Netflix OSS 为典型的,它在客户端的机器里先拿到一个服务目录,处理逻辑在客户端的代码里。另一种是服务端的服务发现,AWS 就是如此。

传统微服务的服务发现更像是函数调用的替代,拆了之后怎么调到不同容器里部署的函数。这里微前端思路非常类似,作用略有区别。微服务的情况是会区分像什么订阅、通知、请求、发布这些,前端很可能都不用或者表现上不在前端运行时使用。还有一些例如“对单”、“对多”这些基本就是前端不太用考虑的东西。

两者一致的地方是都谁也不认识谁了,如何知道哪些服务存在、谁在提供?下面就要讲一下各种构型的服务发现和背后的服务注册分别具体是什么情况。

客户端服务发现 是说客户端——也就是服务的调用者,去请求一个注册的目录,里面包含所有服务和负载均衡的基本信息这些,然后自己决定如何处理,使用哪种具体的 load balance 策略。比如 Netflix 的 OSS,服务在 Netflix Eureka 注册一下,它心跳给各个客户端。客户端自己搞,简单直观。

服务端服务发现 是类似 AWS Elastic LoadBalancer 这种。客户端请求就完了,服务端决定怎么给你反向代理、负载均衡。

服务注册 分自注册和第三方注册。自注册不言而喻。第三方注册就是一个保活机制,定期检查服务状态,帮你去管控该上了还是下了。

我们主要用的是第一种:客户端服务发现,就是你要多请求一个模块列表。这个列表给出的资源是根据用户 session 决定的,有丰富的动态的能力。然后客户端再根据这个列表里的各种信息,去加载模块资源。

2. 给前端带来了什么?

用服务发现的方式去组织微前端,除了使复杂的上线流程变得解耦、快捷,还可以使拆散之后的工程版本方便对齐,实现更高的稳定性和可调试性。还对前端工程带来好多其他好处。下面主要讲一下各种收益中的最重要的两个。

快速上线 是什么概念呢,前面说了几十个业务和在一个项目里,一起发布,这样发布的频率能有多高?实际考察一下放开限制后的情景,就会发现有超乎意料的高。我们的一个微前端应用的业务是头条号,它在 2019 年上半年发了 2000 个版本。前面说了传统上线需要 30 分钟才能完成打包升级和容器的重启,并且 10 分钟才能完一个回滚,这就意味着 1000 小时的上下线等待时间。相比之下我们新的方式点一下 HTTP 请求发出去就生效了,是一个毫秒级的反应速度。

这个搁以前就不是慢、需要干等着的问题了,直接大家就不这样去发了。都学 Native 发版那样火车式发布。结果是响应效率降低了很多,很多需求渐渐变得不再由开发形成瓶颈,反而是总要等版本排发布。

独立切换 我们现在就分别发,可以一个单页应用分几十个模块,各自上各自的、下各自的。而且后面会说到还可以各自配置自己的 AB 测试版:有 10 个模块就可以产生 1024 个 AB 版的组合,20 个模块 100 万个。跟以前完全不敢想象——也就是说一起发版的时代根本做不了这个事。现在不敢想象的反而是,你说字节跳动某个业务里面不能做 AB 测。

我们的头条号平台就是刚才一个典型的微前端项目,包含列出的这么多模块,各模块有独立的版本,和对服务版本的 session 控制。每个模块进去都是版本列表,有一个模块所有的历史版本。通过这个平台配置小流量、AB、上线规则。

运行隔离


1. 耦合开发的严峻形势

17 年我们推进项目的时候有一个很不错的帖子很流行,红遍朋友圈那种,讲 react-loadable 的。ta 从解耦的维度介绍了这个方向。我们当时也有一个很明确的业务需求,要把公司不同部门的人组织到一个项目里。并且这个项目经过经年累月的增肥,已经非常臃肿并且积攒了很多值得推敲的、非直接技术的工程细节。这就意味着要用不同组织,不同的技术,不同的工程规范和打包工具,去合写同一个平台、同一个工程。如果当时用了 iframe 可能就是非常凑合的勉强满足业务,完全不符合我们追求极致的习惯。

然后当时我们很在意一点就是这种跨团队合作,想融合不同的技术团队,实现少费力沟通或者不重沟通,运行隔离是个非常绝对的基本前提,我们其他分享里面也用了不小的篇幅介绍,有对内的也有对外的。当时的效果是什么呢。这个是我们 18 年 4 月内部培训录制到的当时情况:

我们把线上的页面(左图)通过调试工具插入脚本,临时移除掉沙盒功能,得到的右图效果。

2. 运行隔离的目标

运行隔离是啥意思,回想一下刚才说的 AB 测的问题,20 个项目是多少个组合。如果把这个对应到 bug 的维度,大家都在一个应用里乱跑会有多恐怖。那么这样的组合对我们的程序和程序员提出了什么样的要求?

不跑挂 说是“对一切工程师最基本要求”,我觉得不算夸张。所有软件工程师的第一个能力层级都应该是不把系统拖垮。微服务之后这个问题不明显了,因为有架构层面的方式解决了绝大多数挑战。我很信服的一个理论是所有程序员都是四个阶段:写完需求,不拖垮别人,能扩容,性能好。

不干扰 也是另一个大问题,我们当时西瓜团队和头条是两个独立的 App,他们和我们的合作完全跨部门,连 polyfill 的规则都不一样。事先也是做了很多公共组件、 CSS 约定之类的。但是规范和约定远远不够。协作的境界从最差到最好应该是:

  • 定规范:谁来了都好好学、好好听,自己对自己的行为负全责。

  • 能 enforce 规范:不凭自觉,而是用工具和流程等手段去发现和强制,实现可靠性。

  • 不需要规范:系统的确定性由系统解决。靠人去发现和执行规范是消耗大量认知资源的,带来的都是额外的工作量和系统的不确定性。

3. 沙盒

我们还有另外一篇文章专门介绍沙盒的设计和采坑经验。这篇就快速用几张图示意一下。

① 变量保护: 全局变量、 DOM 和 CSS 基本都是走的这条路。前后两次快照,我们来比较,之后根据需要帮你恢复现场。这块内容不细说了,看一眼图就不言而喻:一次比较对照所有 key、两次遍历、黑名单 location、白名单 readonly。估计我这样一说大家都懂。

② 沙盒时序: 稍微多说一些。右图是我们做的 ABCDE 五个模块的加载和混行的时序图。虚线左边是加载,右边是独占线程所占用的时间。也就是说有 ABCDE 五个模块五个沙盒,分别在这个模块编译(下载、创建 js 变量和函数、运行这些语句、最终生成一个 React Component)和运行时(这个模块被打开、渲染对应的所有功能)。

这里面两个基础:js 单线程、事件循环

我们用了非常单纯的单进程操作系统的思路,比喻一下就是 js 的单线程就像单核 CPU 一样。你激活一个模块,相当于激活一个线程,其他都退到背景里。

实际上单核单进程不是必然,大家都知道这个原理。在事件循环的基础上,我们可以封装所有的异步操作,把回调套在沙盒激活后面。比如 setTimeoutaddEventListener,这样每个模块看起来就像是在并行。这块可以说的很多,但是就想一下操作系统的比喻就好了。

4. 加载方式

React 的项目用 react-loadable 本身不多说了,VUEraw (也就是不包含展示层框架的原始版)的各种项目,我们都提供 masterpage 的样例,每个版本对应的都实现了一套和 react-loadable 相似的效果。

子模块(Modules) 就是一个个的 CMD 包,我用 new Function 来包起来。其他就是具体主工程(MasterPage)项目框架的约定,load 过程分为 5 个钩子:

  • preload 是否预加载,是个 promisefullfill 的时候就会触发 Ajax。各种空闲政策阻塞政策都可以由 master 制定;

  • loadCondition 编译前置条件,fullfill 了才会开始运行这部分,执行结果就是得到那个 CMD 的 exports

  • provider 是一个模块的入口的函数,由模块开发者提供,返回模块的一切输出。这个函数的传入参数由 masterpage 主工程来提供。

  • loaded 完成加载,得到编译结果了。

  • 等等后面不说太细了。

环境一致


因为我们之前都是在讲微服务是什么和落地效果如何,从来没有讲过 推行一个微服务你得做什么。现在这部分内容是我们第一次公开分享的,也是一个很独立的维度。

其实就是在讲为什么对微前端来说这个环境一致工具是必须的,是绕不过的必经之路。如果不搞也很容易就栽进坑里,项目失败。然后很可能还不知道是为何失败的,把问题归结为框架不好啊、人不好啊甚至微前端就不好啊之类的问题上。

1. Serverless vs container

container 就是一个寄生环境,尽管这个环境还是挺特殊的,不像 linux 这种完整操作系统。相比之下 Serverless 就特殊多了。特殊到连谷歌云都曾经在商业上被击败。

这里举两个 Serverless 的例子,比如 lambda。它的本地工具是一个 CLI 系统:SAM,是一个非常典型的必要基础设施。如果说发展容器化 AWS 全是靠 docker,发展 lambda 就是靠的 SAM。

另一个典型的例子 firebase。想必前端的同学们都非常清楚,也都用过他们的开发套件。这些工具都非常重视一点就是本地开发,我做个项目到底能不能先测试再上。或者说先调试在上。

要做的话就是尽可能模拟真实环境了,SAM 的话就模拟了 API Gateway,memory limit 这些。有 live debugging、 local debugging。不然的话发什么疯有人敢把线上业务放到一个非常不同的环境下运行。

2. 你是不是环境有问题(在我这是好的)

标题程序员最常说的一句话对不对,另一种表达是“我这是好的”。大家都知道绝大多数情况这么说话不对,但常常会忍不住说。甚至更像是其实是对自己说。一种扪心自问,自我拷问,“我这是好的啊”。

我们用沙盒把微前端做成了像 container,像浏览器里的 docker。但是不够,我们还是把寄生在 masterpage 内这种应用框架的特征,也就是业务具体逻辑,看成是一种 Serverless。

然后我们还把隔离的思路做到极致,我们的 dev 命令是通过启动参数启动一个完全独立的 Chrome 会话,有自己的 cookie 啊缓存啊这些,效果像是装了 2 个 Chrome 乃至多个 Chrome。然后代理工具默认也配到了启动参数,是个 pac 文件。所以也可以单独用或者装 switchy 用。

代理工具 就是调试环境的整个配置,那些走测试环境、哪些走线上请求全部代理管理。生成一个动态的 pac 地址和代理服务。就是刚才说的。

关键请求,比如服务发现的请求,显然是代理掉的。走一个我们为本地环境定制的返回值。更细节的功能是我们可以协助调试主工程(MasterPage)、 组合上某个 module,你也可以用指定的 MasterPage 版本来调用你正在编写的模块。

你也可以指定是否加载完整的线上模块列表、只替换你正在调试的模块。

我们也还有完整的植入 webpack dev server 的服务供选择。前面说了支持任意打包工具,这块是解耦的,只不过你用了我们可以帮你 reload,部分刷新动态刷新。后面再细说。

发布检查 是针对服务注册这一块。这块的一部分,我们的 build 命令有一套检查,对应 git 钩子。

方便调试 我们还在一定程度上支持了 HMR。我们可以像开发一个普通前端应用一样开发主工程(MasterPage)和子模块(Module),子模块更新后改变模块管理器状态,并由内置的 eventbus 机制来重新渲染 HMR,这个机制也可以用到盛传环境。

我们的公共库可以通过在 MasterPage 项目里引入、子模块里 external 的方式实现模块间共享。也支持子模块使用特定版本的基础库。

Vue 用到了全局变量及原型链扩展,暂时还不支持 Hot Reload 的调试。

其他的框架优势


框架上看就是 serverless 的方向。不是真的 serverless 是前端 serverless,业务 module 开发者很多东西都不用再关心了。举个例子就是 console.log。现在大家都知道线上业务要干干净净体体面面,把 console 都收拾整齐。这是我们之前提到的规范的层面,我们可以做到吸收所有 console,存储错误堆栈。然后用户反馈的时候作为 trace 元数据提交到反馈后台等等。

这些都是 masterpage 层面的框架了。当然不是必然关系。但是可以说微前端给了一个非常方便像这样组织项目的渠道。

我们线上的 sourcemap 也是根据服务发现的管理后台权限控制的,只有开发者能看。

下一代前端展望

=======

前面讲了,服务发现是一个对前端可用资源的总体管理。这个能力是不局限于运行时的微服务前端的。对一切资源都适用,下面说一下这块。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

最后

今天的文章可谓是积蓄了我这几年来的应聘和面试经历总结出来的经验,干货满满呀!如果你能够一直坚持看到这儿,那么首先我还是十分佩服你的毅力的。不过光是看完而不去付出行动,或者直接进入你的收藏夹里吃灰,那么我写这篇文章就没多大意义了。所以看完之后,还是多多行动起来吧!

可以非常负责地说,如果你能够坚持把我上面列举的内容都一个不拉地看完并且全部消化为自己的知识的话,那么你就至少已经达到了中级开发工程师以上的水平,进入大厂技术这块是基本没有什么问题的了。

新!**

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

最后

今天的文章可谓是积蓄了我这几年来的应聘和面试经历总结出来的经验,干货满满呀!如果你能够一直坚持看到这儿,那么首先我还是十分佩服你的毅力的。不过光是看完而不去付出行动,或者直接进入你的收藏夹里吃灰,那么我写这篇文章就没多大意义了。所以看完之后,还是多多行动起来吧!

可以非常负责地说,如果你能够坚持把我上面列举的内容都一个不拉地看完并且全部消化为自己的知识的话,那么你就至少已经达到了中级开发工程师以上的水平,进入大厂技术这块是基本没有什么问题的了。

资料领取方式:戳这里前往获取

  • 27
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值