你真的会写接口吗?

很多人觉得写接口很简单,其实接口有很多东西你没有考虑过。

一、什么是接口

“接口“是web 领域中大家最为熟悉的技术语言了,自然界的接口就是用来对接的。例如插座是一种接口,按照一定的规范,他可以兼容很多种插头使用,因此接口是需要有“通用性“和”规范性“的。否则大家都搞自己的一套,使用对接的成本就非常的高。当然在这里我不是要来讨论开发语言的接口(Java、PHP、Go 的 interface),本文重点还是讨论前后端对接所使用的接口。这类接口对于做软件开发的人来说,绝对不陌生。

二、接口的展现形式

我们经常会听到前端对后端说,你这里要给我提供一个新接口,用来获取 xxx 的数据。很多新人此时会觉得,我们的接口难道主要就是帮前端拿数据库数据的吗?这句话其实也没有太大的毛病,在正常的业务开发中,不复杂的业务,接口就是用来做数据库的间接管理的。因此我们经常会听到很多后端兄弟姐妹自嘲说自己就是个CURD码农。

回到正文,接口的展现形式有哪些。主要是:GRPC(TCP)、HTTP(应用层)、soap(应用层,基于HTTP之上)
GRPC 主要是使用 protobuf 作为接口通讯规范,HTTP 则主要用 JSON 、XML 的形式作为通讯规范。

其中,大家最为熟悉的肯定还是 HTTP 的 json 形式了。其实本质上 HTTP 协议所支持的接口,传参并不只是 body 的 json。query/header 都可以放数据,虽然可以这么搞,但是不建议都这么搞,需要考虑到接口安全性、拓展性、高可用性等等问题。

目前使用 xml 的已经非常的少了,基本上走http接口的都是使用 json 来传输数据。这是大家普遍所认同的。

而 GRPC 可能很多公司并没有开始使用,它是一种性能更高的接口实现方式,由于它是基于TCP/IP层实现的,少了一个应用层,因此通讯的效率会更加高,但是它的平台性并没有HTTP 那么好,毕竟需要直接通过TCP通讯,那就需要考虑网络等问题。(一般要支持不同局域网网之间的IP端口通讯,需要更大的成本)

三、接口的实现规范

1、接口文档

实现接口前,一定要写接口文档。很多人喜欢直接撸接口,事后补充文档,但是实际上,在稍微有些规模的公司,前后端分离的基础架构下,前端不可能等你写好接口才开始对接,这样效率很低,因此,前端会要求你先给出接口文档。然后前后端同步开发,再进行接口联调,因此,接口文档十分的重要,当你对接多个组或者多个前端的时候,一份好的接口文档,可以帮你减少很多不必要的咨询沟通。

目前,文档的自动化也是比较普及了,很多都可以基于注解自动生成接口文档。减少了很多人工编写的工作。

如果你还是手写的接口文档,可以尝试用一些市面上成熟的文档自动生成工具,这会舒服很多。

2、接口抽象性

这里聊到接口的抽象性,主要还是我做了几年的中台慢慢体会到的一些经验,当你的接口需要对接多个组时,你的接口需要有良好的拓展性,避免每次需求都要写一个接口,抽象性主要还是面向业务的,举个简单的例子。通过 xxx 获取平台用户。
此时,我们可以抽象平台属性,有哪些平台呢,公众号、小程序、网站、APP ,此时你提供一个平台的字段。就可以同时兼容多个平台的用户查询(当然拓展性和性能也是需要 tradeoff 的,一般拓展性越好的,性能可能越差),通过抽象接口,可以达到比较高的接口复用率,避免维护太多无意义的接口。
同时你也要避免过于看重抽象性,接口主要还是面向业务开发的,如果业务需要你提供独立的接口(可能是为了避免大范围影响,或者可以做单业务的限流),此时不能为了抽象而抽象,有时候,还是需要提供一些定制化的能力,只是希望你下次设计接口的时候,可以多考虑一下。

3、接口规范性

一个标准的接口,通常会有如下属性:

3.1 接口调用域名(各个环境的域名都要提供)
3.2 接口调用路径(路由一般每个环境都是一样的)
3.3 接口鉴权方式 (如果接口外网可访问,建议一定要加上接口鉴权)
3.4 接口请求方式 (这里指HTTP的请求方式,GET/POST 等,实际上 PUT/DELETE/HEAD 这些业务上根本不用)
3.5 接口请求参数 (明确好请求参数的类型,和字段名,字段名建议用全小写+下划线的形式,如果有参考值更好)
3.6 接口返回格式(明确好返回数据的格式,字段名和类型,这里应该区分正常业务返回和异常业务返回)
3.7 错误码列表(最好给出统一的错误码列表,方便调用方明确错误)

一个完整的接口应该包含这些内容,在这里,很多人对请求方法该用什么请求方式纠结。根据http协议的官方说明来说。

