Serverless是近年来最火的技术方向之一,我也曾经对它充满热情,从入门到深入研究,甚至在项目中大力推广。但是经过一年多的实践之后,我却选择了在大部分场景下放弃Serverless。本文记录了我使用Serverless的完整经历,包括我为什么看好它、我踩了哪些坑、为什么最终选择放弃、以及哪些场景我还会继续用。如果你也在考虑用Serverless,希望我的经历能给你一些参考。

一、初识Serverless:充满期待

先说说我是怎么开始用Serverless的吧。

那是2019年,Serverless开始火起来,各种技术大会、技术博客都在讨论它。我看了很多介绍文章,觉得这个技术太美好了:不需要管理服务器,不需要关心运维,只需要写函数代码,上传到平台,平台自动处理部署、扩容、监控。而且按需付费,没有请求不花钱,成本也低。

当时我们团队正好有一个新项目,是一个活动页面的后端,流量波动很大,平时没什么流量,活动期间流量会突然增大。这种场景听起来简直就是为Serverless量身定做的。

于是我决定在这个项目中尝试Serverless。我花了一周时间学习Serverless的概念和用法,然后用函数计算重构了这个项目的后端。上线之后效果确实不错,平时没流量的时候不花钱,活动高峰期自动扩容,完全不用我们操心运维。

第一次尝试的成功让我对Serverless充满了信心。我开始在团队内部分享Serverless的好处,推动更多的项目使用Serverless。那时候我觉得Serverless就是未来,传统的服务器架构迟早会被淘汰。

二、深入使用:问题开始出现

随着使用Serverless的项目越来越多,问题也开始逐渐暴露出来。

问题1:冷启动让人头疼

这是Serverless最经典的问题。函数长时间没有调用之后,平台会把函数实例回收掉。下次调用的时候,需要重新启动实例,加载代码,初始化运行环境,这个过程就是冷启动。

冷启动的时间从几百毫秒到几秒不等,取决于运行时、代码大小、依赖多少等因素。对于Java这种重运行时,冷启动时间可能长达好几秒。

我们有一个API接口,用Serverless实现的。用户第一次调用的时候,经常要等两三秒才能收到响应,用户体验很差。虽然平台有预留实例的功能,但是预留实例是要收费的,而且也不能完全解决冷启动问题。

为了优化冷启动,我们做了很多努力:减小代码包、减少依赖、用更轻量的运行时、保持函数热度等。但是这些优化只能缓解,不能从根本上解决问题。对于对延迟敏感的在线服务,冷启动始终是一个绕不过去的坎。

问题2:调试和本地开发体验差

Serverless的调试体验真的很差。传统应用可以在本地启动,打断点调试,很方便。但是Serverless函数依赖平台的环境,本地模拟和云端实际运行环境有差异,很多问题本地复现不了,只能在云端打日志排查。

而且Serverless函数是无状态的,每次调用都是独立的,很难复现问题。出了问题只能看日志,但是日志有时候也不全,排查起来非常费劲。

我们有一次线上出了一个bug,在本地怎么都复现不了,在云端偶尔出现。我们花了整整一周才定位到问题,原因是云端的Node.js版本和本地不一样,某个API的行为有差异。这种问题如果是传统应用,可能半天就定位了,但是在Serverless环境下排查起来特别费劲。

问题3:厂商绑定严重

Serverless平台虽然都遵循类似的概念,但是具体的API、配置、工具链都不一样。你在阿里云函数计算上写的代码,不能直接迁移到腾讯云SCF或者AWS Lambda上,需要做不少修改。

我们当时选的是阿里云的函数计算。用了一段时间之后,发现有些功能阿里云不支持,想换到其他平台,但是迁移成本太高了,代码、配置、工具链都要改,只能继续用阿里云。

这种厂商绑定让我们很被动。平台的定价、功能、服务质量都由厂商决定,我们没有议价能力,也没有退路。如果厂商涨价或者停止服务,我们会很麻烦。

问题4:状态管理和长连接困难

Serverless函数是无状态的,这对于无状态的API接口没问题,但是对于需要状态的应用就很麻烦。

