当前位置: 首页 > 后端技术 > Node.js

Flare制作记录:应用前后端性能优化

时间:2023-04-04 01:30:14 Node.js

两周前,我向flame在线提交了几个PR后,打包成一个容器,用于书签和在线应用管理。但是在迁移个人书签的过程中,发现flame的性能不是特别好,于是做了一个改进版:flare。在我谈论耀斑之前,我想谈谈火焰。Flame是一个漂亮的在线导航页面项目,你可以在上面存放你经常使用的书签和在线应用程序。由波兰小哥创建,项目地址为https://github.com/pawelmalak/flame。试用后觉得项目还不错,于是做了一些调整,打包了一个新的镜像:https://github。com/soulteary/docker-flame记录了我在项目文档中的修改:简化程序功能和依赖(比如K8S),减小包体积,重构一些细节逻辑,简化应用启动流程。重写天气获取逻辑,用城市名代替经纬度获取天气数据。修复了程序中的一些小bug,支持中文搜索。该程序已简化为中文。但是随着深入使用,我发现该页面存在比较大的性能问题。由于波兰小哥采用了SPA方案,该项目包含了6700多个SVG图标和数种网页字体,界面也存在一些小问题,导致页面体积非常大,界面请求量也很大。即使你上传一些元素繁重的SVG作为你的书签图标,React触发的页面渲染也会导致浏览器卡顿。我大概有几百个书签需要处理,估计以后书签的数量还会增加,所以我用程序批量创建了近千个书签。然后我发现渲染这么多书签,页面会卡顿,甚至在页面中搜索包含关键字的书签也会出现明显的掉帧现象。所以,我决定重写一个更轻的程序来解决我的需求。新项目地址在这里。好奇的可以试试:https://github.com/soulteary/docker-flare制作flare的过程其实就是flame性能调优的过程。但在解决问题之前,首先要能够定位问题。应用性能问题分析这个应用的性能优化其实并不复杂,和传统的应用优化没有太大区别:优先考虑减少计算量,如果不能减少,就用计算效率更高的方式来解决这个问题。不过在使用场景方面,在分析技术问题之前,可以先从功能入手。对于不适用我的功能,首先从功能的角度来说,我不需要这个应用集成Docker来提供“服务发现”的功能。比如我启动容器后,这个应用会自动把新启动的容器添加为书签或者应用。其次,有了自己的SSO服务后,不再需要使用简单的账号密码登录等功能,所以这个功能也可以去掉。最后,关于书签数据的存储,我觉得在没有用户体验很好的web编辑器的情况下,可能不如配置声明的方式那么容易管理和维护。(你可以使用任何你喜欢的编辑器来更新和维护内容,你可以使用Git或任何你喜欢的东西,并将你自己的数据保存在一个白盒中)。基于以上改动,我大概可以省掉几部分代码:容器(Docker&K8S)集成、登录认证、应用和书签、书签分类的“CRUD”。前端架构存在的问题在Flame项目中,笔者使用create-react-app脚手架创建项目。项目依赖为:Reactv17+TypeScript+Redux。为了提供简洁一致的图标,作者引入了Templarian/MaterialDesign-JS,这是一个非常简单的SVG图标库,具有经过精心处理的DOM结构。在服务器端用构建工具打包并GZip压缩后,需要传输接近1MB的资源,而原脚本程序体积接近3MB。相对较大的程序大小会导致较长的页面加载和执行时间。比如一个页面的首次渲染时间在1s左右波动,多数情况下会超过1s,完成时间一般在1.5s以上。可能是作者对服务器端程序开发不够熟悉。虽然在前端更新应用配置时会复用该接口,但在展示内容页面时会调用多达8个接口。另外,为了在页面显示和更新天气信息,波兰小哥还使用了WebSocket进行数据交互。其他问题在文章前面已经提到,这里不再赘述。问题项目后端架构使用的技术栈为Node.js,web框架为市场占有率极高的最新版Express,ORM框架为Sequelize,数据存储实现为SQLite3。以上选择,乍一看问题不大。如果应用程序不需要公开提供浏览访问权限,则应该没有性能问题。但是,如果我们仔细观察服务响应,就会发现有些请求的响应时间非常长,比如页面资源,比如对页面至关重要的JS程序资源,它们的获取耗时接近400ms。另外,前端发起多次请求获取数据,SQLite配合数据存储使用。如果提供对公共内容的访问,很容易遇到性能瓶颈。改进应用当我们清楚地理解了上述问题后,例如,简单的解决方案是:在原有程序的基础上进行重构和调整,简化前端请求,合理拆分模块,处理资源加载和执行时序,调整数据存储和处理提高服务器端响应能力的方法和其他组合拳。然而,这是最简单、最有利可图的解决方案吗?调整前端实现。如果说在需要交互的页面程序中使用MVVM框架会有更高的收益和性价比,那么在没有多端组件代码复用,没有服务端渲染需求的情况下,使用这类框架是一个性价比不高的选择。有的同学可能会问,如果不使用React、Vue、Angular等框架,2022年还得去捡jQuery等老工具吗?虽然有可能,但是在近乎纯展示的场景下,我们可以不用JS实现业务功能和简单的交互,比如自动获取焦点,改变菜单按钮的激活状态,甚至是带有动画效果的天气图标。因此,在调整实现时,其实还有一个选择:不使用任何脚本。程序完成后可以看到从服务器获取整个页面数据、结构分析、样式计算、元素布局、页面绘制的完整时间为33ms(包括空闲等待时间),关键流程耗时不到10ms,页面渲染完成时间缩短至1.65ms。获得页面的快速渲染能力后,即使我们不使用浏览器缓存资源加速渲染,我们也可以实现页面切换的“无刷新”浏览(因为渲染速度足够快)。调整后端实现虽然我很喜欢用Node.js,之前分享过很多基于Node.js的方案或者优化实践,但是为了低成本提升高性能的资源响应,这里需要切换技术栈的:比如Golang。使用Golang简化程序功能后,程序对每个请求的响应基本可以维持在几毫秒的水平(受限于网络传输),比之前降低了大约2到3个数量级。页面关键DOMContentLoad时间缩短为原来的八分之一。结合上面前端优化提到的渲染时间,粗略估计从资源下载到渲染总共不到10ms。如果不是浏览器的某些限制,绘制帧率应该可以远超60帧,这进一步满足了我们“即使有刷新也比一些无刷新实现更流畅”的实现。在上面的实现中,我拆分了页面图标请求和页面文档。在书签和图标类型不多的场景下,它可能不是最好的解决方案,但是一旦书签达到数百或数千,你就会发现图标拆分可以极大地提高性能。当然为了满足比较少的场景,我也实现了合并输出。算上网站favicon的获取,一共只有两次请求。在书签不多的情况下,渲染性能甚至优于文档和资源拆分输出效率。图标资源优化Flame采用的方案是读取后端界面配置,从前端脚本动态创建SVG图标插入到文档中。Flare程序默认的做法是将SVG和document拆分来处理带有大量书签的页面的性能问题。虽然页面性能问题解决了,但是服务器端的IO问题也会随之而来。因此,也需要在服务器端处理好资源的释放和读取,尽量将资源的磁盘IO降为零。听起来很荒谬,但其实结合代码生成的方式,还是相当容易实现的。当然,因为Go有自动GC,当使用不同的资源时,会分配大量内存,影响效率。这里,可以考虑使用持久化方案来解决问题。处理起来还是挺有意思的,限于篇幅和话题就不展开了。前面两篇我提到了一个部分,关于Golang嵌入式资源的使用和优化。例如,在不对HTTP服务实现做任何优化,将运行资源限制在两个核心的前提下,仅优化资源IO后,即可实现稳定的3ms输出资源,每秒提供超过27000次响应服务。除了常规优化容器镜像优化,镜像优化对于容器时代的应用来说也是非常关键的。容器优化方法在上一篇文章中多次提到,不再展开。有兴趣的可以自行阅读前面的内容。#码头图片|grepflasoulteary/flare0.1.122b18ad73c6612mbsoulteary/flaume2.2.0B39FFFC0CA81152MBPAWELMALAK/FLAME2.2.0FA47C93C0AF6179MBPPAWELMALAK/FLAME2.0.0.0.0.0.0729B0优化19B0,大约是以前大小的十五到十六分之一。额外优化如果我们使用lighthouse来测试Flame前端的实现,我们可以看到前端程序的实现存在一些小问题。虽然四个环是绿色的,三个是得分的,但只有一个环是绿色的。在重新实现的过程中,除了精简结构和调试实现之外,这四个圈子也得到了满分(Chrome版本v97+)。终于讲到这里了,相信你已经明白我是怎么做的了,如果你对Flare感兴趣,也需要一个简单的导航程序,可以访问项目https://github.com/soulteary/docker-flare,快来自己试试。--EOF我们有一个折腾小群,聚集了上百个喜欢折腾的朋友。在没有广告的情况下,大家会一起聊软硬件、HomeLab、编程的问题,也会时不时在群里分享一些技术沙龙的信息。喜欢折腾的朋友欢迎扫描二维码加好友。(加好友请备注真实姓名、出处和目的,否则审核不通过)如果觉得关于扔进群的内容还有用,请点赞分享给您的朋友,谢谢你。如果你想更快的看到后续内容的更新,请随时“点赞”或“转发分享”。这些免费的鼓励会影响后续相关内容的更新速度。本文使用“Signature4.0International(CCBY4.0)”许可协议。欢迎转载或重复使用,但需注明出处。Signature4.0International(CCBY4.0)本文作者:苏洋创建时间:2022-01-19统计字数:5054字阅读时间:11分钟阅读链接:https://soulteary.com/2022/01...