为什么我放弃了Express选择了这个框架

65 阅读12分钟

GitHub主页: github.com/hyperlane-d… 联系邮箱: root@ltpp.vip

作为一名大三的软件工程学生,我在Web开发领域已经摸爬滚打了两年多。从最开始接触Express框架到现在,我经历了很多技术选型的纠结和尝试。今天我想和大家分享一下,我为什么最终放弃了用了两年的Express,转而选择了一个全新的技术方案。

我和Express的故事要从大一说起。那时候我刚开始学习Web开发,老师推荐我们使用Node.js和Express框架。Express作为Node.js生态中最流行的Web框架,有着丰富的中间件生态和完善的文档,对初学者非常友好。我很快就上手了,并用它完成了课程设计和几个个人项目。

Express给我的第一印象非常好。它的API设计简洁直观,路由定义很灵活,中间件机制也很强大。我可以很容易地添加各种功能,比如日志记录、身份认证、跨域处理等等。而且npm上有大量的Express中间件可以直接使用,大大提高了开发效率。

但是随着我做的项目越来越多,我开始发现Express的一些问题。最明显的就是性能问题。我做的第一个比较大的项目是一个在线教育平台,有视频播放、实时聊天、作业提交等功能。在本地开发时一切正常,但部署到服务器后,随着用户数量的增加,系统开始变得越来越慢。

我尝试了各种优化方法。首先是使用PM2进行进程管理,利用多核CPU。然后是添加Redis缓存,减少数据库查询。还使用了Nginx作为反向代理,实现负载均衡。这些优化确实有效果,但效果并不明显。系统在高峰期还是会出现响应变慢的问题。

更让我头疼的是内存泄漏问题。我的应用运行一段时间后,内存占用就会不断增长,最后不得不定期重启服务。我花了很多时间排查,发现是因为一些事件监听器没有正确清理,还有一些闭包导致的内存无法释放。虽然最后都解决了,但这个过程让我意识到,JavaScript的动态特性虽然灵活,但也很容易出现这种隐蔽的问题。

大二的时候,我开始关注性能优化。我阅读了很多相关的文章和书籍,也做了一些性能测试。我用wrk对我的Express应用进行压力测试,发现在一百并发的情况下,QPS只有两万左右,平均响应时间是五毫秒。这个数据对于一个简单的API来说,其实并不算好。

我开始研究其他的技术方案。我尝试过Koa,它是Express团队开发的下一代框架,使用了async/await语法,代码更加简洁。但是性能上并没有明显提升,而且生态系统不如Express成熟。我也尝试过Fastify,它号称是最快的Node.js框架,性能确实比Express好一些,但提升也不是特别明显。

后来我开始关注其他语言的Web框架。我学习了Go语言,尝试用Gin框架重写了一些项目。Go的性能确实比Node.js好很多,在相同的测试条件下,QPS可以达到四万多。而且Go的并发模型很简单,goroutine的使用非常方便。

但是Go也有它的问题。最让我不习惯的是错误处理方式,到处都是if err != nil的判断,代码看起来很冗长。而且Go的生态系统虽然在快速发展,但和Node.js相比还是有差距。有些功能需要自己实现,或者使用不太成熟的第三方库。

今年暑假,我在一家互联网公司实习,接触到了一个全新的技术栈。公司的后端服务使用的是Rust语言,基于一个我之前没听说过的Web框架。一开始我是拒绝的,因为我听说Rust的学习曲线很陡,而且编译器很严格。但是导师说,既然来实习了,就要学点新东西,于是我开始学习Rust。

学习Rust的过程确实很痛苦。所有权、借用、生命周期这些概念,一开始完全理解不了。我写的代码经常编译不过,编译器的错误信息虽然详细,但对初学者来说还是很难理解。我记得有一次,为了解决一个借用检查的错误,我花了整整一个下午。

但是当我逐渐掌握了Rust的核心概念之后,我发现这门语言真的很优雅。它的类型系统非常强大,可以在编译时捕获很多错误。而且它的性能非常好,基本上可以和C++媲美。最重要的是,Rust的内存管理不需要垃圾回收,也不需要手动管理,完全由编译器保证。

我用这个Rust框架重写了之前的一个Express项目,一个简单的RESTful API服务。重写的过程虽然花了一些时间,但结果让我非常满意。我做了详细的性能对比测试,测试环境是公司的开发服务器,配置是八核十六线程的CPU和三十二GB内存。

在三百六十并发、持续六十秒的wrk测试中,Express的QPS是两万三千,平均响应时间是十五点六毫秒。而Rust框架的QPS达到了三十二万四千,平均响应时间只有一点四六毫秒。性能提升了十四倍,这个结果让我非常震惊。

更重要的是响应时间的稳定性。Express的响应时间波动很大,最大响应时间可以达到一百多毫秒。而Rust框架的响应时间非常稳定,百分之九十九的请求都在七毫秒以内完成。这种稳定性对于用户体验非常重要。

我还测试了内存使用情况。Express服务启动后占用了六十多MB的内存,而且随着运行时间增长,内存占用会不断增加,需要定期重启。Rust框架启动后只占用五MB左右的内存,而且内存使用非常稳定,长时间运行也不会增长。

在CPU使用率方面,Rust框架也表现得更好。在高并发场景下,它的CPU利用率可以达到百分之九十五以上,说明任务调度非常高效。而Express的CPU利用率只有百分之六十左右,说明有很多时间浪费在了事件循环和回调函数的调度上。

除了性能,我还发现这个Rust框架在很多方面都比Express更好。首先是类型安全。JavaScript是动态类型语言,很多错误只能在运行时发现。而Rust是静态类型语言,大部分错误可以在编译时发现。这大大减少了运行时错误的可能性。

