接⼝设计的16个原则
接⼝设计需要考虑哪些⽅⾯
1. 接⼝的命名。
2. 请求参数。
3. ⽀持的协议。
4. TPS、并发数、响应时长。
5. 数据存储。DB选型、缓存选型。
6. 是否需要依赖于第三⽅。
7. 接⼝是否拆分。
8. 接⼝是否需要幂等。
9. 防刷。
10. 接⼝限流、降级。
11. 负载均衡器⽀持。
12. 如何部署。
13. 是否需要服务治理。
14. 是否存在单点。
15. 接⼝是否资源包、预加载还是内置。
16. 是否需要本地缓存。
17. 是否需要分布式缓存、缓存穿透怎么办。
18. 是否需要⽩名单。
当我们设计接⼝,我们或多或少都会有上⾯列举的⼀些考虑,我们只有想的更多才能让让我们的接⼝更加完善,我个⼈觉得100%完美的接⼝是不存在,只有适合才是最重要。
接⼝设计原则
原则⼀:必须符合Restful,统⼀返回格式,约定业务层错误编码,每个编码可以携带可选的错误信息。
原则⼆:命名必须规范、优雅。
原则三:单⼀性。
单⼀性是指接⼝要做的事情应该是⼀个⽐较单⼀的事情,⽐如登陆接⼝,登陆完成应该只是返回登陆成功以后⼀些⽤户信息即可,但很多⼈为了减少接⼝交互,返回⼀⼤堆额外的数据。
⽐如有⼈设计⼀个⽤户列表接⼝,接⼝他返回每⼀条数据都是包含⽤户了⼀⼤堆跟另外⽆关的数据,结果⼀问,原来其他⽆关的数据是他下⼀步想要获取的,想达成数据的懒加载。
原则四:可扩展。
接⼝扩展性,是指设计接⼝的时候多想想多种情况,多考虑各个⽅⾯,其实我觉得单独将扩展性放在这⾥也是不妥的,感觉说的跟单⼀性有点相反的意思,其实这个不是这个意思。
这边的扩展性是指我们的接⼝充分考虑客户端,想想他们是如何调⽤的,他要怎样使⽤我的代码,他会如何扩展我的代码,不要把过多的⼯作写在你的接⼝⾥⾯,⽽应该把更多的主动权交给客户程序员。
如获取不同的列表数据接⼝,我们不可能将每个列表都写成⼀个接⼝。还有⼀点,我这⾥特别想指出来的是很多开发⼈员为了省事(姑且只能这么理解),将接⼝设计当成只是 app 页⾯展⽰。
这些⼈将⼀个页⾯展⽰就⽤⼀个接⼝实现,⽽不考虑这些数据是不是属于不同的模块、是不是属于不同的展⽰范畴、结果下次视觉⼀改,整个接⼝⼜得重写,不能复⽤。
原则五:必须有⽂档。
良好的接⼝设计,离不开清晰的接⼝⽂档表述。⽂档表述⼀定要⾜够详细
原则六:产品⼼。
为什么我说要有产品⼼?因为我觉得很多⼈忽略了这⼀点。我来说⼀下假如开发⼀个app,如果⼀开始连个交互⽂档给你都没有的话,你怎么设计接⼝?
所以我觉得作为⼀个服务端后台开发⼈员应该要有产品⼼,特别是对于交互⽂档应该好好理解,因为这些都会对我们的接⼝设计有很⼤的影响。
我在设计接⼝的时候就很常发现很多交互⽂档根本就⾛不通,产品没有考虑到位,交互⽂档缺失,这时候作为⼀个开发要主动推动,完善。
原则七:第三⽅服务接⼝数据能缓存就缓存。
原则⼋:第三⽅服务需要做降级。
原则九:建议消除单点。
原则⼗:接⼝粒度要⼩。
原则⼗⼀:客户端能处理的逻辑就不要给服务端处理,减少服务端压⼒。
原则⼗⼆:资源预加载。
原则⼗三:不要过度设计。
原则⼗四:缓存尽量不要穿透。
原则⼗五:接⼝能缓存就缓存。
原则⼗六:思辨⼤于执⾏
如何保证接⼝的⾼可⽤、⾼性能
上⾯也列举很多需要考虑和设计的原则,其实还有很多⽅⾯,我这边也不是特别全⾯。
居于上⾯列举的这些考虑点,其实这边说服务是更恰当,能把上⾯说的点做好,其实接⼝也是⽐较可靠,如何设计以及保证接⼝的⾼可⽤和⾼性能。可以思考⼀下以下⼏个 point
⾼性能:如果我们发现这个接⼝tps和响应时间没有达到我们的要求怎么办。
A:数据存储⽅⾯:我们会想数据库有没有分库、分表、有没有做主从,有没有读写分离、字段是否有加索引、是否存在慢 sql,数据库引擎是否选⽤合适、是不是⽤了事务;
其次我们会想到是不是引⽤了分布式缓存、缓存 key ⼤⼩是否合适,失效时间是否设置合理,会不会⼤量缓存穿透、有没有引⼊本地缓存。
B:业务⽅⾯:是否有⼤量的计算、能否异步处理。是否需要引⼊线程池或者 MQ 来异步处理任务。有没有必要将接⼝进⾏垂直拆分和⽔平拆分、将接⼝粒度变⼩。
C:其他⽅⾯:nginx 层⾯做缓存、加机器、⽤ ssd,资源放 cdn,多机房部署、资源⽂件预加载。
⾼可⽤:如何保证服务⾼可⽤,需要从⼏个维度来实现:
A:消除单点,基于⾼可⽤第⼆位。
B:能做集的全部做集。譬如 Redis 集、mysql集、MongoDB副本集。
C:能做读写分离的都做读写分离。
D:异地多机房部署,接⼊ GSLB
E:必须有限流、降级机制。
F:监控。⾼可⽤的保证,基于第⼀位。
下图是从⼀个基本的请求出发来梳理需要涉及到各个段,以及各个端能做的事情。谈谈接⼝服务,但不局限于接⼝本⾝。
1. 客户端:资源预加载、限制请求、数据上报。我这边就拿客户端来举个例⼦。接⼝服务所依赖的资源包或者⼀些公共配置预加载
在本地,减少接⼝的交互,通过请求配置⽂件是否更新,code是否是304等来;
接⼝做⼀些请求限制,⽐如抢红包、抢券等,单位时间内N次点击只请求⼀次等;接⼝失败数据上报来;这就是客户端可以做到的对接⼝有帮助的事情
2. GSLB/HttpDNS:多机房部署、流量切换、域名劫持,⼀般技术和业务⽐较成熟的公司这⼀层。
3. 资源⽂件放CDN。
4. 负载均衡器:lVS+Nginx是互联⽹常⽤的做负载均衡,可以实现四层/七层负载均衡;这⾥除了可以分流、转发以外,我们⽤的更
多的基于令牌桶限流、缓存。
5. 本地缓存。本地缓存能减少我们访问DB或者分布式缓存,本地缓存推荐使⽤guava,guava⾥⾯有很多特性很好⽤,例如基于令
牌桶的限流;当缓存失效时只穿透⼀个请求去访问后端。
6. 线程池。
7. 模块拆分。将⼀个项⽬按功能模块拆分,⼀个接⼝也可以按业务粒度进⾏拆分。
8. 数据中⼼。提供数据⽀撑,譬如⿊名单。
9. 数据库。加索引、分库、分表、读写分离
10. 分布式缓存。数据分⽚、拆分⼤key,并做集,采⽤分布式锁
11. MQ。做接⼝拆分利器,异步操作。
12. 其他服务。限流、防刷以及降级(特别是第三⽅服务,保证第三⽅服务down掉不要影响我们⾃⾝的服务)。在这⾥也需要考虑
做第三⽅数据的缓存或者持久化,譬如实名认证、⾝份证认证等。
13. 监控。监控永远是必须的,能让你第⼀时间知道接⼝服务是否ok
个⼈⼩分享
前端页面模板1)接⼝Restful,统⼀返回格式,约定业务层错误编码,每个编码可以携带可选的错误信息
在前司,客户端和服务之间是有统⼀的数据返回格式,约定各层的编码,可以通过编码位数以及编码就可以看出是那⼀层出问题。
我觉得这对我们定位问题以及维护来说具有莫⼤的意义,并对异常也进⾏捕捉,封装成对应的 code,我之前阅读⼀些⼈的代码发现其项⽬根本没有做这⼀层,因为简单⽽不做我觉得有失所望。
2)采⽤ hybird 模式
采⽤ hybird 模式涉及到资源预加载的问题,在很多项⽬⾥⾯都⼤量使⽤,譬如前司的⽣活服务,就采⽤了 hybird 模式,先将资源⽂件(包含图⽚、前端页⾯)打包放到服务器并通过版本号进⾏管理,并通过⼀个总的配置⽂件来管理,如果是H5页⾯可以进⾏模板预先设计,down到本地。
配置⽂件格式:
*⽂件1* name:xxx url:http:xxxx md5:xxxx *⽂件2* name:zzz url:http:zzzz md5:zzz
客户端每次启动应⽤或者定时请求总的配置⽂件,通过http code是否是304判断是否需要下载这个总的配置⽂件,如果code是200,那么下载这个配置,⽐较那个⽂件发⽣变化,并将其下载。这样的好处:
1. 减少接⼝的交互;
2. 资源预加载,节省流量,打开页⾯更加流畅,对于服务端来说字需要返回数据json串就⾏,⽽不需要其他,减少服务端压⼒;
3. ⽅便开发⼈员,资源管理更加简洁,⽐如做活动需要的h5页⾯,只需要前端上传对应的h5资源包到服务端,不需要通过后端开发
⼈员就可以搞定。
虽然这个原理很简单,但是现在很多app还是没有做这个,都是通过填写⼀个url,加载⽹页的⽅式去打开,体验性太不友好。
3)客户端
客户端跟服务端就是接⼝请求的关系,很多时候需要要求客户端做⼀些数据缓存的⼯作以及⼀些检验⼯作。在前司已经好⼏次给客户端的同学坑过了,客户端同学接⼝乱调⽤,死循环调⽤。
⼀次是做⼀个关于事件提醒的功能,需要每天定时调⽤调⽤服务端⼀个接⼝,结果客户端的同学写了⼀个 bug 导致请求每隔⼀两秒就调⽤⼀次,导致服务器这边此接⼝ pv 翻了N倍,⽽且这个 bug 通过测试同学很难测试出来;
还有⼀次发现服务端⼀段时间以后 UV 不见涨,但是PV却涨的很猛,定位发现是客户端同学A图省事在⼀个⽅法⾥⾯调⽤了N个接⼝,也就是模板⽅法。
因为版本更新,同学B需要做⼀个新的功能,然后也调⽤了A同学的接⼝导致,从⽽导致PV上升,其实B同学完全不需要调⽤这么多接⼝。这些都是真实案例,所以这⾥需要有⼀个监控接⼝异常的机制。
4)思辨⼤于执⾏
写到这⾥觉得这个⾮常重要,思辨⼤于执⾏,意味着我们不是⼀股脑就去⼲,也不是不去⼲,我们做事情需要思考、辨别;从⽽让事情更⾼效、更好、更有⼒的执⾏。接⼝设计也⼀样,需要我们去思辨。
5)本地缓存、分布式缓存以及异步
缓存在前司主要分为客户端缓存、CDN缓存、本地缓存(guava)、Redis缓存。
在MZ早期是接⼝是采⽤ DB+本地缓存的⽅式提供数据,但这种模式DB压⼒⼤,接⼝吞吐量⼩,本地缓存多机难⼀致性、更新不及时问题。
为了解决这些问题,引⼊分布式缓存,并通过 Task 将业务数据刷到 Redis,接⼝只访问 redis,不会访问 DB,及时 DB 故障也不会影响功能。
不同的业务系统系统通过 MQ 来解耦,多机房不是通过 MQ 来实现数据的⼀直。
⽐如,评论,先通过写 Redis,写 MQ 来实现数据在多机房同步,再通过 task 将 Redis 中评论同步到 DB 中。
接⼝设计涉及⽅⽅⾯⾯,这边也只谈到⼀个⼤概,虽然有点泛泛⽽谈,希望此拙⽂对你有所启⽰。
6)数据库
数据库分库分表,⼀般都是通过 userId 或者 imei 或者 mac 地址来分表,单表数据量控制在500w以内,这需要我们提前估算好数据量,尽量避免数据的迁移。
在前司,数据库⼀般都是采⽤ mysql+MongoDB 两种,MySQL存储⽤户的⽤户数据,MongoDB 存储业务数据,就像阅读和⽣活服务⾥⾯的业务数据就存储在 MongoDB ⾥⾯。
在数据库这层,我们主要也是通过主从模式、读写分离、分库、分表来实现数据的可⽤性。
7)业务
业务尽可能拆分、独⽴部署、将项⽬按业务划分、按功能划分等。譬如⽣活服务,我们当时主要拆分成管理后台 admin、任务 task、活动、web、数据展⽰模块。
8)数据中⼼
每个⼤⼀点的公司都有数据部门,我们这边可以通过数据中⼼的数据分析来达到我们需要的数据。
⽐如⿊名单,推⼴效果、活动数据。我们可以通过这些完善我们的接⼝功能。之前在前司做了个数据处理后异步加载到 Redis 来实现数据利⽤的项⽬。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论