点击上方 "程序员小乐"关注, 星标或置顶一起成长

每天凌晨00点00分, 第一时间与你相约

每日英文

Never expect, never assume, and never demand. Just let it be, because if it's meant to be, it will happen the way you want it to.

永不期待,永不假设,永不强求。顺其自然,若是注定发生,必会如你所愿。

每日掏心

人生最遗憾的莫过于:轻易地放弃了不该放弃的,固执地坚持了不该坚持的。。


来自:公众号 前端之巅 | 责编:乐乐

程序员小乐(ID:study_tech)第 695 次推文   图片来自网络

往日回顾:滴滴程序员年薪80万却被亲戚鄙视:钱多有啥用,我儿子二本大学教师

   正文   

如何利用微前端技术实现单体应用程序的现代化改造?在本篇教程中,我们将探讨如何将前端从单体架构当中剥离出来,并快速完成微前端架构迁移。本文作者将结合个人项目实践经验为大家介绍心得。
问题所在

我们假设有这么一个单体代码库,它使用了某种后端模板引擎或者系统(例如 EJS 或者 ERB),但没有认真考虑前端的设计需求。或者更糟糕的是,前端的开发早于 SPA 的出现,或者还可能使用了类似 Ruby on Rails 那样的框架。因此,JavaScript 文件(例如.js.erb 文件或 AEM 片段等等)中可能包含了后端变量。这种粗制滥造且各组件间紧密耦合的代码库几乎无法进行现代化升级。

我们当然希望不要再在这个单体系统中开发前端代码,我们希望转向更加 JavaScript 化的生态系统——但具体该怎么做?

大多数企业都负担不起(或者不愿负担)这种因工具淘汰带来的重写成本与停机时间。功能的演进需要开发的支持,但要保持同样的速度开发这些功能显然越来越困难。

正因如此,我们应该通过一种渐进且平滑的方式将单体逐步拆分为更多较小的部分,同时保证不让业务发生中断。

说来简单,但单体架构的拆分过程相当棘手。在进行前端迁移时需要为支持 JavaScript 的应用程序规划和开发新 API,拆分就变得尤为困难。

在等待新 API 开发和发布的过程中,前端迭代开发、微前端(MFE)实现和团队的自主行动都将陷入僵局。但真的要这样子吗?错!我们可以对前端和后端进行解耦,让它们齐头并进。

Zack Jackson — ScriptedAlchemy

下面将介绍一种方法,它能够顺利解耦前端,并将其移植成具有 SSR 的独立 MFE。如此一来,团队不再需要等待后端 API 被拆分成微服务或者等待后端 API 可用。这个方法叫作“由内而外替换单体”。

阻碍因素

微前端通常包含以下两大重要依赖项:

1) 认证;

2) 提供给应用程序的数据,在浏览器端和在服务器端渲染(SSR)期间。

根据我的个人经验,无论你的遗留系统属于 Rails、Java 还是.Net,用户身份认证一直是最难与单体后端进行剥离的部分。

将单体作为布局引擎

MFE 有多种不同的架构规范。本文将重点介绍其中一种,即在后端微服务中非常流行的一个版本——LOSA(Lots Of Small Applications,大量小应用)。对于“由内而外”迁移来说,这是最理想的选择。

流经单体的 LOSA 请求 / 响应流

LOSA 应用(通常为微前端)属于独立的 Node.js 服务,能够在服务器端渲染网页的一部分或者某些片段。每个页面可以由多个 LOSA 服务组成。这些应用程序 / 微前端单独进行构建、部署,并运行在容器中。

上图所示为同一页面采用了三种不同的渲染方式,演示了一个增量迁移的过程。先是单体渲染页面,再过渡到 LOSA 微前端,然后变成垂直的微前端。最后,单体被彻底替换掉。

当然,单体仍然负责处理 HTTP 请求,并将最终响应发送至客户端。微前端可以放在集群的防火墙后面——仅提供给遗留系统使用,直到 API 网关和用户身份认证机制剥离完成(或者至少已经转化为 API 端点)。在此期间,我们不需要做太多的改动。

渲染流程

下图展示了迁移后的请求 / 响应流程。

首先,发出一个请求:
GET/POST 'https://MFEwebsite.com/parts/header?format=json