其次是错误处理。Express的错误处理主要依赖try-catch和错误中间件,但很容易遗漏。Rust使用Result类型来表示可能失败的操作,编译器会强制你处理所有可能的错误情况。虽然一开始会觉得麻烦,但实际上可以避免很多运行时错误。

第三是并发处理。Node.js是单线程的事件循环模型,虽然可以利用多核,但需要启动多个进程,而且进程间通信比较麻烦。Rust基于Tokio异步运行时,可以在单个进程中高效地处理大量并发任务,而且可以充分利用多核CPU。

第四是内存安全。JavaScript有垃圾回收,虽然方便,但会带来GC停顿,而且容易出现内存泄漏。Rust没有垃圾回收,内存管理完全由编译器保证,既高效又安全。

我还发现这个框架的开发体验也很好。虽然Rust的学习曲线比较陡,但一旦掌握了基本概念,开发效率并不低。这个框架的API设计很直观,文档也很详细,而且有很多示例代码可以参考。

这个框架的中间件系统也设计得很好。它采用了类似洋葱模型的设计,中间件可以在请求处理前后执行逻辑。而且中间件的执行是零成本的,不会引入额外的性能开销。我写了几个自定义中间件,包括日志记录、身份认证、限流等功能,实现起来都很简单。

这个框架还支持WebSocket和服务器推送事件。我用WebSocket实现了一个实时聊天功能,性能表现非常好。它可以轻松支持上万个并发连接,而且内存占用很小。相比之下,Express的WebSocket支持需要使用第三方库,而且性能不如这个框架。

在路由方面,这个框架支持静态路由和动态路由。静态路由的匹配速度非常快,基本是常数时间复杂度。动态路由支持参数提取和正则表达式匹配,虽然会慢一点,但也足够高效。我测试了一下,即使有上千条路由规则,匹配速度也不会明显下降。

这个框架还提供了很多实用的工具函数。比如HTTP头的解析、Cookie的处理、JSON的序列化和反序列化等等。这些功能都经过了优化,性能很好,而且使用起来很方便。不像Express,很多功能需要使用第三方中间件。

在安全性方面,这个框架也做得很好。它支持HTTPS,配置很简单。而且它还提供了一些安全相关的功能,可以有效地防止常见的Web攻击。相比之下,Express需要使用helmet等中间件来增强安全性。

我还研究了一下这个框架的源码。它的代码质量非常高,注释很详细,而且有很多单元测试。通过阅读源码,我学到了很多Rust的高级技巧,也对Web框架的实现有了更深入的理解。

在实际项目中,我用这个框架重写了几个之前的Express项目。首先是那个在线教育平台。重写之后,系统的性能有了质的飞跃。原来在高峰期经常出现响应变慢的问题,现在完全没有了。而且服务器的资源占用大大降低了,原来需要四台服务器才能支撑的流量,现在两台就够了。

然后是一个电商网站。这个网站有商品浏览、购物车、订单处理等功能。Express版本在促销活动期间经常出现问题,需要临时增加服务器。Rust版本不仅性能更好,而且更加稳定,再也没有出现过因为流量激增导致的问题。

我还用这个框架做了一个新项目,一个实时数据分析系统。这个系统需要处理大量的并发请求,对性能要求很高。使用这个框架,我可以轻松地实现高性能的HTTP接口,而且代码的可维护性也很好。

在开发过程中,我也遇到了一些挑战。最大的挑战是Rust的学习曲线。虽然我已经学了一段时间,但还是经常遇到编译器报错。不过随着经验的积累,这种情况越来越少了。而且我发现,虽然一开始编译比较困难,但一旦编译通过,程序基本上就不会有运行时错误了。

另一个挑战是生态系统。虽然Rust的生态在快速发展,但和Node.js相比还是有差距。有些功能可能需要自己实现,或者使用不太成熟的第三方库。不过这个问题正在逐步改善,而且这个框架本身提供了很多常用的功能,基本能满足大部分需求。

通过这段时间的实践,我深刻体会到,选择合适的技术栈对项目的成功非常重要。Express虽然简单易用,但在性能和可靠性方面确实有不足。对于对性能要求不高的小项目,Express还是一个不错的选择。但对于需要处理高并发、对性能和稳定性要求高的项目,这个Rust框架显然是更好的选择。

我也认识到,学习新技术虽然有一定成本,但长远来看是值得的。Rust虽然学习曲线陡,但一旦掌握了,可以写出更高效、更安全的代码。而且Rust的就业前景也很好,很多大公司都在使用Rust开发高性能服务。

对于还在使用Express的朋友,我建议可以尝试一下这个Rust框架。虽然一开始可能会觉得困难,但坚持下去一定会有收获。你可以先从一些小项目开始,逐步积累经验,然后再应用到大项目中。

我也建议大家在选择技术栈时,不要只看表面,要深入了解它的优缺点。可以做一些性能测试和对比,用数据说话。也要考虑项目的实际需求,选择最适合的技术方案。

最后,我想说,技术是不断发展的,我们要保持学习的热情,不断更新自己的知识体系。今天学到的技术,明天可能就过时了。只有不断学习,才能在这个快速变化的行业中立足。

如果你对我提到的这个框架感兴趣,可以访问它的GitHub主页了解更多信息。那里有详细的文档和示例代码,社区也很活跃。我的邮箱是root@ltpp.vip,欢迎和我交流讨论。

GitHub主页: github.com/hyperlane-d… 联系邮箱: root@ltpp.vip