业务开发的基本要求

上层业务系统开发原则

 这里指的业务系统,特指web层业务系统,不包括底层基础服务(比如商品中心等提供dubbo服务的系统)这类系统的特点是直接面向B端商家或者C端用户,很多时候需要用户登录,大多提供http(https)服务,流量来源有PC页面、APP和H5。对于这类系统的开发,有一些通用的原则(或者叫必须知道的事情)需要大家关注,如果你不知道这些,那就不是一个合格的业务开发,需要反思!

  1. 操作类接口必须进行完整地业务校验
    1. 权限校验  这个是底线,是必须要做的校验。指的是校验当前登录用户需要具有被操作资源的权限。如果这个校验没做,那么用户登录之后就可以操作其他账号的资源,危险性不言而喻。不知道这个的,拉出来打三十大板,然后拖回去面壁思过!
    2. 业务逻辑校验  业务系统,顾名思义,就是要处理很多业务逻辑。业务逻辑大致分为两类,一类是业务操作逻辑,就是完成一个功能,需要获取哪些信息,然后分别调用哪些底层操作接口。另一个就是业务逻辑校验,并不是所有操作都能无条件执行。业务逻辑校验又可以分为两类,一类是天然的、基础的限制,也就是说需求评审的时候连产品经理都不会告诉你要加上这类限制,比如被操作资源是否存在,必要的入参是否齐全、参数格式是否合法等,还有一类就是产品经理会明确告诉你的校验逻辑,就是哪些因素会限制当前的用户操作,比如当前用户是否满足条件,当前被操作资源的状态能否进行该操作,是否超过了数量或者次数限制,有没有时间上的限制,有没有外部系统或者相关的其他资源的额外限制等等。这类限制可能非常多,写代码的时候,一定要多想想,不然很容易漏掉。如何避免漏掉呢,一定要有自测的意识,单元测试一定要做好,尽可能覆盖多个场景。
  2. 必须校验底层接口的返回结果  即使做了很多的业务校验,底层接口的操作也不一定能够成功,原因有很多,比如上层业务校验不完善,或者底层数据状态不一致,或者底层代码有bug,甚至是RPC调用异常等等。千万不能默认底层操作一定是成功的,然后直接告诉用户操作成功,这是一种很不负责任的表现!一定要检查底层接口的返回结果,确认操作真的成功了,才能告诉用户操作成功。
  3. 所有不成功的操作必须要给用户友好地错误提示  见过很多人写业务代码,只要操作失败了,不管三七二十一,直接给一个“操作失败”,用户看到后一脸蒙蔽,不知道发生了什么。这不仅仅是糊弄用户,更是坑自己和坑队友,为什么呢,用户一旦反馈问题,开发拿不到有用的出错信息,只能看日志看代码,如果碰巧日志缺失或者代码逻辑混乱,那就真的只能呵呵了!写出这种代码的同学,不仅懒,而且不负责任!
  4. 所有关键操作信息必须打出必要的有价值的日志  见过很多系统里面这种日志“xxx被删除”,或者“xx操作失败”,缺少关键信息的日志就是垃圾,我们不应该成为制造垃圾的人。小时候学写记叙文,老师都教我们一定要有六要素——地点,人物,事情的起因,经过,结果,相应的代码中的日志应该也有几要素,即when、where、who、what、why,也就是说打日志的时候必要的信息有时间、打日志的代码位置、谁的请求、做什么操作(或者出了什么错误)、出错的原因等,当然还有一些能够辅助排查问题的上下文信息。比起制造垃圾的人,还有一类人更可恨,那就是catch住异常默默吞掉,啥都不做的人!
  5. 所有接口必须要有单元测试  单元测试不仅可以用来保证自己写的代码基本没有问题,也可以大大减少后面维护的人(可能是自己,也可能是某个无辜的同学)修改老的逻辑的时候引入bug的概率。如果一个新系统刚建立的时候没有单元测试,后面再补的话,效果会大打折扣,因为单元测试本质上是白盒测试,只有写代码的人自己在当时才最清楚代码逻辑,这时候写单元测试,才能起到白盒测试的作用。单元测试会大大减少后期花在bug排查上的时间,越是复杂的业务系统,单元测试的作用也越重要。连单元测试都懒得写的人,也是又懒又不负责任!

核心基础服务的特点有以下几个:

  1. 上游调用方很多,影响面比较大,一旦出问题或者故障,都会很严重(2019年2月21日店铺服务不可用几分钟,故障级别P1)
  2. qps相对来说比较高,任何小问题在大流量下都会被放大(测试环境和预发环境流量很小,一般功能测试很难发现性能问题)
  3. 因为偏底层。所以性能上对终端用户的影响比较大,接口耗时哪怕增加几毫秒,用户感受到的耗时可能会被放大到几十甚至上百ms

正因为以上的特点,所以基础服务相对来说,对开发人员的代码水平要求也会更高,下面列举一些做基础服务开发时需要注意的事情:

  1. 数据正确性和一致性 这个是最重要的,举个例子,查询商品信息的时候,商品状态、价格信息、标签信息等绝对不能搞错,这会影响展示、计价和下单。做任何优化之前,都要考虑,是否会引起这方面的问题。基础服务一般都重度使用缓存,缓存中数据的准确性尤其需要注意,一般来说正常的从缓存中查询数据出错的概率比较低(因为基础的单元测试都能覆盖正常情况),而缓存超时等异常情况导致出问题的概率就比较大了,因为测试环境很难遇到并测试这种情况,一般单元测试如果不mock异常也很难覆盖这种情况。这种因为使用了缓存导致查询出来的数据有问题的情况我遇到了很多很多次,有些是code review的过程中发现的,有些在线上引起了少量用户的反馈,有些导致了严重的线上故障。
  2. 性能 在保证数据准确的前提下,时刻关注性能。保证性能的三板斧有:
    1. 缓存。读多写少的数据,尽量加缓存。
    2. 异步。尽量减少接口rt,那些不是必须在接口返回前要做完的事情,都可以考虑异步去做(比如记日志、发消息、设置缓存等等)
    3. 并发。在确保多个操作没有前后依赖的情况下,可以考虑使用多线程并发去做,减少处理(响应)时间(比如查询多个信息作为一个整体返回,或者执行多个不需要保证事务的操作)。

    除此之外,还有其他很多需要住的点,比如

    1. 批量查询应该避免循环或者并发调用单个查询的方式来实现,尽可能走批量的实现(mget查缓存,批量查数据库等),不要嫌麻烦而复用一些不该复用的逻辑。标签中心出现过并发调用单个接口导致redis qps 40万cpu100%的情况。
    2. 尽量避免重复调用。这里的重复调用有两层含义:1)是调用同一个接口多次,这种考虑在请求级别做数据缓存;2)需要的不同数据在同一次调用中就能获取,不需要调不同接口,比如需要查询商品总库存和每一个sku库存,就不需要调用库存中心两个接口,只需要调用查询sku库存的接口,商品总库存累加起来就可以得到。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券