渲染页面内容需要各类数据,那些无法从已解耦端点查询到的“缺失”信息可以在请求期间以 props 的形式发送给 MFE。请求会经过一系列中间件,这些中间件负责渲染 React 应用程序,然后调用已解耦的 API,并将响应结果以 props 的形式返回。这些 props 最终将组成 window.INITIAL_STATE。

代 码

关于模板功能或者过滤器的实现方法,我向大家推荐 Hypernova。不过我自己并没用过,我已经习惯了一切自己动手,并在 Rails、Node 以及 PHP 后端中实现了类似的机制。但考虑到各类后端平台都有自己的特点,所以这里我就用 Hypernova 作为示例向大家讲解。

下面使用 express 实现 MFE 渲染端点:

来自另一个系统的请求(在这里就是那个单体):
GET/POST 'https://MFEwebsite.com/parts/header?format=json{   html: '<div> ... </div>',   css: '/static/header.3042u3298423.css',   js: '/static/header.idhf93hf23iu.js',   initial_state: {items:[...]}}
用于处理响应的中间件:
export function exampleRenderAPIware(req, res) {  const renderedMarkup = renderHTMLpage(    req,    this.index,    intial_state,  );  asyncRender.then(() => {    const responseObject = {      html: renderedMarkup,      initial_state,      js: jsResource,      css: cssResource,    };    res.status(200).end(JSON.stringify(responseObject));  });}

发出这些初始 POST 请求的控制器也需要处理响应结果,将 JS 与 CSS 放在正确的位置,最后在遗留模板的对应位置渲染 React。之前通常由其他控制器负责处理的资产现在需要负责将脚本与样式注入到遗留标头与 body 标签的底部。请注意,单体仍然被作为布局引擎。我们也在替换其他部分,并以 React SSR 方式添加新功能。最终,这些 LOSA 应用将通过一个 MFE(或者借助 Webpack 黑魔法,我自己开发了 webpack-external-import)整合在一起。

如何从模板数据迁移至新 API?

在迁移中,解耦并上线新的 API 到底会带来怎样的影响?

之前,在单体把数据传给 MFE 时,express 访问 HTTP 的请求正文。而现在,express 向 API 异步获取数据。虽然数据格式可能会发生变化,但 React 仍然能够正确获取到 props。

性能差异

与旧单体相比,LOSA 架构的性能还不够好,通常需要 400 到 600 毫秒才能渲染出页面的特定部分。我们采用了异步 Worker 结构,这样就可以同时请求多项服务来渲染应用程序的不同部分(而不是渲染单个应用)。但这种作法提高了应用下线的难度,因为“生产故障”会导致侧边栏或页脚部分长时间缺失。因此,进一步拆分才是最好的选择。

我所说的 LOSA 异步 Worker 是这样的:我们使用大量的 Node 服务,每一个服务负责渲染页面的一个或多个组件。

遗留控制器(图中的灰色齿轮部分)可以将视图数据转给 POST 请求,而非后端模板引擎。回收数据机制则能够帮助后端减少支持负担。由于无需做出重大修改,后端开发人员能够腾出时间,专注于解耦数据服务,而前端也可以进行独立的开发。

视图数据被发送给了外部的 React 服务,而响应消息(包含了 HTML、样式表、初始状态以及 CSS URL)则被发送给后端模板引擎。现在,模板引擎只需要渲染 POST 请求所对应的响应,从而将视图或视图的一部分与原有单体剥离开来。

React 渲染时间

React 真的很慢!SSR 也不怎么快——因此新的 LOSA 架构解决方案无法带来理想的性能表现。我们的解决方案是:在 React 内部进行片段缓存。


  • 黄色:无 React 片段缓存——端到端(400 毫秒左右)

  • 深紫:有 React 片段缓存——端到端(150 毫秒左右)

  • 橙色:全优化架构(20 毫秒左右)

  • 绿色(底部):来自后端的原生片段缓存

React 优化工作相当复杂,受篇幅所限,恐怕只能另起一篇文章详加说明了。总之,Graphana 数据显示,我们至少将渲染性能提高了一倍,不过轮循时间仍然很长。尽管 React 已经能够在内部快速完成渲染,但 150 毫秒的端到端时间还没有达到我们的预期。在下一篇文章中,我们将具体聊聊片段后端与片段缓存。

渲染时间 VS 轮回时间

