大家好,欢迎来到IT知识分享网。
大多数程序员,做得最多的事,也不过是写接口这件事而已。
今天总结下接口设计需要注意的事情。尽量每种都给出具体的场景、案例等,希望大家能有所收获。
1.逻辑梳理
- SequenceDiagram
- 准备开发备忘录
2.参数校验
3.接口的兼容性
我记得组内曾经就出现过,同事修改了PC端的接口,影响到了移动端的功能,因为当时没有移动端的需求,边没有回归,最后导致了生产事故,紧急上线修复。
4.接口防重
实现方式
一般基于自定义注解和redis分布式锁实现。
注意
这种一般是针对页面的接口,如果是外部调用的接口,尽量不要做防重校验,而是在逻辑上、数据库层面进行控制,因为外部调用可能很频繁,短时间内很容易出现重复的调用。
5.接口限流、熔断、降级
对于分布式系统来说,为了避免某个应用不可用导致整个系统的不可用,需要降低服务间的强关联,避免服务雪崩,保证尽可能多的服务可用。
主要使用sentinel和Hystrix组件,在控制接口的请求数量、及时关闭对外部接口的调用。
6.外部调用的超时、异常、重试
外部调用的时候需要考虑超时、异常和重试几种场景。
超时
有个接口是和外部团队进行交互的场景,因为外部接口异常,导致有一批数据推送失败。最后只能去找日志,手动推送。像这种场景,可以和对方确认,是否支持重复推送,其次,在自身的系统里,也可以增加简单的重试机制。比如基于redis进行参数的缓存,并指定持久化机制等。
异常
对于重要的报错,是否需要进行短信或者站内信等方式进行通知?
重试
接口请求失败或者出现暂时性错误,重试是提高接口成哥率的重要手段。查询外部接口,最好是支持重试,设置超时时间。
场景的重试机制包括循环重试、并发框架异步重试、消息队列重试、redis重试以及使用Spring Retry库等方式。
- 循环重试:判断接口的返回值,报错时再次调用。
- 异步重试:比如CompletableFuture框架并提供了失败时的处理方法,可以再次调用
- 消息队列和redis重试:需要把请求参数封装成消息放到消息队列或者redis的队列中,进行消费,失败后继续重试,成功后删除消息。
- Spring Retry 库可以很方便地实现接口请求的重试机制,基于注解指定重试接口和处理策略。
@Retry(value = Exception.class, backoff = @Backoff(delay = 1200,multiplier = 1.5),maxAttempts = 3) public void notifyToiletryCreateOrder(String originalCode) throws ServiceException {
// 代码逻辑 }
7.日志打印
- 入参和出参,特别是外部调用的入参、出参;
- 重要逻辑的数据信息;
- 异常信息;
同时建议设置traceId,使用skywalking等工具实现接口的链路追踪,便于快速处理问题。
8.异步
- 外部调用,不需要返回值的场景,应该采用异步的方式,不能影响自身业务;
- 对于外部调用,最好能够统一操作,和自身逻辑拆分开,比如短信调用,可以放在最后。
比如报表接口,需要统一账单金额、收款金额、退款金额,就可以采用不同的线程进行数据统计,最后汇总,减少响应时间。
for (String communityCode : communityCodes) {
// 分项目计算 CompletableFuture.supplyAsync(() -> sumOne(), ThreadPool); // 汇总结果集 totalResult.add(communityResult); } CompletableFuture<Void> future = CompletableFuture.allOf(totalResult.toArray(completableFutures)); future.join(); // 汇总结果 result.addAll(Stream.of(completableFutures).map(CompletableFuture::join).flatMap(Collection::stream).collect(Collectors.toList())); // 后续处理 ...
9.影响内存或者数据库的场景
当我们处理数据的时候,要对处理的数据量有一定的了解,内存占用,耗时等,不要在生产环境直接导致事故。场景的比如大对象,长事务问题要注意。
大对象
长事务问题
@Transactional public int createOrder(String orderNo){
// 数据库操作 Order order = orderDbStorage.selectByCondition(orderNo) orderDbStorage.save(order); orderItemDbStorage.save(order.getItems()); // RPC调用 sendRpc(); // 消息推送 sendMessage(); return order.getId(); }
这种就要尽可能把事务的代码拆出来,减少事务的范围。
public int createOrder(String orderNo){
Order order = orderDbStorage.selectByCondition(orderNo); // 拆分数据库操作作为一个独立的事务 int id = OrderBService.createOrder(order ); sendRpc(); sendMessage(); return id; }
@Transactional public int createOrder( Order order){
orderDbStorage.save(order); orderItemDbStorage.save(order.getItems()); return order.getId(); }
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/133103.html