美文网首页
说一说API接口设计应该注意哪些事项

说一说API接口设计应该注意哪些事项

作者: 天草二十六_简村人 | 来源:发表于2023-01-08 10:45 被阅读0次

    一、背景

    API接口设计在微服务开发的过程中太重要了,一旦发布出去了,客户端也好,调用方也好,再想让他们升级或者改动的难度非常大。
    这要求我们在定义接口的时候,需要慎之又慎,不可马虎。

    二、目标

    • 1、可扩展性好
    • 2、单一职责
    • 3、隔离性
    • 4、幂等性
    • 5、接口的并发
    • 6、一致性

    二、设计思路

    1、可扩展性

    后期迭代的过程中,难免会增加一些参数,当然得是非必填的,否则之前的调用就会报错。

    • 新增的非必填参数,加在query中,不要写在path里。(尽可能保证rest api的uri path不要太长,因为rest风格的接口是基于资源的角度)
    // 反例
    // 删除题目
    @DeleteMapping("/api/v1/answerRoom/{answerRoomId}/stu/{stuId}/task/{taskId}/topic/{topicId}")
    
    在path中带入了太多的PathVariable参数,意味着他们是必填的,不仅导致路径太长,而且破坏了可扩展性。
    
    // 正例
    // 删除题目,它的目标对象是题目ID,不跟其他无关的领域做强耦合。
    @DeleteMapping("/api/v1/topic/{topicId}")
    

    你可能会反问说,题目一定和它的上级领域相关的啊,离不开任务或答疑室,也离不可题目所属的学生ID。所以上面的接口设计,也是正确的啊。
    接口设计没有对错之分,只有原则的把握度的问题。熟好熟劣,自己推敲再三。

    2、单一职责

    有时候接口需要拆分为多个接口,有时候又可能需要合并抽象为一个接口。

    2.1、商品的相关接口

    上下架、修改库存等从商品的编辑接口抽离出来。

    image.png

    2.2、积分的发放

    用户获得积分的途径有多种,拆分为用户购买积分、奖励发放积分和管理后台手动发放给用户等。

    2.3、聚合支付

    支付平台需要将多种支付渠道(微信支付、支付宝支付和不同的银行渠道)的支付请求,合并为一个抽象接口。而不是微信支付一个接口、支付宝支付又是另一个接口,以支付渠道为维度的话,虽然隔离性是好,但是业务对接方要吐槽不停。
    支付平台还需要将多个对接的业务方,也合并为一个接口。
    总之,对外的支付请求接口,就一个聚合接口。

    3、隔离性

    保证接口的粒度尽可能小,比如和支付方对接,我们都需要提供回调接口给到第三方支付。这时候,和上面的支付请求接口相反,往往是按支付渠道拆分为多个回调接口。

    image.png
    @PostMapping("/api/v1/notify/abc")
    @PostMapping("/api/v1/notify/alipay")
    @PostMapping("/api/v1/notify/hzbank")
    @PostMapping("/api/v1/notify/icbc")
    @PostMapping("/api/v1/notify/wxpay")
    
    

    当然,入口拆分开来,是因为他们回调的参数体差异很大,所以我们需要把共性的处理逻辑,放在一个抽象类里,实现代码的复用。

    4、幂等性

    我们说一个接口做到了幂等,必须满足可以重试(不建议给调用方直接报错提示)

    实现幂等的方式有很多,一般在post请求体中增加一个token字段 。接口在处理业务逻辑前锁定这个token对象即可。可以把token入库,处理完业务,再删除token。 当然也可以不删除token,这将视你怎么使用了。

    • token机制,因为网络的不可靠性,可能发生重试,所以需要我们的接口能够对重试进行兼容:要么报错提示说不要重复提交,要么直接返回已处理。

    5、接口的并发

    在对接口的性能摸底的时候,期望的响应时间内,当时的qps并发量为多少。

    常见的实现方式有:

    • 无锁
    • 乐观锁
    • 分布式锁
    • 数据库的悲观锁

    一般建议你使用基于版本号的乐观锁,因为它的粒度最小,易于控制。

    这里举一个分布式锁的例子,支付系统的回调接口是对外暴露出去的,防止被人攻击,或者第三方支付的频繁回调,我们往往是会对支付流水号进行一个锁定。好处是可以利用支付订单的状态机机制,避免重复处理支付回调。(如果是已支付,则返回)而因为在分布式的场景下,支付网关是有多个节点,想要锁定支付流水号,必须是使用分布式锁了。

    6、一致性

    这里的一致性,是说一个接口方法的所有操作而言。如果全部是操作事务性的数据库,则可以利用数据库的事务来保证。如果还涉及调用外部服务的接口,则需要通过补偿机制和持久化的重试来做到尽可能的一致性。

    不太建议你引入分布式事务框架来做,更多地倾向于本地日志表。

    • 数据库的事务机制
    • 本地日志表的重试
    • 补偿机制

    这里不会详细讲解上述,只是给你一个提醒,设计和实现接口的时候,有没有满足一致性的要求。

    上文不满足事务数据库的事务机制的情况,除了典型的微服务之间的接口调用外,还有redis、rabbitmq等中间件的操作,也是无法支持事务。

    当然,后者想要满足接口的一致性,就显得更加困难了。这里推荐你对重要业务,做到必要的补偿。

    使用xxl-job分布式的定时任务,对可能出错的业务做补偿,也可用来和业务对接方做定期的对账。

    四、和接口设计的其他思路

    将查询类的接口和操作类的接口,区分开来。操作类的接口,一般复杂在处理的表及字段比较多,但是它的qps比较低。查询类的接口,它是复杂在拼接数据,所以我们建议是做数据异构,查询异构后的数据。

    1、分库分表

    提高写入的qps,结合接口的qps来具体确定是否引入分库分表方案。

    2、读写分离

    • 数据库的主从模式
      操作在主库 ,查询在从库。和下面的数据异构方案一样,都存在一定的同步延迟。
    • 数据异构
      将操作和查询分离,利用数据异构的思路,既保证操作业务的简单,又适应复杂查询的需求。比如我们的商品服务,查询使用ES,操作使用关系型数据库mysql。

    相关文章

      网友评论

          本文标题:说一说API接口设计应该注意哪些事项

          本文链接:https://www.haomeiwen.com/subject/edljcdtx.html