谁能想到,一个前端组件也能把服务搞崩溃。这个组件正是next/image,原本是想利用next/image来优化图片,next.js官方也一直推荐这么做。
突然有一天用户反馈网站出现504超时报错,当时优先重启恢复服务。然后把日志打开观察,果不其然,几分钟后就又出现504超时,伴随出现的日志则是:
upstream image response failed for https://example.com/280d59d8-f3b0-11ed-a295-00163e253f9a_00002_VvBbv3zj.jpg?OSSAccessKeyId=LTAI5nodLHeacT1J5SmWh&Expires=317044217325&Signature=Wf5jYWf7vnXOyRoKLVtiTCrt8%3D 404 初步判断是图片404导致服务器超时,深入猜测是使用next.js服务端渲染请求了图片资源,而图片资源不存在导致服务器渲染出错。
但是很不合理,服务端只是把图片资源的地址渲染到html中,并不会在服务端请求图片资源才对,为什么服务器日志会出现上述错误信息呢?于是观察浏览器中图片的请求,发现格式是这样的: http://example.com/_next/image?url=https%3A%2F%2Fexample.com%2Ff6c912da-f0cc0000_0d7GBaLE.jpg%3FOSSAccessKeyId%3DmWh%26Expires%3D317043899846%26Signature%3DYUMro%253D&w=384&q=75
浏览器请求的图片资源并不是图片的真实地址,而是经过了next.js服务处理的,尤其是URL最后两个参数,是图片压缩参数。如果你发现图片变模糊了,也可能是这两个参数捣的鬼。直接拿着这个地址去浏览器请求发现响应很慢,这时再次得出结论:这个图片组件有问题。为了验证把所有使用了next/image组件全部使用原生img标签,发布到线上后就再没出现504超时。
总结:next/image组件包装后的图片资源需要经过node层压缩处理,会消耗CPU资源,对于大量的图片资源会有风险,酌情处理。 本次出现的时候伴随着图片资源404,可能是有bug存在,如果加载不到正确的图片就会触发bug。 另一种解决方案:根据官方文档说明,如果使用next/image,需要添加sharp包来提高性能,但是可能需要注意内存消耗问题。
为了使next.js项目能够不宕机,使用了pm2守护进程,既能保证node出现异常情况能够自动重启,也能保证服务器整机重启时自动恢复服务。多年使用下来的确能够良好运行,不过最近却出现了与原本期望不符的情况。
在已经启动next.js项目的情况下,如果需要重启,我使用了pm2 reload appname,实际上没有完美的零停机重启,反而是直接出现服务不可用,访问网站就502,并且一直无法恢复。在查阅pm2 issue后发现确实有这样的bug。
无奈,只能使用pm2 restart来重启应用。
前后端分离 近几年比较流行的web项目开发架构是前后端分离,前后端分离架构在系统稳定性方面非常有优势,其中一点优势主要体现在用户感知上,即使服务端发生错误也不会展现在视图层,一般情况下用户是可以继续浏览网页,不会很突兀的显示这样的信息: 502 bad gateway
在接口发生错误时虽然可能会获取不到一些数据,但是在用户体验上比直接显示502错误要好。
部署也相对安全和方便。
前后端分离架构虽好,但不是”银弹”,不是所有网站都能使用前后端分离架构来做。
服务端渲染 至于分不分离都有诸多的优缺点,可根据实际场景选择,本文要说的是不分离情况下文章开始所提到的问题——难看的502,500错误。
本文我将使用“服务端渲染”来代替不分离的架构。
由于服务端渲染的所有数据处理都在服务端进行,发生任何错误都可能引起500错误,这种错误会直接体现在视图层,用户看到这样的信息很不友好。 500 Internal Server Error
遇到这种错误,可以使用适当的措施来弥补,带来的效果却非常良好。开发web最多遇到的错误码有:404,500,502。
404错误很多网站都有默认页面,像这样的:
但是500,502错误却鲜有默认页面,我们何不也搞一个呢?
500错误 首先说一下500错误的应对措施,500发生在服务端,比如空指针,数组溢出,超时等都会引起web服务500错误,一般的500错误在应用层面是可以捕捉到的,在response返回之前捕获异常跳转到一个”合适的页面”,这样就可以避免出现500 Internal Server Error信息。虽然这个“合适的页面”不是本应该跳转的页面,但起码还是个正常页面。
502错误 再说一下502错误,发生502错误一般是应用直接宕机了,任何页面都是访问不到的,这样是无法在应用层面监听和处理的,但也不是没办法,我们可以在NGINX上处理,当NGINX发生502时跳转到”合适的页面”。这个页面需要是一个静态页,可以是友好的提示信息,可以是网站首页,根据需求自己定制即可。
server{ error_page 502 /502.html; location = /502.html { root /usr/share/nginx/html; } } 以上两种错误处理虽然不能应对所有网站,比如一些个性化网站,即使掩饰的再好,用户也知道出问题了,但是在有些网站却能起到良好的作用,比如有些web站点发布时会中断服务出现502,比起光秃秃的500和502错误,上面的处理是不是就好多了。