渲染时间一直是个麻烦事,即使是在 React 中采用了片段缓存之后,性能仍然无法令人满意。令我感到失望的是,虽然 Node.js 内部的渲染速度很快(约 20 毫秒),但整个流程仍然需要 140 到 200 毫秒才能完成。

瓶颈所在

  1. JSON 大小,特别是初始应用状态——即渲染页面所需要的最少 state。我们不再在初始渲染中放置太多字符串化的 state,只发送足够让 React 完成渲染并让折叠组件变得可交互的必要 state。
  2. 需要渲染的 DOM 节点数量——不再将代码放在无用的 DIV 中,只需要给它们加个 class。利用 HTML 的语义特性以及 CSS 的级联效果,我们可以少写一些 HTML 代码,这样也就减少了 React.createComponent 函数的生成。
  3. 垃圾回收——我们将在下一篇文章中讨论更多细节。
  4. 速度由数据服务决定——在中间层使用 Redis。很多朋友认为“缓存失效问题难以解决”,我建议各位认真考虑一下事件溯源,或者我们可以使用 CQRS 与异步 Worker 来处理读写操作。
  5. 单体架构与 MFE 之间的 HTTP 开销——也就是 gRPC、CQRS、UDP 以及 Protobuf。二者之间的通信应该通过内部 Kubernetes 网络进行。POST 速度很慢,但也不是不能用。遇到问题时个别处理即可。

如何提升后端渲染性能

简单来说,模板化、片段缓存与 gRPC/CQRS,移除 JSON 中臃肿的数据。React 在服务器端速度较慢,但请记住,一切拆分都只会让速度变得稍慢,而不是更快。

伸缩问题如何解决?
对于一切解决方案,如果不能在规模化场景下实现良好的成本效益,那么都将只是空谈。我们绝对不能容忍天文数字级的运营成本或者糟糕的性价比。大规模且廉价的解决方案才是好的解决方案。下面来看几点容易被忽视的成本要素:
  1. 昂贵的第三方服务费用;
  2. 更多 / 更大的容器环境;
  3. 由于性能不佳而导致的收入损失;
  4. 由于两个分支无法同时被合并到 master,因此单体架构会导致发布周期或部署流程阻塞;
  5. 开发人员在风险较低的环境中可以快速行动,业务人员能够将新想法推向市场,并对出现问题的部分及时回滚——快速行动的能力正是实现高成本效益的必要前提。

最终结果

流量: 1000 万次渲染 / 天

资源分配:
  • 实例: 5

  • 内存: 100mi (100 MB 内存)

  • CPU: 100 (单核)

  • 最大 CPU 使用率阈值: 65%

  • 响应时间:20 至 25 毫秒

  • DOM 复杂度:高

  • 响应时间缩短了 95%


  • 绿色:后端渲染时间

  • 蓝色:使用了片段缓存和 state 优化的 React

我的单线程 JavaScript 应用程序要比使用完整片段缓存的多线程后端系统更快。

原文链接:levelup.gitconnected.com/micro-frontend-architecture-replacing-a-monolith-from-the-inside-out-61f60d2e14c1


欢迎在留言区留下你的观点,一起讨论提高。如果今天的文章让你有新的启发,学习能力的提升上有新的认识,欢迎转发分享给更多人。

欢迎各位读者加入程序员小乐技术群,在公众号后台回复“加群”或者“学习”即可。

猜你还想看

阿里、腾讯、百度、华为、京东最新面试题汇集

Java8 中用法优雅的 Stream,性能也"优雅"吗?

Spring Boot & Restful API 构建实战

Redis的内存淘汰策略,看了都说好!

关注「程序员小乐」,收看更多精彩内容
嘿,你在看吗