比如我们想做一个WebSocket服务,需要维护长连接。但是Serverless函数是无状态的,每次调用都是一个新的实例,无法维护连接状态。虽然平台有一些解决方案,但是都比较复杂,而且性能不好。

再比如我们需要做一个定时任务,需要在内存中维护一些状态。但是Serverless函数执行完就销毁了,状态无法保留,只能存在外部的数据库或者缓存里,增加了复杂度和成本。

问题5:成本并不总是更低

Serverless宣传的一大优势是按需付费,成本更低。但是实际用下来,我们发现成本并不总是更低。

对于流量稳定的服务,Serverless的成本可能比传统服务器更高。因为Serverless的调用费用和执行时间费用加起来,在流量稳定的情况下,可能比租一台服务器还贵。

而且Serverless还有一些隐藏成本,比如网络流量费用、API网关费用、日志费用等。这些费用加起来,可能比你想象的高很多。

我们有一个服务,原来用一台2核4G的服务器,每个月成本大概200块。后来迁到Serverless,每个月的费用反而涨到了300多块。因为这个服务流量比较稳定,Serverless的按需付费优势体现不出来,反而因为调用次数多,费用更高。

问题6:监控和可观测性不足

Serverless的监控和可观测性也不如传统应用。传统应用可以很方便地接入APM工具,做全链路追踪、性能分析等。但是Serverless函数的生命周期很短,而且是平台托管的,很多监控工具无法直接接入。

虽然平台提供了一些基础的监控指标,比如调用次数、执行时间、错误率等,但是这些指标比较基础,无法满足复杂的调试和性能分析需求。出了问题,很难快速定位到根因。

我们有一次遇到函数执行时间突然变长的问题,平台的监控只能看到执行时间变长了,但是看不到具体是哪一步慢了。我们花了很久才定位到是某个第三方接口变慢了,如果有完善的APM工具,可能几分钟就能定位。

三、从推广到放弃:我的转变

随着遇到的问题越来越多,我对Serverless的态度也开始发生转变。

刚开始的时候,我觉得这些问题都是暂时的,随着技术的发展都会解决。我也在团队内部积极推广Serverless,觉得大家应该拥抱新技术。

但是慢慢地我发现,有些问题是Serverless架构本身的固有限制,不是靠技术发展就能完全解决的。比如冷启动,只要函数是按需启动的,冷启动就不可能完全消除。比如厂商绑定,只要用的是托管平台,就不可能完全避免。

而且我发现,我们团队的大部分项目,其实并不需要Serverless的特性。我们的大部分服务流量都比较稳定,不需要自动扩缩容。我们的团队也有运维能力,不需要完全免运维。对于这些项目,用传统的服务器架构反而更稳定、更可控、成本更低。

于是我开始重新评估Serverless的适用场景。我不再盲目推广Serverless,而是根据项目的具体需求来选择技术方案。对于适合Serverless的场景,继续用;对于不适合的场景,果断换回传统架构。

这个转变不是一蹴而就的,而是在一次次踩坑、一次次反思之后慢慢形成的。现在回头看,我觉得这个转变是理性的,不是对Serverless的否定,而是对它有了更清醒的认识。

四、我为什么放弃了大部分场景

具体来说,我在以下场景中放弃了Serverless。

1. 对延迟敏感的在线API

对于用户直接使用的在线API,对延迟要求比较高,冷启动的影响太大了。用户打开一个页面,要等好几秒才能加载出来,体验很差。这种场景我现在都用传统的服务器架构,服务一直运行,没有冷启动问题,响应速度稳定。

2. 流量稳定的常驻服务

对于流量稳定的服务,Serverless的成本优势体现不出来,反而可能更贵。而且常驻服务需要一直运行,用Serverless的话函数会一直被调用,和传统服务器没什么区别,但是成本更高,调试更麻烦。这种场景用传统服务器更合适。

3. 复杂的业务系统

对于业务逻辑复杂的系统,需要维护状态、需要长连接、需要复杂的事务处理,Serverless的无状态模型会让开发变得很复杂。这种场景用传统的单体应用或者微服务架构更合适,开发和维护都更简单。

