后端接口设计开发注意事项
1、接口参数校验
2、接口老版本兼容
3、接口扩展性考虑
4、接口防重处理
①对于查询类型、删除类型接口,不论调用多少次,都是不会产生错误的业务数据,因此不用做防重处理;
②对于新增和修改,例如转账或者提现类接口,重复提交就会多次转账和提现,影响业务需要做防重处理,让前端传入请求序列号,可以采用redis、LRUMap、数据库防重表、分布式锁等处理。
id获取全局请求token —> 写入redis缓存 —> 请求时带上token —> 后端删除 —> 再次请求提示重复
5、核心接口,线程池隔离
登录接口、首页数据接口、转账提现接口等,都可能使用到线程池,某些普通接口也会使用线程池,如果不做线程池隔离,普通接口出bug线程池打满,会导致登录等主要业务受到影响。
6、关键接口,日志打印
关键业务代码,需要打印日志进行保驾护航,在入参和出参位置或者其他关键位置,良好的日志打印具有如下好处:
①方便排查定位线上问题,划清问题责任;
②生产环境不能直接debug,必须依靠日志查问题和具体异常。
7、三方接口异常、重试、超时
如果调用第三方接口,或者分布式远程服务的的话,需要考虑:
①异常处理
比如,你调别人的接口,如果异常了,怎么处理,是重试还是当做失败还是告警处理。
②接口超时
没法预估对方接口一般多久返回,一般设置个超时断开时间,以保护你的接口。之前见过一个生产问题,就是http调用不设置超时时间,
最后响应方进程假死,请求一直占着线程不释放,拖垮线程池。
③重试次数
你的接口调失败,需不需要重试?重试几次?需要站在业务上角度思考这个问题
8、接口功能单一性原则
单一性是指接口做的事情比较单一、专一。比如一个登陆接口,它做的事情就只是校验账户名密码,然后返回登陆成功以及userld即可。
但是如果你为了减少接口交互,把一些注册、一些配置查询等全放到登陆接口,就不太妥。
其实这也是微服务一些思想,接口的功能单一、明确。比如订单服务,积分,商品信息相关的接口都是划分开的。将来拆分微服务的话。
是不是就比较简便啦。
9、接口部分场景采用异步处理
举个简单的例子,比如你实现一个用户注册的接口。用户注册成功时,发个邮件或者短信去通知用户。这个邮件或者发短信,就更适合异步处理。因为总不能一个通知类的失败,导致注册失败吧。
至于做异步的方式,简单的就是用线程池。还可以使用消息队列,就是用户注册成功后,生产者产生一个注册成功的消息,消费者拉到注册成功的消息,就发送通知。
10、接口查询优化,串行改为并行
假设我们设计一个APP首页的接口,它需要查用户信息、需要查banner信息、需要查弹窗信息等等。那你是一个一个接口串行调,还是并行调用呢?
可以使用CompletableFuture并行调用提高性能。
//查询获奖经历
LambdaQueryWrapper<RewardExp> rewardExpQuery=new LambdaQuerywrapper<RewardExp>()
.eq(RewardExp::getResumeId,resume.getid())
.eq(RewardExp::getDe1Flag,NORMAL)
.orderByDesc(RewardExp::getDate);
Comp1etableFuture<List<RewardExp>> rewardExpFuture=CompetableFuture.supp1yAsync(() ->
rewardExpMapper.selectList(rewardExpquery)
);
// 查询资格证书
LambdaQuerywrapper<Credentia> credentiaQuery=new Lambdaquerywrapper<Credentia>()
.eq(Credentia1::getresumeId,resumegetid())
.eq(Credential::getDe1Flag,NORMAL)
.orderByDesc(Credential::getDate);
Comp1etableFuture<List<Credentia1>> credentia1Future=CompetableFuturesuppyAsync(()-
credentia1Mapper.selectList(credentialquery)
);
// 查询工种
LambdaQueryWrapper<ResumeJobs> jobsQuery=new LambdaQueryWrapper<ResumeJobs>()
.eq(ResumeJobs::getResumeId,resumegetd()).eq(ResumeJobsgetDe1F1ag,NORMAL);
Comp1etableFuture<List<ResumeJobs>> jobs Future=Comp1etabeFuture.supp1yAsync(() ->
resumeJobsMapper.selectList(jobsQuery)
);
Comp1etableFuture.a71of(rewardExpFuture,credentialFuture,jobsFuture).join();
11、接口合并与批量处理
数据库操作或或者是远程调用时,能批量操作就不要for循环调用。一个简单例子,我们平时一个列表明细数据插入数据库时,不要在for
循环一条一条插入,建议一个批次几百条,进行批量插入。同理远程调用也类似想法,比如你查询营销标签是否命中,可以一个标签一个
标签去查,也可以批量标签去查,那批量进行,效率就更高。
12、接口性能,Sql优化
我们做后端的,写好一个接口,离不开SQL优化。
SQL优化从这几个维度思考:
①explain分析SOL查询计划(重点关注typeextrafiltered字段)
②索引优化(覆盖索引、最左前缀原则、隐式转换、order by以及groupby的优化join优化)
③大分页问题优化(延迟关联、记录上一页最大ID)
④数据量太大(分库分表、同步到es,用es查询)
参与讨论
(Participate in the discussion)
参与讨论