微前端架构:如何由内而外取代单体架构相关推荐

  1. 常见的服务器架构入门:从单体架构、EAI 到 SOA 再到微服务和 ServiceMesh

    前言:架构的演变流程 单体架构 ==> 垂直架构 ==> 前后端分离 ==> EAI架构  ==> SOA架构 ==> 微服务 ==> 微服务2.0 1.单体架构: ...

  2. 微服务架构会和分布式单体架构高度重合吗

    在最近的Microservices Practitioner Summit峰会上,来自Facebook的工程师Ben Christensen就目前正在普遍快速增长的分布式系统与二进制依赖关系的一种反面 ...

  3. 分布式架构的演变过程之单体架构

    无论何种技术实现,其学习过程往往都是枯燥无味的,学的越深就越发如是,IT行业更是如此.需要去实现技术是小部分人的工作,如何让不直接接触技术的人知道我们在做什么.怎么做.为什么这么做就显得有那么点意义了 ...

  4. 为何从单体架构迁移到微服务这么难?

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 面对微服务如火如荼的发展,很多人都在了解,学习希望能在自 ...

  5. 单体 soa 微服务 区别_漫谈何时从单体架构迁移到微服务?

    面对微服务如火如荼的发展,很多人都在了解,学习希望能在自己的项目中帮得上忙,当你对微服务的庐山真面目有所了解后,接下来就是说服自己了,到底如何评估微服务,什么时候使用微服务,什么时间点最合适,需要哪些 ...

  6. 微服务架构与单体架构的区别

    一.单体架构和微服务架构 1.什么是单体架构 一个归档包(如war包)包含了应用所有功能的应用程序.常言道:一个war包打天下. 2.单体架构的优缺点: 优点:简单,没有什么花哨的问题需要解决,部署非 ...

  7. 漫谈何时从单体架构迁移到微服务?

    面对微服务如火如荼的发展,很多人都在了解,学习希望能在自己的项目中帮得上忙,当你对微服务的庐山真面目有所了解后,接下来就是说服自己了,到底如何评估微服务,什么时候使用微服务,什么时间点最合适,需要哪些 ...

  8. 微服务架构图_漫谈何时从单体架构迁移到微服务?

    面对微服务如火如荼的发展,很多人都在了解,学习希望能在自己的项目中帮得上忙,当你对微服务的庐山真面目有所了解后,接下来就是说服自己了,到底如何评估微服务,什么时候使用微服务,什么时间点最合适,需要哪些 ...

  9. 微服务连载(二)漫谈何时从单体架构迁移到微服务?

    面对微服务如火如荼的发展,很多人都在了解,学习希望能在自己的项目中帮得上忙,当你对微服务的庐山真面目有所了解后,接下来就是说服自己了,到底如何评估微服务,什么时候使用微服务,什么时间点最合适,需要哪些 ...

最新文章

  1. linux第9天 UDP
  2. Docker创建springboot项目镜像pom及Dockerfile配置文件
  3. javascript Window 对象模型
  4. 手贱随手在Linux敲了 as 命令,出不来了
  5. Kafka设计原理看了又忘,忘了又看?
  6. 荣耀9igoogle模式_iGoogle个性化主页的6种替代方法
  7. 配置ubuntu使用console登录登录欢迎提示
  8. 【每日算法Day 71】面试官想考我这道位运算题,结果我给出了三种解法
  9. linux中检测到时钟错误,make: 警告:检测到时钟错误。您的创建可能是不完整的
  10. Java反编译生成java文件
  11. AMPL 多差产品容量限制运输模型
  12. nfc卡模式与标准模式_NFC(1)NFC简介,3种模式
  13. 助力不文明行为检测识别—基于yolov3-tiny实现抽烟检测
  14. Automatic Detection of Welding Defects Using Faster R-CNN
  15. 锁仓怎么解_期货如何锁仓,解锁以及锁仓的好处
  16. 最薄翻转笔记本 联想Yoga13-IFI 6700
  17. 全球500强的网站只有500个!强者生存!
  18. Python疫情数据采集, 并做可视化展示
  19. 微信公纵平台基本配置php_php微信公众平台开发(一) 配置接口
  20. 51自学网java壁虎_我要自学网JAVA基础4-26日历补充壁虎老师的完整代码

热门文章

  1. CentOS 8 重启网络服务nmcli
  2. 【JY】YJK前处理参数详解及常见问题分析:刚度系数(三)
  3. APP自动化测试——Appium运行环境搭建保姆级教程
  4. 关闭彻底Windows10自动更新工具-Windows Update Blocker
  5. AToken全观:V神发话了,支持钱包开发者收取额外交易费
  6. 制定steam教育理念明文
  7. amebaZ2 amebaZ II RTL8710CM 8710CM
  8. WiFi万能钥匙蹭网原理详细剖析
  9. 《计算机网络》PPT完整下载
  10. vscode - vscode中使用svn插件进行提交代码