4. 需要深度定制和控制的系统

对于需要深度定制运行环境、需要控制网络配置、需要特殊硬件的系统,Serverless平台的限制太多了。平台提供的环境是固定的,你无法自由定制。这种场景用传统服务器或者容器更合适。

五、哪些场景我还在用Serverless

虽然放弃了大部分场景,但是在一些特定场景下,我还是会继续用Serverless。

1. 事件驱动的异步任务

比如文件上传之后的处理、消息队列的消费、定时任务等。这些场景是异步的,对延迟不敏感,冷启动影响不大。而且任务是事件驱动的,没有事件的时候不运行,按需付费成本低。Serverless非常适合这种场景。

2. 流量波动极大的场景

比如活动页面、秒杀系统、临时的营销活动等。这些场景平时没什么流量,高峰期流量突然增大,用传统服务器的话,平时资源浪费,高峰期又可能扛不住。Serverless的自动扩缩容和按需付费在这种场景下优势明显。

3. 简单的工具和脚本

比如一些简单的API接口、数据处理脚本、Webhook处理等。这些功能简单,流量不大,用Serverless开发快,部署简单,成本低。不需要为了一个小功能维护一台服务器。

4. 原型验证和MVP

在产品的早期阶段,需要快速验证想法,用户量不大,需求变化快。这时候用Serverless开发速度快,成本低,不需要关心运维。等产品验证成功,用户量上来了,再考虑是否迁移到传统架构。

六、给想尝试Serverless的朋友的建议

如果你也在考虑用Serverless,这里有一些建议给你。

1. 不要盲目跟风

Serverless虽然很火,但是不是所有项目都适合。在决定用之前,先想清楚你的项目是否真的需要Serverless的特性。如果你的项目流量稳定、对延迟敏感、业务复杂,那可能不适合用Serverless。

2. 从小项目开始尝试

不要一上来就把核心系统迁到Serverless。先从一个简单的、非核心的小项目开始尝试,积累经验,了解Serverless的优势和局限。等熟悉了之后,再考虑在更多项目中使用。

3. 做好冷启动的准备

如果你的项目对延迟敏感,一定要提前考虑冷启动的问题。可以用预留实例、保持函数热度、优化代码包大小等方式来缓解冷启动。但是要知道,这些方法只能缓解,不能完全消除。

4. 注意厂商绑定的风险

在选择Serverless平台的时候,要考虑厂商绑定的风险。尽量选择开放的、支持标准的平台,或者在代码中做一些抽象,降低迁移成本。不要把所有系统都放在一个平台上,避免被单一厂商绑定。

5. 做好成本监控

Serverless的成本结构比较复杂,有调用费用、执行时间费用、网络费用、日志费用等。一定要做好成本监控,定期查看费用明细,避免出现意料之外的高额费用。

6. 保留回退的方案

在使用Serverless的同时,要保留回退到传统架构的方案。如果发现Serverless不适合你的项目,可以快速迁移回去,不要被锁死在Serverless架构上。

七、写在最后

从入门到深入,再到大部分场景下放弃,这一年多的Serverless实践经历让我收获很多。

我不再是那个盲目追新的技术狂热者了。我学会了理性地看待每一项新技术,看到它的优势,也看到它的局限。没有完美的技术,只有适合的技术。

Serverless不是银弹,它有自己的适用场景,也有自己的固有限制。在适合的场景下,它能大大提升效率,降低成本。在不适合的场景下,它反而会带来更多的麻烦和成本。

我现在对Serverless的态度是:不盲目推崇,也不完全否定。根据项目的具体需求,选择最合适的技术方案。这才是一个成熟的技术人应该有的态度。

技术在不断发展,Serverless也在不断进步。也许未来某一天,冷启动问题解决了,厂商绑定问题解决了,Serverless会变得更加通用。但是在那一天到来之前,我们还是要根据实际情况,做出理性的选择。

最后用一句话结束本文:"技术选型的本质,是在合适的场景用合适的工具。"愿每一个技术人都能理性看待新技术,做出最适合自己项目的选择。