Said 2.0 的一些开发经验

Said 在三个月内使用了新的技术进行了重写,这三个月收获丰厚,和大家分享一下此次架构的一些心路历程。

JavaScript - 前端开发交流群:377786580

Said 1.x 的痛点

Said 于两年前(2016-02-04)上线,而因为自己磨蹭,第一版开发了很久,开发的同时正是前端技术各种爆发的时候,经过了几次技术选型,最终选定了服务层使用 C#,数据存储使用和 C# 的技术生态融合一体的 Sql Server(毕竟都是微软亲儿子)。因为自己以前做过 C#,同时希望自己在做前端的时候,后端的技术也不要被遗弃。

在前端部分的选型,考虑到前台站点主要突出轻与快,于是直接上 jQuery

而后台的前端部分,考虑到交互频繁和表单复杂,当时选择了 bootstrap 和司徒正美的 avalon,为什么当初没有选择 vue? 因为在后台快开发完的时候 vue 才发第一版。

在开发中逐渐遇到了很多问题。

首先是后台是通过 .net mvcrazor 视图引擎渲染出了页面,然后把数据注入到页面中,js 去读取数据,还原数据并渲染。前后端不纯粹没有分离,其实后台这种项目非常适合做单页使用前后端分离,因为前后端揉合在一起十分痛苦。后台也没有引入类似 npm 的包管理,导致引用的第三库都是自己下载然后 copy 到项目中。好处就是可以自定义开发,例如引入了 showdown.js 来解析 markdown,针对 showdown.js 自己做了很多定制化开发。缺点十分明显,一切靠手动,升级更痛苦。

同样,也没有完善的模块切割和打包机制,导致后来进行功能改造的时候十分痛苦,改一处牵连多处。

