共享主机MEAN Stack应用性能优化及懒加载咨询
可落地性能优化方案
数分钟级别的登录、数据库响应延迟属于严重的服务端瓶颈,前端优化只能改善页面加载速度,无法解决接口超时问题,按优先级落地以下手段:
- 共享主机基础资源排查
共享主机的CPU、内存、磁盘IO为多租户共享,先通过top、free -h、iotop命令查看运行时资源占用:- 如果内存占用触顶触发系统swap,给每个Node服务启动时加内存上限参数:
node --max-old-space-size=256 app.js(数值根据主机给你的内存配额调整,三个Node服务+MongoDB总内存占用不要超过配额的80%),避免单个服务占满内存把所有进程挤到交换分区拖慢速度 - 如果磁盘IO跑满,先关闭MongoDB的冗余查询日志,在mongod.conf里把日志级别设为0,配置日志轮转避免单日志文件过大;如果自身服务IO占用不高仍卡顿,大概率是宿主机超售,软件优化收益极低,建议优先反馈主机商或调整部署方案
- 如果内存占用触顶触发系统swap,给每个Node服务启动时加内存上限参数:
- MongoDB性能优化(数据库调用慢的核心诱因)
- 给所有查询条件字段加索引,尤其是登录场景用到的用户表
email/username字段,无索引全表扫描在低IO的共享主机环境下极易出现分钟级延迟,加索引命令参考:db.users.createIndex({email: 1}),可以用db.collection.find(查询条件).explain("executionStats")校验查询是否走索引,返回结果中stage为COLLSCAN就是全表扫,需要补索引 - 调整MongoDB内存占用,在mongod.conf里配置
wiredTiger.engineConfig.cacheSizeGB为主机可用内存的1/3,避免MongoDB无限制占内存挤压Node服务资源 - 控制数据库连接池大小,三个应用连接同一Mongo实例时,单应用连接池上限不要设超过20,默认无限制的连接池很容易打满Mongo最大连接数导致所有查询排队
- 给所有查询条件字段加索引,尤其是登录场景用到的用户表
- Node/Express服务优化
- 不要直接用node命令启动服务,用PM2做进程守护,单服务配置的进程数不要超过主机分配的CPU核心数,共享主机一般是1-2核,单服务开2个进程足够,进程过多会导致CPU抢占反而更慢
- 给非实时变动的接口加内存缓存,用
apicache中间件给公共配置、列表类接口加1-5分钟的本地缓存,能减少80%以上的重复数据库查询 - 排查阻塞事件循环的代码:不要在接口逻辑里写同步文件操作、大对象循环序列化/反序列化、循环嵌套数据库查询,这类同步代码会阻塞整个Node进程,导致所有接口排队等待
- 把静态资源全部交给主机自带的Nginx/Apache处理,不要用Express的static中间件托管静态文件,Nginx处理静态资源的效率是Express的10倍以上
- 现有配置补全
- 在gzip基础上开启Brotli压缩,对Angular打包后的JS/CSS文件压缩率比gzip高20%以上
- 给静态资源配置强缓存,响应头加
Cache-Control: public, max-age=31536000, immutable,避免用户重复加载未更新的静态文件 - 确认前后端都跑在生产模式:Angular必须用
ng build --configuration production打包,Node服务要设置NODE_ENV=production,开发模式下的调试日志、变更检测、源码映射会让性能下降数倍
懒加载的作用&新手实现示例
懒加载只能改善Angular应用首屏加载过慢的问题,无法解决后端接口分钟级延迟的故障,核心逻辑是把应用按路由拆分为多个独立代码块,用户访问对应路由时才加载对应代码,不用首屏加载全量JS资源。
Angular原生支持路由级懒加载,不需要额外引入复杂依赖,新手按以下步骤实现即可:
- 生成业务模块时直接用CLI命令自动生成懒加载配置,不要手动把业务模块导入到根模块
app.module.ts中,比如用户中心、商品列表两个独立业务模块,执行以下命令:
ng generate module modules/user --route user --module app.module ng generate module modules/goods --route goods --module app.module
- 命令执行完成后CLI会自动在
app-routing.module.ts中写入懒加载路由配置,手动核对配置格式如下即可:
const routes: Routes = [ // 首页、登录页等首屏就要访问的路由直接放根模块,不要做懒加载 { path: 'user', loadChildren: () => import('./modules/user/user.module').then(m => m.UserModule) }, { path: 'goods', loadChildren: () => import('./modules/goods/goods.module').then(m => m.GoodsModule) }, // 通配符404路由放最后 {path: '**', redirectTo: ''} ];
- 执行生产打包命令后,dist目录会生成独立的
user-module.js、goods-module.js文件,首屏不会加载这两个文件,只有用户跳转到对应路由时才会下载,通常首屏JS体积可以减少40%-60%,首屏加载速度提升明显。
注意:登录页、首页这类用户访问站点第一个打开的页面不要做懒加载,否则会增加首次跳转的等待时间。
内容的提问来源于stack exchange,提问作者Yandy
相关产品推荐
相关产品推荐

