02 重要性:为什么要对前端服务做全链路的监控和优化?
你好,我是三桥。
上一节课,我们聊到了前端技术全链路的发展过程。全链路不是一下子就蹦出来的一个想法,实际上,它是经过了长期的技术演变、分工、架构升级后,才逐渐形成的一套全链路视角的软件解决方案。
举个例子,一款大型应用的构建,基本上来自不同的研发团队,不同的编程语言,甚至有些服务的服务器就部署了上千台,甚至还有些部署是跨地区的。
我们看一个完整的请求调用链。
在这样一个复杂的链路体系里,几乎每一个前端页面的请求都会形成一套复杂的分布式服务调用链路。例如0.A3.Z.D.F.2,就经过了“应用-消息服务-应用-数据库”这么一长串链路。如果要真正理解不同架构系统之间的行为关系,就需要在这些应用或系统之间建立具备关联关系的动作。这样,在发生线上故障的时候,就能快速定位、解决问题了。
从技术的角度来看,这种技术的解决方案好像跟业务没有什么太大关系,毕竟不是产品新功能,又不能给业务带来增长。
那为什么这种方案最近几年在程序员圈子里变得那么重要呢?学习全链路的知识又能给我们带来什么帮助呢?
用户视角的全链路
相信你一定担任过两种角色,一种是使用他人产品的角色,也就是用户;一种是研发出来产品之后,那你就是产品的创造者。
我先从用户的角度,给你分享一段我使用飞书文档的经历。
我一直非常喜欢使用飞书文档,因为它支持在线编辑、可公开分享和协助。早期飞书提供了两种在线编辑文档格式:文档(Document类)和电子表格(Excel类)。
在飞书多维表格推出前,我一直用电子表格归类工作内容,收集、整理知识点。
直到有一天,我在的时候发现网页加载很慢,表格切换等待时间很长,编辑内容经常卡顿。以前我能利用它提升工作效率,现在却变成了低效工作,甚至,它影响了Web浏览器的正常运行。
我作为前端的工程师,大概能猜到是Web页面遇到性能问题了。我猜这个问题有可能是功能升级导致的,也有可能是业务逻辑设计得不好。但是,我无法准确判断。
两天之后,我实在忍不了了,就直接联系了当时在飞书团队里面的一个旧同事,把问题反馈到了他们的内部团队。
然后,他们要求我提供查问题的信息,比如文档ID、用户ID。不到半天时间,飞书团队就反馈找到问题了,并承诺尽快修复。接着不到2天时间,问题就修复了。低效工作又变回了高效工作。
好了,面对这样一段真实用户的问题反馈,如果你是解决这个问题的前端技术工程师,你会怎么解决呢?
通常有两种排查问题的方法。一是重现问题并尝试解决,二是通过用户提供的信息,查看全链路监控的上下游,找出问题出现在哪个环节,然后定位和修复问题。
我相信大部分人都是第一种方法,我们定义为 测试重现法。
我们接到问题后,第一时间肯定是想着在测试环境尝试重现用户问题,然后定位问题所在的代码位置和逻辑,再本地重现,最后修复代码并上线。
我们先来看看第一种方法的问题排查流程。
这种方法的核心是通过在测试和本地环境重现来发现和修复BUG。它有一个特点,就是如果BUG是通用问题,普遍用户都存在,那么用这种方法基本上都能快速重现。实际上,这种通用问题在上线前后都能立刻发现。
但是我们很多用户的BUG都是有特殊性的,极其“个例”。用测试重现法解决个例用户,不但耗时耗人力,还不一定能找到BUG的解决方案。一个在团队里工作的同学,不可能每天都盯着这些问题,投入自己的工作时间一个个地重现、定位、排查、修复。因为每一个问题都有时间成本、沟通成本,必定会影响团队工作计划的进度。
OK,再说第二种方法, 全链路日志定位法 。 它在解决问题上有什么优势呢?
首先,我们来看看全链路定位法的大致流程。
有没有发现当我们接收用户反馈的问题之后,并没有像测试重现法那样先重现问题?相反,我们的首要工作是通过全链路监控查看有没有日志告警,链路日志中是否有异常日志,先判断是不是大范围的问题。
如果不是通用问题,那就再通过用户提供的信息(如用户ID、时间范围)查询该用户的全链路关联日志,比如前端脚本报错日志、前端请求接口日志、业务配置是否合理等等。
这是通过日志定位法流程分析用户问题,我们再来换个角度看这个方法。
当接收到用户的反馈后,我们只需要先去查一查日志,看看用户相关的日志是否有异常。这件事情的工作量非常小,大概就几分钟的事情。比起直接测试环境重现问题,优先看日志至少省去了80%的前期工作量。简单来说,就是提效。
如果通过日志发现问题不是前端代码或者功能引起的,那我们接下来的工作,就是初步分析问题的来源,究竟是后端问题、数据问题还是产品缺陷。实际上很多前端工程师都不具备这样的分析技能,因为大部分前端同学的职责只是负责页面展示和交互,无需过度关心数据问题和产品是否存在缺陷。
有了刚才的结论,我们就有两条路可以选择了。如果你能判断是什么问题导致的,假设是其它端的BUG引起,那么就可以把问题的排查结果描述出来,转发给对应团队同时抄送给产品。如果你没法判断问题的根源,那就把问题转交给更适合查问题的后端或者客户端同学。
如果在第一步通过链路日志就能发现前端逻辑问题,再结合用户提供的信息和重现的步骤,最后定位代码有问题的地方,基本上有90%概率是知道如何修复问题的。
再看回一开始我遇到的问题。
在我遇到案例中的问题时,实际上是由于我在使用电子表格时某些特定功能的组合后触发了该问题,再加上飞书发布了新版本,一般用户很难会遇到这个问题。
对于这种特殊个例的问题,如果采用测试重现法,可能需要尝试多种场景才能定位问题,非常麻烦,必定影响工作计划。然而,如果采用第二个方法,即通过用户提供文档信息和用户信息,结合日志快速定位,这样就能更高效地解决问题了。
其实,用户是能接受产品存在Bug的,只要能真正解决到用户的问题,有一个时间承诺,用户肯定会继续使用产品,甚至会成为忠实粉丝。而如果用户报Bug总是得不到有效反馈,甚至反馈的是找不到原因或无法修复,用户就会逐渐觉得产品不好用,慢慢地用户就会离开你。
可见,从前端团队和产品的角度而言,我们搭建一套前端全链路的解决方案,不仅提高了团队效率,还能给到用户良好的使用体验和畅通的反馈渠道。另一方面, 前端全链路不仅能用来解决用户问题,还能分析产品功能使用状况、代码逻辑问题、用户交互体验等不同领域的状况。
工程师视角的全链路
接下来,我们以登录功能为例,尝试分析一下登录页的前端全链路有哪些,看看全链路为什么能给我们带来如此大的帮助。
实现登录功能是前端工程师最基本的技能之一。尽管用账号密码登录看起来很简单,但要实现一个完善的登录系统,并把前端全链路整合到功能内,并不是一件简单的工作。
例子中,我们采用基于React、Next.js技术栈,结合通用TailwindCSS,来实现一个登录页功能。
我们先来看一下最终的效果图。
上图是直接使用TailwindCSS实现的登录组件,功能非常简单,就是通过电子邮件和密码登录。
然后,我们看下登录表单的核心UI组件代码。
// src/app/components/Login/index.tsx
<form className="space-y-6" action="#" method="POST">
<div>
<label
htmlFor="email"
className="block text-sm font-medium leading-6 text-gray-900"
>
Email address
</label>
<div className="mt-2">
<input
id="email"
name="email"
type="email"
autoComplete="email"
required
className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
/>
</div>
</div>
<div>
<div className="flex items-center justify-between">
<label
htmlFor="password"
className="block text-sm font-medium leading-6 text-gray-900"
>
Password
</label>
<div className="text-sm">
<a
href="#"
className="font-semibold text-indigo-600 hover:text-indigo-500"
>
Forgot password?
</a>
</div>
</div>
<div className="mt-2">
<input
id="password"
name="password"
type="password"
autoComplete="current-password"
required
className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
/>
</div>
</div>
<div>
<button
type="submit"
className="flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
>
Sign in
</button>
</div>
</form>
在Login组件代码里,我们只展示了核心表单的部分,如果你是一名前端工程师,相信这些代码并不陌生,非常好理解。但如果你是非前端工程师,看不太懂,你可以直接以登录的逻辑思维往下看。
让我们来分析一下登录逻辑。当用户输入电子邮箱和密码登录时,他们肯定期望在登录成功后得到相应的反馈。通常情况下,这个反馈会是一条提示登录成功的信息,并且用户会被重定向到相应的页面。
但是,真实的情况是链路流转不止这一条。接下来我们通过3个场景,来看看在实现页面过程中需要关注哪些全链路的点。
第一种假设,用户登录失败。
如果我们没有实现登录失败的提示,用户会感到非常困惑,无法立即了解自己的登录状态。
但只要系统能提示密码错误,用户还可以继续尝试更改密码并重新操作。当然,这种交互提示还有很多不同的场景状态,例如用户不存在、验证码状态、第三方登录状态等。
还有另一种情景,系统异常和网络问题。网络问题相对容易判断,用户可自行检查设备的网络问题。但像系统异常这种情况,代表着服务不可用。对于用户来说,根本找不到解决方案,如果产品缺失反馈渠道,用户也束手无策。
不过,如果我们能够提前做好链路日志埋点,并配置好监控,就能提前预警和发现问题,产品也不至于服务不可用,甚至导致用户流失。
如果登录异常频繁发生,甚至占比持续升高,可能暗示着功能设计存在问题。这就体现了全链路思维的优势,因为我们可以利用已有的链路日志数据进行分析,判断是代码逻辑问题还是产品设计问题。如果是产品设计问题,就可以向产品提供交互优化建议。
第二种假设,登录等待时间很长。
登录等待时间长,但又能登录成功,这种情况用户一般是不会反馈的。
再举个例子,关于接口请求响应时间,前端代码虽然可以判断接口耗时情况,但我们平时很少会计算每个接口的耗时。就算有,也只会用console.log打在控制台上。
试想想,在前端BFF的业务场景里,同一个业务逻辑经历过5个同学开发5次需求,使用超过10个后端接口,最后第6次需求交给你时,你会去思考有性能问题吗?80%概率不会,因为开发环境大概率不会发现性能问题。
虽然接口的性能属于后端同学关心的问题,但对于前端同学来说,这是最需要关心的,而且极其重要。
你看,后端对接口性能的关注是从输入到输出过程的性能,这里的逻辑主要是围绕服务器端进行的,关注的点都在服务器端。而前端业务调用接口除了后端的范围之外,还要关注用户从发起请求到响应请求的整个过程。只要用户网络环境出现抖动,前端页面的性能必定有所下降。
性能维度在前端全链路里是最重要的一个维度,是直接影响产品体验的维度。但真实的情况是,大部分前端项目,都不会做接口性能监控、前端页面加载性能分析。直到用户反馈,才意识到前端页面出现了性能问题,是不是很可怕?
第三种假设,登录脚本报错。
如果没有监控,没有上报异常日志,没有记录操作记录,怎么查?本地复现不了,怎么查?太难了。
脚本报错,无非就是我们开发的前端应用有BUG。有时候不会影响登录,但有时候也会阻碍登录流程。纯本地环境有时很难判断,这种场景背后的可能性实在太多,可能是发版原因导致的,有可能是CDN网络问题导致的,也有可能是浏览器兼容问题。
举个产品拉新场景的例子,如果这个有问题的页面是营销推广的落地页,那就惨了,钱都拿去投放了,但脚本报错导致页面根本无法用,转化率基本为0。
其实,这个问题的根源很简单,前端发布的新版本都是一些前端资源文件,最终都会部署到CDN节点上。如果CDN节点更新不及时,用户访问到的前端页面和前端资源就可能出现不一致的情况。这就是我们常说的前端资源哈希码还是旧的,不是最新的前端资源文件。
对缺少前端开发经验的同学来说,基本上是无法一下子判断出来是前端资源问题的,甚至以为是前端代码更新逻辑导致的。
如果我们能够针对前端资源的场景整合前端全链路,补充用户访问资源的情况,就能快速定位用户的问题。
在最知名的Sentry监控系统里,收集到的前端脚本报错日志都能关联到前端资源文件,只要我们查了,就能快速发现问题的根源。
但是如果这样的小问题不能快速判断问题根源,到头来,最后又归到前端工程师做得不好的责任,上线前功能没把关好,验收没做好,上线后没立刻验证,出现线上故障又没有立刻反馈。
上面三种假设,都是建立在只有电子邮件和密码的简单模式上。但实际场景是非常复杂的,现在的产品标配都是手机号登录、第三方授权登录、小程序登录等等。这么多的登录渠道,前端入口多,后端处理的链路多,甚至包括第三方系统,只要有一个环节出现故障,必定影响产品正常运作。
所以, 程序员的工作,不仅要保证每一个功能的完整性以及稳定性,还要做好产品新功能的交付。运行中的产品或功能,要做到可监控和可索源,甚至要提前发现问题和解决问题。 这就需要我们用前端全链路的思维去创造产品。
小结
这节课,我从两个不同角度描述了产品出现缺陷时,用户和程序员的关注点。用户希望了解问题的起因以及何时能解决他们的困扰。而程序员在面对用户最棘手问题的时候,需要做到快速定位问题根源,并给出明确的问题修复时间。
现在的产品研发团队分工很细,基本上前端团队每次发布新版本,最后都会交给测试同学或者质量同学去操作,验收就交给测试和产品。在发完版本后,都不会有意识地去验证自己负责的功能是否正常,不会有意识地去看看日志和监控,更不会有意识地去客户群里看看最新讨论的问题是否和最新发布的新功能有关。
但是,对于一款面向用户的产品或应用, 前端同学永远都是站在用户的最前面的人,而用户是产品最重要的资产之一。 如何在有限的时间内通过前端全链路准确定位用户问题,并有效帮助用户解决实际困难,从而将用户转化为忠实粉丝,是我们的重要挑战。
也因此,前端全链路显得极为重要。它能有效地协助定位问题。通过监控,我们能提前发现潜在的问题,提升解决问题的效率。用户反馈不应该成为项目开发进度的负担。
下节课,我会和你一起,具体讨论前端全链路实战中的三要素:指标、监控、决策。
思考题
最后,留给你一道思考题。在处理前端技术问题时,除了在上面提到的两种定位问题方法外,你还会采取哪些方法来定位前端问题?另外,你会如何提高解决问题的效率?
欢迎你在留言区和我交流。如果觉得有所收获,也可以把课程分享给更多的朋友一起学习。我们下节课见!