ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

后端开发不只是写接口:聊聊那些容易被忽略的工程细节

后端开发不只是写接口:聊聊那些容易被忽略的工程细节 我刚工作的时候把接口调通数据库能返回数据就觉得自己完成了任务。后来线上告警在深夜炸了才发现自己写的代码连一次完整的链路跟踪都拼不齐。这个行业里大部分后端事故都不是写在业务逻辑里而是藏在工程细节的裂缝中。接口只是冰山一角水面之下才是决定系统能否活下来的关键。所谓工程细节听起来琐碎却往往是一个开发者和另一个开发者的分水岭。比如日志。日志写是写了但等于没写很多项目的日志就是一堆System.out和随意拼凑的信息。没有请求ID没有业务上下文没有分级。最要命的是出问题的时候日志打印出了一条“error”却没有打印入参和异常栈。一个没有trace_id的日志系统就是一堆无法串联的碎片排查问题像在垃圾堆里找一枚针。高质量的日志应该在入口处生成全局trace_id在关键路径上记录关键数据且严格按照debug/info/warn/error分级。日志不是写给机器看的是写给深夜接你电话的同事看的。错误处理别把异常直接抛给下游接口的返回值里最让人头疼的就是直接给一个HTTP 500加上一段英文堆栈。调用方不知道是参数错了还是服务器炸了只能干瞪眼。后端接口的优雅不在于永远不出错而在于出错时让调用方知道怎么做。这要求我们定义清晰的错误码体系4xx是调用方的问题5xx是系统的问题给客户端的信息要够用但不泄露内部细节同时要在服务内部保留完整的异常堆栈。错误处理不是多写几个try-catch而是设计一套语义明确的“失败协议”。幂等性一次调用和一万次调用结果必须一样网络是不可靠的所以调用方通常会重试。如果你的接口没有幂等性第一次请求已经扣了款第二次重试再扣一次用户不炸才怪。把接口设计成不幂等等于把随机故障留给用户去承担。飞单、重复支付、库存超卖大多源于此。解决方案并不复杂客户端生成唯一幂等键服务端用这个键做去重或者用版本号做条件更新。难的是你要意识到“重试”是常态而不是意外。因此凡是从网络进来的写操作都要默认假设会被调用两次。超时与重试最被低估的杀伤力很多后端代码里根本没有超时时间依赖的数据库或第三方服务一旦慢下来整个线程池就等着陪葬。没有超时时间的调用是一次没有安全网的走钢丝没有重试策略的调用是另一场不可控的雪崩。更可怕的是重试不加退避策略请求会在故障源上叠加成更大的故障。你得给每一次远程调用设置连接超时和读取超时还要给重试设置最大次数和指数退避。同时要考虑超时之后的降级方案是返回缓存数据还是返回一个明确的失败宁可快速失败也绝不让调用方无限期等待。并发控制数据也害怕被同时修改单机数据库里跑一个UPDATE语句看起来很容易但线上是无数个请求一起涌进来。两个请求同时读到同一个库存各自扣减结果就超卖了。在数据库里跑起并发更新单表成绩再好也拦不住并发时的脏写。常见的做法是乐观锁加一个version字段更新时带上版本号更新不成功就重试或者报错也可以用数据库的原子操作比如UPDATE ... SET stock stock - 1来避开读改写。但更关键的是并发控制不能只依赖数据库很多时候要配合分布式锁或队列来削峰。写代码的时候心里要时刻有一根弦这段逻辑在并发环境下还能对不对优雅停机让正在处理的请求体面地结束很多人部署服务就是直接kill -9。但一个正在处理支付请求的服务强行杀掉会导致正在写入的数据停在半路。run起来只是一个开始怎么停下才见功力。优雅停机要在收到退出信号时做这么几件事停止接收新的连接把正在处理的请求标记为“不再接收新任务”等待当前请求完成但设置一个最长等待时间关闭连接池、注销服务注册最后再退出进程。Kubernetes环境下尤其重要因为Pod随时可能被驱逐。如果你不做优雅停机服务更新的时候就是一场随机的数据事故。可观测性没有监控就等于蒙眼开车接口上线后你说它“正常”依据是什么是看用户没投诉还是看错误率没有飙升一个接口是否健康不能靠用户抱怨了多少次来判断。可观测性的三支柱是metrics、logging、tracing。你得知道这个接口的吞吐量、P99延迟、错误率你得能看到一次请求经过了哪些服务每个服务花了多少时间你还得在指标异常时能快速定位到对应的日志。没有这些系统就是一个黑盒。很多后端开发只写业务不搭观测设施结果线上出问题时一屋子人靠猜。可观测性不是运维的事是每一个写接口的人欠给系统的账单。安全细节别把内部信息暴露给全世界有些接口一报错就把SQL语句、内部IP、甚至数据库表结构都吐给前端。这些信息对调试有用但对攻击者更有用。任何一次不经意的内部异常堆栈都可能成为攻击者手里的图纸。另外权限校验绝不能只在前端做。后端每个接口都要做鉴权特别是那些通过ID操作的接口要检查这个ID是否属于当前用户否则就是越权漏洞。还有输入校验、防SQL注入、敏感字段脱敏、限流防刷。安全不是安全工程师一个人的事它渗透在每一个接口的参数、状态码和响应体里。配置管理环境差异是魔鬼的游乐场开发环境能跑测试环境也正常一到生产环境就挂了。为什么多半是配置问题。把数据库密码写在代码里等于把自家钥匙贴在大门上。更糟的是有些项目的配置散落在各种文件里改一个值要发一次版。正确的做法是用环境变量或配置中心来管理配置区分dev/test/prod环境敏感信息用密钥管理服务加密。还要配置默认值和校验防止漏配或错配。配置和环境分离是后端工程化最简单的起点也是很多人始终跨不过去的一道坎。依赖管理升级一时爽兼容火葬场引入一个依赖很简单一个import就能搞定。但依赖升级却是噩梦的开始。每一次看似无伤大雅的依赖升级都可能是埋在下个夜晚的定时炸弹。JDK升级、框架大版本、第三方库的传递依赖冲突都能让原本正常的系统在某个角落里悄悄崩溃。优秀的后端会记录依赖升级的原因用CI检查二进制兼容性上线时把依赖变更单独发布而不是和业务代码混在一起。更重要的是一定要有回滚方案。管理依赖不是防患于未然而是承认自己会犯错然后给错误留一条后路。写接口是后端最基础的能力而上面这些细节才真正决定了一个系统能走多远。很多人觉得它们琐碎、无趣、不产生收益于是选择忽略。但线上事故不会因为你忽略就不发生它只会攒着在某次大促或者深夜的版本发布中一起爆发。后端工程师的成长就是从把接口调通到把系统守住的转变。下一次写完接口不妨问自己日志能追踪吗失败能说清楚吗重复请求安全吗服务停机能体面吗如果答案是否那你的接口离真正可用还差着十万八千里。
返回列表