HTTP 中的GET、POST、PUT、DELETE 就对应着对资源的查、改、增、删 4个操作。

然而实际上,我们基本不会用PUT / DELETE 。只用 GET、POST。

那么什么时候用GET,什么时候用POST 呢?

我们带着逆向思维来思考下,什么情况下不应该使用GET,GET 由于参数是在URL上的,所以它的安全性和拓展性比较差。非开发人员都可以直接通过修改URL的参数来进行简单的试错。而且浏览器对URL的长度也是有限制的,因此它是存在限制的,在日常的开发中,我们通常认为,获取数据就应该用 GET 、对数据的修改,新增、删除都应该使用 POST,带着这种思维去实现接口,你会慢慢的发现,你的 GET 接口变的越来越臃肿,举个例子,你要提供一个批量获取数据的接口,参数是ID列表,此时如果你用GET,那么如果你的ID是UUID类型的或者说是雪花ID类型,那么很容易你就可以突破那个URL长度的限制。所以说接口的规范本质上也不是一成不变的。

至于市面上的 RESTFUL 规范,我也不建议你全套照着他那么做,实现起来真的十分的别扭。

一般如果公司有成文的规范是最好的,或者部门内部有个统一的方式。比如像我们部门,我们在用 proto 定义接口时,都默认使用 POST,不再使用 GET了,一个是为了我们接口文档更好的生成(基于proto 生成接口文档),另一个是考虑接口的安全性,拓展性等。

在这里,还有一个问题,数据应该放在哪里?
一般来说,我们请求参数可以放在 body 、query 、header、cookie 里

header 我们一般用来放接口的协议参数,例如调用接口时传的授权token。一般是业务通用的,不会带很明确的某个业务属性。
因此我不建议你把一些业务独有的字段放在 header 里。参数还是要习惯性的放在 body 或者 query 里。

cookie也是很有用的,如果你希望基于某个域名自动带上某个确定的参数,用它就很爽了,但是你最好将它设置为 http-olny 。是否设置为允许跨域,就要看具体的业务了

如果你使用的POST ,强烈建议使用 JSON 而不是 form-data ,你只需要在 header 里添加:Content-type: application/json,
大多数框架的解析器就会自动帮你解析参数。至于JSON的好处,大家可以自行百度。

4、接口的实现

当你的接口已经设计完成了,接下来就是实现环节了,不管你是使用哪种后端语言,都可以实现对应的HTTP接口,从网络的层次来看,所谓的HTTP接口,本质上就是指定IP的端口通讯(端口默认是80/443,当然你用别的端口也可以),像Java可以配合Tomcat、NGINX等来作为网关。如果你是微服务体系,可以使用 etcd 来实现服务的注册和发现。

在开发过程中,一般我们会对接口的实现进行简单的分层处理,例如, 入口层-逻辑层-仓储层。

入口层:接口的第一个层次,一般用来做参数校验,如果你们公司有成熟的校验工具,可以直接用注解来拦截并实现简单的参数格式校验的话,那么就会轻松很多
逻辑层:建议业务逻辑都在这里实现,并且可以进行封装以达到复用。
仓储层:仓储层是和数据库打交道的层次,用来操作数据的。

如果你学过DDD,那么就可以分成控制器层、应用层、领域层、仓储层。但是说真的,如果不是很复杂的业务,不建议使用DDD来设计。各种对象转换,确实会让你写到怀疑人生。

5、接口的联调

当后端接口实现完了,就需要先进行自测,强烈建议后端先使用 postman 进行自测,有条件的可以补充下接口的单元测试,然后再和前端进行联调,我们不关心前端是怎么样,我们只需要对接口负责,(如果是前后端不分离的项目,那么就当我没说吧)

在对接联调过程中,curl 是个很好的东西。如果前端说你接口有问题,你可以直接让他提供 curl 给你。因为里面包含着请求你接口的所有现场(域名、路由、参数、有些我们还能直接基于 CURL 进行接口的重放)。对一些新手或者说不是特别专业的人来说,问一句答一句的效率真的很低。我可以给你举个例子:

前端:你的XX接口有问题
后端:哪个接口?
前端:…
后端:你请求的是哪个环境,参数是什么?
前端:…
后端:能看到我的错误返回吗?
前端:等等,好像是我代码写的有问题。
后端:。。。

(这里并没有针对前端,因为后端和后端之间也会有类似的问题,只是举个例子)

6、接口的状态

接口实际上是有两种形态的,有状态的和无状态的,在服务端,我们更加喜欢无状态的接口,因为这种接口的拓展性很强,可以多物理机多容器部署,而不受影响,而有状态的接口,在拓展时就需要考虑到状态的复制或者迁移。举个简单的例子。一个简单的前后端分离项目,后端是直接在本地的服务器上存储了登录态,此时,我们是无法将这个项目进行多机部署的。