前台部分(也就是 https://tasaid.com),使用传统的 jquery。同样没有引入版本控制,兼容性自己把控。css 方面使用了 sass,但是是通过 koala 工具进行的编译,没有和项目融合在一起。

这其中,js 的打包压缩是个痛点,不过后来发现了 .net mvcBundle 能够在项目运行时自动压缩打包文件,这个问题才解决了。

以上痛点基本上忍一忍就可以,但是后端服务层的问题不能忍,后端服务层出现的问题才让我决定了此次的升级。

服务层(也可以叫后端服务)是使用 C# 进行编写的,引入了大名鼎鼎的 entity framework 框架,让我全程不用关心数据库只需要定义 Model 写业务即可,但由于是第一次使用 entity framework ,在后续出现了很多的问题,最大一次问题是 某些情况下连两个并发可能都扛不住。遇到这些问题的时候,由于精力有限不是主攻这个方向的,解决起来难度系数比较大。而且由于国内 C# 使用率的问题,在人脉圈找人帮忙解决问题都很难。

之后 Said 又部署了 HTTPS,部署完了之后产出了这篇影响还不错的文章: 《从 HTTP 到 HTTPS - IIS 部署免费 HTTPS》,本以为就此安心了,结果分分钟被打脸。在某些地方 HTTPS 访问 https://tasaid.com 的时候,会出现 403 - 禁止访问: 访问被拒绝

这种问题十分头疼,因为有些用户 HTTPS 访问是没有问题的, 而另外一些用户 HTTPS 却访问不了,例如我家里的电脑访问没有问题,公司的电脑访问就 403 了,而在公司用手机访问又没有问题。这一问题排查了近一个月,社区大佬也问了一圈没有人知道为什么,各种配置都查了根本没有头绪,导致了此次升级的诱因之一。

另外一个问题是某些情况下会出现移动端访问变成 PC 端页面,Said 是使用 .net mvc 提供的 MVC Display Mode 来针对不同的设备显示不同页面,然后这一问题查了许久也成了玄学问题。

后面还有各项问题,例如服务器前期比较稳定,在后来平均每 15 天内存就会被耗尽,只能重启服务器来解决,问题也懒得排查了。在去年双十一观望了下服务器的活动,发现活动还挺不错,横向比较了下 Windows ServerLinux ServerWindows Server 吃资源还是太厉害了,于是直接订了一年的服务器,然后开始着手准备 Said 的重构。

大体来说,痛点集中在这些:

  • 前后端没有分离,导致混合编写维护十分不便
  • 没有良好的包管理系统,导致第三方库升级艰难
  • 模块系统没有控制好,同样导致维护成本很高
  • 社区不够庞大,导致问题很难被解决

凤凰涅槃

Said 在重构之初本来考虑着要不要使用 .net core (.net 跨平台) 技术来重构,后来思索再三,考虑到项目语言风格的一体性(编码体验一致),前端的蓬勃发展(遇到的问题都可以在社区找到解决方案),以及对自己前端发展技术的一次总结和试验,决定了技术架构如下:

  • 语言使用 TypeScript。2017 年中旬我已经实验过 TypeScript,爱不释手感觉自己快要不会写 js 了。
  • 服务层使用 nodejs。本质上 TypeScript 编译到 nodejs
  • 使用 mongodb 存储数据。Said 的数据关系并不复杂,使用 mongodb 更方便迅捷。
  • 使用 npm 作为包管理工具。npm 已经确定是整个 nodejs/js 生态圈王牌地位了。
  • 后台使用 reactjs + tsx 作为基本框架和语言。tsxTypeScript 专门针对 jsx 所定制的语言,强化了 jsx 的静态类型,比起 vue 来说和 TypeScript 结合的体验简直不要太爽。
  • 后台使用 antd 作为 UI 框架。antd 也是使用 tsx 来编写的。
  • icon font 全部使用 阿里巴巴矢量图标库。 利用阿里巴巴矢量图标库收集和在线生成字体文件。
  • 使用 stylus 作为 css 预处理器。
  • 使用七牛存储静态文件。

基础架构

语言层面,选用 TypeScript 是因为它的静态类型让 js 有了质的飞跃。这一点可以参考我的 《从 JavaScript 到 TypeScript 系列》,强烈推荐 TypeScript

数据存储方面,因为 mongodb 上手简单方便,结构化数据存储非常适合 Said 的应用场景,并且和 nodejs 结合浑然天成。存储数据只需要声明一组 JSON 然后保存,非常简便。

npm 的包管理直接解决了过去引入第三方库,升级不方便的痛点,现在可以跟着社区的主流针对包进行升级,维护成本非常低。

最后,选用七牛云存储是因为 Said 过去都是自己存储和处理静态资源,浪费服务器资源和流量。而七牛云存储的价格不算贵,配置起来也十分方便,所以直接将静态文件扔上去然后走七牛的 CDN 服务加快速度。现在的 Said 不会存储任何静态资源,所有的资源都上传到了 CDN,管理十分方便。很低的价格和良好的服务就能够享受到 CDN 服务。

后台技术

后台本质上是一个单页应用,使用了 webpack 打包成单页。部署前会把 js/css 通过脚本全部上传到七牛云上,然后在服务层只需要输出打包后的 html 即可。

技术层面,后台主要使用了 react,同时结合了 mbox 做状态管理。服务层(nodejs) 提供接口, mbox 读取接口数据然后自动渲染。为了配合 react 中可以更优雅操作 state, 引入了 immutable.js

UI 则使用了组件丰富的 antd,丰富的组件让开发很迅捷,同时良好的各种细节动画让自己也省心很多。

后台使用了 react-codemirror 作为 markdown 编辑器,同时服务层使用 marked 解析 markdown。代码高亮使用传统老牌 highlight.js

前台和服务层

前台的客户端脚本也使用了 webpack 打包, 先把 TypeScript 编译到 js,然后使用 babel 编译代码到 es5,用于解决代码兼容性问题。之所以还加 babel 是因为 TypeScript 只编译语法特性,而不会附加 polyfill。同 Said 1.x 一样,前台也只引用了 jQuery,迎合 Said 一开始要追求的访问性能 —— 轻而快。前台所有的资源和后台一样,在部署前上传到七牛云,然后修正链接即可。

服务端直接用 TypeScript 编译产出 es6,因为服务端跑的 node 8,不需要考虑太多兼容性问题。

过去 Said 会在用户访问的时候往数据库插入记录用于统计,后来这张表存了 4W+ 数据,现在使用 log4js 直接写本地日志,后续再单独针对日志做分析即可。

统计使用 cnzz - 友盟统计,国内目前没有找到比 cnzz 更全的统计(也有其他的第三方统计平台心怀鬼胎),同时会根据自己写的 log 做数据分析(基本上只是看一眼….)。

通过 greenlock-express 来部署 HTTPSgreenlock-express 会自动申请/续订 letsencrypt 的免费证书,从而让站点一直运行 HTTPS(尚未验证,等三个月 letsencrypt 证书到期了之后就可以知道是不是自动续订了)。然后使用 spdy 部署了 HTTP/2。关于这里可以参考 《Node.js 部署免费/自动续订 HTTPS》。

服务器上使用了 pm2 作为 nodejs 的进程托管。pm2 用于管理和守护 nodejs 的进程,在 nodejs 崩溃的时候可以自动重启服务。Said 在部署的时候,根据服务器 CPU 的个数部署了集群模式 (cluster),因为 nodejs 是单线程的,同一个 CPU 下同一时间只会跑一个 nodejs 服务,cluster 模式可以充分利用多核资源。

在部署中遇到了 log4js 不写 log,使用 node 直接运行项目是没问题的,一旦使用 pm2 部署就不写 log,查资料看到了 这个

1
2
3
4
5
6
7
8
9
log4js.configure({
appenders: {
// ...
},
categories: {
// ...
}
pm2: process.env.NODE_ENV === 'production',
})

性能

这是 Said 此次升级后的性能评测报告:

资源加载时间报告:

said 加载时间

 

评分报告:

said 评分

评分站点: http://www.mmtrix.com/index

2018年11月11日 02:42:45:服务器已经从青岛机房迁移到张家口机房(只是因为便宜…)。

JavaScript - 前端开发交流群:377786580


Said 2.0 的一些开发经验
https://tasaid.com/posts/67e55b26/
作者
linkfly
发布于
2018年3月8日
更新于
2018年11月11日
许可协议