前言:我从去年三月开始在这个公司工作,至今已经一年半了。在这段时间里,我一直致力于部门的资源服务开发与搭建。由于公司战略的调整,我现在需要将这个服务交接给其他部门。在负责这个服务的过程中,我遇到了许多挑战和困难,也受到了许多批评和质疑。但我坚持下来了,并且取得了显著的成果。服务的日访问量已经由最初的八千飙升到二十亿,业务规模也扩大了一倍。今天,我想与大家分享我的经验,希望你们在搭建服务时能够少走弯路。
一、基础服务应注意的事项
1. 基础服务的定义:
当接手一个新服务时,首先要明确两件事:服务的定位以及它应承担的职责。我在工作中曾因为对服务的定义不明确而陷入困境。后来我明白了,它就像一个基于资源的库存系统,应具备添加、消耗、返点、冻结、解冻、转账等功能。明确这些功能后,我们就清楚哪些是我们开发所承担的能力,哪些不属于我们承担的能力。这使我得以对我的服务进行大量解耦,更好地处理业务需求。
2. 基础服务的文档:
我在服务初期并没有重视文档的建立,但后来我发现文档在整个服务中扮演着非常重要的角色。为了更方便地服务新接入的业务方,避免重复出现的问题,我准备了两份文档:一份是wiki接入文档,另一份是接口文档。wiki文档的主要内容有:服务专业名字解释、服务负责人及联系方式、服务配置说明、服务接口说明、服务调用示例和常见问题说明等。
3. 服务的性能:
性能是每个服务都需要关注的重点。在设计服务初期,我们应设定性能指标,如操作接口和查询接口的响应时长等。围绕这些标准进行优化,可以提高服务的效率和质量。
4. 服务的高可用性和并发:
为了保证服务的高可用性,我们采用了负载均衡技术,以确保一台机器宕机时整体服务不受影响。并发是大多数服务都会面临的问题。我们采用悲观锁和乐观锁来解决并发问题,以保证数据的安全性和服务的稳定性。对于分布式服务,我们采用了基于redis的分布式锁,以满足高性能需求。
5. 服务的一致性:
服务一致性包括服务本身数据的一致性和与调用方保持一致性。对于调用方的一致性,我们提供了幂等性保证。当调用方出现超时或异常时,再次调用服务将返回订单重复提交的信息,以便调用方根据返回值判断操作是否正确。我们采用了永久性幂等策略,但为了避免占用过多数据库资源,可以考虑对添加操作采用永久性幂等,而对消耗操作采用非永久性幂等。这样可以对幂等数据进行归档,减轻数据库压力。
以上就是我作为服务开发者的一些经验和教训分享。希望这些能够帮助你们在未来的工作中更好地应对挑战,少走弯路。也希望我们能够共同进步,一起学习和成长。服务一致性:
我们常说的一致性,目前尚未完全解决。针对跨单消耗的问题,我采用占位思想进行解决,即先更新资源,出现异常则进行返点。尚未采用最终一致性解决,因为最终一致性可能导致数据被多次扣除。例如,资源第一次扣除失败后被放入队列,而在队列中进行扣除时,新的请求可能获取同一条资源进行扣除,从而引发数据安全问题。我们目前采取的是三次重试,服务的一致性尚未完全实现。关于一致性问题,我们会在后续的文章中继续探讨。
服务接入方的重要性:
当业务方需要调用我们的服务时,必须谨慎对待,以免掉入潜在的问题之中。下面我举两个例子。
首个例子是关于资源添加的。业务方提出他们要举办活动,需要每天添加资源。在评估量级时,我们发现虽然量级不大,但在活动期间添加资源会导致大量零碎订单的产生。如果过期时间设置过长,可能会导致数据库中的每种资源达到数百或数千条,查询或消耗时将变得非常缓慢。之前因为查询过多订单导致服务超时的问题,让我们深刻认识到问题的严重性。在与业务方的沟通中,我们将过期时间设定为一个月,并在进行压力测试确认无误后才予以审批。
第二个例子是关于提供定制化接口的教训。我们曾为业务方提供定制化接口,导致后续技术升级时需要考虑的接口变得复杂多样。因为忽视了这个细节,几次没有考虑清楚都导致了线上事故。经过深入了解,我们发现业务方其实并不需要把消耗详情存入他们的数据库,这些数据完全可以采用离线处理的方式。这是早期沟通不明确需求导致的“毒瘤接口”。
综上
1. 必须清晰明确地了解业务方的需求,明确我们服务应该具备的能力,对于不合理的需求要敢于拒绝。
2. 避免提供定制化的接口。如果某个业务方的需求影响到整体服务,我们必须拒绝或者寻找其他兼容方案。
3. 每次业务方的接入都必须详细记录背景、量级、QPS等信息,并发送邮件明确说明。
二、基础服务过程中的案例分析:
1. 数据迁移和数据转换引发的事故。详细情况请查阅文章
2. 线上操作数据库表结构引发的事故。详情可见文章
3. 重构引发的线上事故主要包括代码整合和删除已废弃逻辑两种情况。代码整合时,一处代码的资源订单生效日期未采用用户传入的日期而导致问题。删除废弃逻辑时,返回实体中的字段由废弃前的值变为null导致其他业务方出现空指针异常。项目重构前必须另外拉分支并修改版本号以便回滚;严格测试新旧版本差异以确保不影响线上业务。
4. 慢查询引发的线上事故是由于新上线的查询接口中存在未加索引的sql字段导致的超时问题。上线新服务时必须经过沙箱严格测试包括性能指标测试以确保稳定性。
修复之旅:内外实体深度解耦与服务的稳健进化
一、修复与升级过程
内外实体彻底解耦,内部接口拒绝任何外部接口的传输干扰,确保系统的纯净性和稳定性。在内外实体间建立起清晰的界限,这是系统修复的首要任务。
二、设计阶段策略
设计阶段中,明确内外实体的职责与差异。内外部实体不必保持一致,外部实体主要聚焦于业务方需要的字段传输,承载着丰富的业务含义;而内部实体则必须与数据库字段保持同步。通过这样的设计,系统更具灵活性和可扩展性。
三. 升级阶段注意
升级内部逻辑时,有时因为各种原因需要对内部服务进行更新。在此过程中,我们必须警惕是否有外部实体参与内部服务的传输。如有涉及,务必注意调整内部实体以符合业务方的需求。若时间允许,推进内外实体的解耦是明智之举。
四、应对业务方的特殊需求
当某一业务方提出特殊属性更改需求时,我们应首先评估这个需求的合理性以及我们服务提供的能力。如果可以通过配置化规则解决,则迅速实施;如无法满足,则考虑升级服务版本,并对返回值进行迭代。在升级过程中,要特别注意不影响其他业务方的调用。
五、全面应对所有业务方的需求
更新pom版本以升级服务,全面统计所有调用方并推动他们同步升级。这是一个系统性的升级过程,确保服务的稳定性和可用性。
六、持久层异常引发的线上事故解析
背景提示:持久层异常被捕获却未抛出。持久层应尽量避免异常捕获,如确需捕获异常,必须有明确的返回值,以便上层处理。这样可以避免线上事故的发生,确保系统的健壮性。
七、枚举使用不当引发的线上事故详述
具体细节请查阅[
八、限流把控不严导致的资源无法添加问题解析
背景:业务方通过MQ异步调用服务,因限流不严,导致短时间内流量过大,出现资源无法添加的情况。问题原因:一是业务方定时处理数据时未能及时通知我们;二是我们没有进行有效的流量削峰。解决方案:一是在服务文档中明确要求业务方在定时处理数据时提前通知最大QPS和数据量级;二是采用流量削峰策略,避免短时间大量流量冲击,将超出部分的流量暂存于MQ端,达到一定量级时触发报警。原文出处:[ |