这个时候,我们通常会引入第三方来存储我们的状态,这样我们的接口就可以变成无状态了的,例如使用 MySQL、Redis等。

四、接口的部署架构

常见的部署架构。如果是 HTTP 接口。万变不离其宗,本质上就是 :

客户端->DNS->网关-服务

其中DNS 我们一般都用现成的。 网关的选型就比较多了。Tomcat 、NGINX、kong、apisix 等等。
大家最为熟悉的肯定还是 NGINX。

如果是微服务体系的,可以参考一下我这篇文章的相关图文:
浅谈单体服务向微服务的演进

五、接口要考虑哪些重要的东西?

最后才是精华。
在我们编写后端接口的时候。是需要考虑很多东西的。比如说安全性、拓展性、稳定性等等。我们展开来说说。

1、安全性

1.1 IP白名单
你可以通过设置IP白名单来限制别人对你的接口的访问

1.2 调整默认端口
不使用默认的80或443端口,这样别人必须指定端口才能访问你的服务

1.3 增加鉴权机制
如果是外网可访问,还是最好加上鉴权机制,一般有几种方式,一种是通过一种加密算法实时算出一个token,一种是你申请临时访问凭证,短时间内可以重复使用这个凭证,鉴权系统的好坏,也会影响接口的性能。比如说实时算的,你需要解出来看对不对的(会消耗CPU)。申请临时的,为了保证性能,这种一般会在Redis中放某个会过期的临时凭证。最常用的算法就是JWT了。

1.4 网络的限制
比如说只能内网访问

2、稳定性

稳定性也十分的重要。尤其是面对高并发的情况。分几个角度讲下。

2.1 数据库安全性

不要因为别人的请求,打爆了你的数据库呀。一个是你要避免你的接口存在慢查询的SQL。可以适当的使用Redis 来进行数据的缓存。当你编写接口的时候,就要思考这个接口的数据实时性应该如何考虑,如果实时性要求很高,那你的Redis也需要进行同步的更新。也不能无脑的使用Redis来做缓存。

2.2 可能是恶意的攻击

针对DDOS这种攻击,一般大家想到的第一做法就是禁IP,上高仿(砸钱抗),有时候大流量也并都是攻击,也可能是正常业务或者是说别人没有风险提前识别到,导致大量流量打到了你的接口上。我之前就碰到了一个真实案例,某某人使用事件驱动,在某段时间推送了大量的事件,每个事件都需要调用一次接口。然后就把接口对应的数据库实例给打爆了。这种情况下,即使使用了Redis来挡,也需要考虑几种场景,命中率的问题,命中率低的话流量还是会打到数据库。或者热点KEY的问题,可能会把Redis的CPU使用率搞起来,或者是大key的问题,再或者是用了不正确的Redis命令(比如说keys)。

因此,我们可以使用限流的机制。确保流量不会超过我们的额定流量。限流有很多种是实现方式,比如说计数器方式、队列算法、漏桶算法,令牌桶算法、基于响应时间的动态限流 等等。

一般的策略是超出的流量直接返回失败,或者是排队等待处理。通过这种方式来保证系统不会过载。

当然,为了避免多米诺骨牌效应,我们还可以搞服务熔断。

当资源和访问量出现矛盾的时候,我们在有限的资源下,为了能够扛住大量的请求,我们也可以对系统进行降级操作。通过暂时牺牲掉一些东西,来保障整个系统的平稳运行。

3、拓展性

1、接口的幂等性

这是接口的自我保护机制,针对获取数据的接口,自然就是幂等的。对于数据变动的,尤其是新增数据的接口。
幂等性的接口,在某些业务场景下是合适的。比如说,在某些流程链路较长的业务上。有可能会发生业务中断。此时系统为了支持重试,就会需要一些接口支持幂等性。不存在就新增,存在就返回对应的ID。如果他不提供幂等性的接口,那么调用方就必须先判断之前是不是新增过了,是不是得把之前调用过的数据存下来。会比较麻烦。也有一种场景,事件驱动新增数据的,有可能事件会重推,此时如果接口不是幂等的。那么重推事件的消费必然是失败的,如果你的消息中心还不能识别它是合理的业务错误,可能就会进行大量的重试。

2、接口本身的拓展性
其实也是和接口的抽象性有关,在实现接口的时候,多考虑一下其他调用场景。尽量避免类似的需求都要新增一个接口,维护起来真的很麻烦。不单单只是接口需要这么考虑,在日常的架构设计中或者产品模型设计中,都要多考虑场景问题。比如说我搞重构的时候。流程是这样的:

  • 梳理旧业务逻辑和模型
  • 收集业务方需求
  • 输出新模型
  • 通过各种场景预演验证新模型的可行性
  • 确定新模型
  • 基于新模型开发
  • 灰度验证和数据迁移

可能还有很多我没有讲到的。上面说的基本都是我的亲身经历,大家也可以补充下自己遇到的。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MClink

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值