什么?你设计接口什么都不考虑?

 

如果让你设计一个接口,你会考虑哪些问题?

1.接口参数校验

接口的入参和返回值都需要进行校验。

  • 入参是否不能为空,入参的长度限制是多少,入参的格式限制,如邮箱格式限制

  • 返回值是否为空,如果为空的时候是否返回默认值,这个默认值需要和前端协商

2.接口扩展性

举个例子,比如用户在进行某些操作之后,后端需要进行消息推送,那么是直接针对这个业务流程来开发一个专门为这个业务流程服务的消息推送功能呢?还是说将消息推送整合为一个通用的接口,其他流程都可以进行调用,并非针对特定业务。

这个场景可能光靠说不是很能理解,大家想想策略工厂设计模式,是不是可以根据不同的策略,来选择不同的实现方式呢?再结合上面的这个例子,是否对扩展性有了进一步的理解呢?

3.接口幂等设计

什么是幂等呢?幂等是指多次调用接口不会改变业务状态,可以保证重复调用的结果和单次调用的结果一致。

举个例子,在购物商场里面你用手机下单,要买某个商品,你需要去支付,然后你点击了支付,但是因为网速问题,始终没有跳转到支付界面,于是你又连点了几次支付,那在没有做接口幂等的时候,是不是你点击了多少次支付,我们就需要执行多少次支付操作?

所以接口幂等到的是什么?防止用户多次调用同一个接口

  • 对于查询和删除类型的接口,不论调用多少次,都是不会产生错误的业务逻辑和数据的,因此无需幂等处理

  • 对于新增和修改,例如转账等操作,重复提交就会导致多次转账,这是很严重的,影响业务的接口需要做接口幂等的处理,跟前端约定好一个固定的token接口,先通过用户的id获取全局的token,写入到Redis缓存,请求时带上Token,后端做处理

4.关键接口日志打印

关键的业务代码,是需要打印日志进行监测的,在入参和返回值或者如catch代码块中的位置进行日志打印

  • 方便排查和定位线上问题,划清责任

  • 生产环境是没有办法进行debug的,必须依靠日志查问题,看看到底是出现了什么异常情况

5.核心接口要进行线程池隔离

分类查询啊,首页数据等接口,都有可能使用到线程池,某些普通接口也可能会使用到线程池,如果不做线程池隔离,万一普通接口出现bug把线程池打满了,会导致你的主业务受到影响

6.第三方接口异常重试

如果有场景出现调用第三方接口,或者分布式远程服务的话,需要考虑的问题

  • 异常处理

    比如你在调用别人提供的接口的时候,如果出现异常了,是要进行重试还是直接就是当做失败

  • 请求超时

    有时候如果对方请求迟迟无响应,难道就一直等着吗?肯定不是这样的,需要设法预估对方接口响应时间,设置一个超时断开的机制,以保护接口,提高接口的可用性,举个例子,你去调用别人对外提供的一个接口,然后你去发http请求,始终响应不回来,此时你又没设置超时机制,最后响应方进程假死,请求一直占着线程不释放,拖垮线程池。

  • 重试机制

    如果调用对外的接口失败了或者超时了,是否需要重新尝试调用呢?还是失败了就直接返回失败的数据?

7.接口是否需要采用异步处理

举个例子,比如你实现一个用户注册的接口。用户注册成功时,发个邮件或者短信去通知用户。这个邮件或者发短信,就更适合异步处理。总不能一个通知类的失败,导致注册失败吧。那我们如何进行异步操作呢?可以使用消息队列,就是用户注册成功后,生产者产生一个注册成功的消息,消费者拉到注册成功的消息,就发送通知。

8.接口查询优化,串行优化为并行

假设我们要开发一个网站的首页,我们设计了一个首页数据查询的接口,这个接口需要查用户信息,需要查头部信息,需要查新闻信息

等等之类的,最简单的就是一个一个接口串行调用,那要是想要提高性能,那就采取并行调用的方式,同时查询,而不是阻塞

可以使用CompletableFuture(推荐)或者FutureTask(不推荐)

  1.         Map<Long, List<SubjectLabelBO>> map = new HashMap<>();

  2.         List<CompletableFuture<Map<Long, List<SubjectLabelBO>>>> completableFutureList = 

  3.         categoryBOList.stream().map(category ->

  4.                 CompletableFuture.supplyAsync(() -> getLabelBOList(category), labelThreadPool)

  5.         ).collect(Collectors.toList());

  6.         completableFutureList.forEach(future -> {

  7.             try {

  8.                 Map<Long, List<SubjectLabelBO>> resultMap = future.get(); //这里会阻塞

  9.                 map.putAll(resultMap);

  10.             } catch (Exception e) {

  11.                 e.printStackTrace();

  12.             }

  13.         });

  14.         public Map<Long, List<SubjectLabelBO>> getLabelBOList(SubjectCategoryBO category) {...}

9.高频接口注意限流

自定义注解 + AOP

  1. @Target(ElementType.METHOD)

  2. @Retention(RetentionPolicy.RUNTIME)

  3. public @interface RateLimiter {

  4.     int value() default 1;

  5.     int durationInSeconds() default 1;

  6. }

  7. @Aspect

  8. @Component

  9. public class RateLimiterAspect {

  10.     private final ConcurrentHashMap<String, RateLimiter> rateLimiters = new ConcurrentHashMap<>();

  11.     @Pointcut("@annotation(RateLimiter)")

  12.     public void rateLimiterPointcut(RateLimiter rateLimiterAnnotation) {

  13.     }

  14.     @Around("rateLimiterPointcut(rateLimiterAnnotation)")

  15.     public Object around(ProceedingJoinPoint joinPoint, RateLimiter rateLimiterAnnotation) throws Throwable {

  16.         int permits = rateLimiterAnnotation.value();

  17.         int durationInSeconds = rateLimiterAnnotation.durationInSeconds();

  18.         // 使用方法签名作为 RateLimiter 的 key

  19.         String key = joinPoint.getSignature().toLongString();

  20.         com.google.common.util.concurrent.RateLimiter rateLimiter = rateLimiters.computeIfAbsent(key, k -> com.google.common.util.concurrent.RateLimiter.create((double) permits / durationInSeconds));

  21.         // 尝试获取令牌,如果获取到则执行方法,否则抛出异常

  22.         if (rateLimiter.tryAcquire()) {

  23.             return joinPoint.proceed();

  24.         } else {

  25.             throw new RuntimeException("Rate limit exceeded.");

  26.         }

  27.     }

  28. }

  29. @RestController

  30. public class ApiController {

  31.     @GetMapping("/api/limited")

  32.     @RateLimiter(value = 10, durationInSeconds = 60) //限制为每分钟 10 次请求

  33.     public String limitedEndpoint() {

  34.         return "This API has a rate limit of 10 requests per minute.";

  35.     }

  36.     @GetMapping("/api/unlimited")

  37.     public String unlimitedEndpoint() {

  38.         return "This API has no rate limit.";

  39.     }

  40. }

10.保障接口安全

配置黑白名单,用Bloom过滤器实现黑白名单的配置

具体代码不贴出来了,大家可以去看看布隆过滤器的具体使用

11.接口控制锁粒度

在高并发场景下,为了防止超卖等情况,我们会对共享资源进行加锁的操作来保证线程安全的问题,但是如果加锁的粒度过大,是会影响

到接口性能的。那什么是加锁粒度呢?举一个例子,你带了一封情书回家,但是不想被爸妈发现,然后你偷偷回到房间里放到一个可以锁

住的抽屉里面,而不用把房间的门锁给锁上。无论是使用synchronized加锁还是redis分布式锁,只需要在共享临界资源加锁即可,不涉

及共享资源的,就不必要加锁。

  • 锁粒度过大:

    把方法A和方法B全部进行加锁,但是实际上我只是想要对A加锁,这就是锁粒度过大

  1. void test(){

  2.     synchronized (this) {

  3.        B();

  4.        A();

  5.     }

  6. }

  • 缩小锁粒度

  1. void test(){

  2.        B();

  3.     synchronized (this) {

  4.        A();

  5.     }

  6. }

12.避免长事务问题

长事务期间可能伴随cpu、内存升高、严重时会导致服务端整体响应缓慢,导致在线应用无法使用

产生长事务的原因除了sql本身可能存在问题外,和应用层的事务控制逻辑也有很大的关系。

  • 如何尽可能的避免长事务问题呢?

  1. RPC远程调用不要放到事务里面

  2. 一些查询相关的操作如果可用,尽量放到事务外面

  3. 并发场景下,尽量避免使用@Transactional注解来操作事务,使用TransactionTemplate的编排式事务来灵活控制事务的范围

在原先使用@Transactional来管理事务的时候是这样的

  1. @Transactional

  2. public int createUser(User user){

  3.     //保存用户信息

  4.     userDao.save(user);

  5.     passCertDao.updateFlag(user.getPassId());

  6.     // 该方法为远程RPC接口

  7.     sendEmailRpc(user.getEmail());

  8.     return user.getUserId();

  9. }

使用TransactionTemplat进行编排式事务

  1. @Resource

  2. private TransactionTemplate transactionTemplate;

  3. public int createUser(User user){

  4.     transactionTemplate.execute(transactionStatus -> {

  5.       try {

  6.          userDao.save(user);

  7.          passCertDao.updateFlag(user.getPassId());

  8.       } catch (Exception e) {

  9.          // 异常手动设置回滚

  10.          transactionStatus.setRollbackOnly();

  11.       }

  12.       return true;

  13.     });

  14.     // 该方法为远程RPC接口

  15.     sendEmailRpc(user.getEmail());

  16.     return user.getUserId();

  17. }

总结:

感谢每一个认真阅读我文章的人!!!

作为一位过来人也是希望大家少走一些弯路,如果你不想再体验一次学习时找不到资料,没人解答问题,坚持几天便放弃的感受的话,在这里我给大家分享一些自动化测试的学习资源,希望能给你前进的路上带来帮助。

软件测试面试文档

我们学习必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有字节大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。

 

          视频文档获取方式:
这份文档和视频资料,对于想从事【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!以上均可以分享,点下方小卡片即可自行领取。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/328471.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

单位个人如何向期刊投稿发表文章?

在单位担任信息宣传员一职以来,我深感肩上的责任重大。每月的对外信息宣传投稿不仅是工作的核心,更是衡量我们部门成效的重要指标。起初,我满腔热血,以为只要勤勉努力,将精心撰写的稿件投至各大报社、报纸期刊的官方邮箱,就能顺利登上版面,赢得读者的青睐。然而,现实远比理想骨…

6、Qt—Log4Qt使用小记1

开发平台&#xff1a;Win10 64位 开发环境&#xff1a;Qt Creator 13.0.0 构建环境&#xff1a;Qt 5.15.2 MSVC2019 64位 一、Log4Qt简介 Log4Qt是使用Trolltech Qt Framework的Apache Software Foundation Log4j包的C 端口。它旨在供开源和商业Qt项目使用。所以 Log4Qt 是Apa…

OSPF实验

OSPF单区域实验案例 需求 实现全网互联互通 配置步骤 配置PC接口IP地址 配置路由器的接口IP地址 配置OSPF 创建ospf进程&#xff0c;定义router-id指定相应区域宣告网段进入ospf 验证结果 配置命令 第一步&#xff1a;配置PC接口IP地址 第二步&#xff1a;配置路由器接口…

Leaflet.canvaslabel在Ajax异步请求时bindPopup无效的解决办法

目录 前言 一、场景重现 1、遇到问题的代码 2、问题排查 二、通过实验验证猜想 1、排查LayerGroup和FeatureGroup 2、排查Leaflet.canvaslabel.js 三、柳暗花明又一村 1、点聚类的办法 2、歪打正着 总结 前言 在上一篇博客中介绍了基于SpringBoot的全国风景区WebGIS按…

【多模态】31、Qwen-VL | 一个开源的全能的视觉-语言多模态大模型

文章目录 一、背景二、方法2.1 模型架构2.2 输入和输出2.3 训练 三、效果3.1 Image Caption 和 General Visual Question Answering3.2 Text-oriented Visual Question Answering3.3 Refer Expression Comprehension3.4 视觉-语言任务的少样本学习3.5 真实世界用户行为中的指令…

BGP(border gateway protocol)边界网关协议初识篇

BGP它是一种路径矢量协议&#xff0c;用于决定数据包在互联网中的最佳路径。 1、工作原理&#xff1a; 自治系统&#xff08;AS&#xff09;间路由: BGP主要用于连接不同自治系统之间的路由器&#xff0c;其中每个自治系统&#xff08;AS&#xff09;代表一组具有共同路由的网…

Rust构造JSON和解析JSON

目录 一、Rust构造JSON和解析JSON 二、知识点 serde_json JSON 一、Rust构造JSON和解析JSON 添加依赖项 cargo add serde-json 代码&#xff1a; use serde_json::{Result, Value};fn main() -> Result<()>{//构造json结构 cpu_loadlet data r#"{"…

【C -> Cpp】由C迈向Cpp (6):静态、友元和内部类

标题&#xff1a;【C -&#xff1e; Cpp】由C迈向Cpp &#xff08;6&#xff09;&#xff1a;静态、友元和内部类 水墨不写bug &#xff08;图片来源于网络&#xff09; 目录 &#xff08;一&#xff09;静态成员 &#xff08;二&#xff09;友元 &#xff08;三&#xff09…

想让普通金额数字显示为逗号分隔的数字?

使用vueelement 后台传的数据 1.编写方法 放在method当中 /** 数字转换显示格式 */priceFormat (num, n) {n n || 2;let symbol ",";if (num null) return num;if (typeof num ! number) throw new TypeError(num参数应该是一个number类型);if (n < 0) thro…

eNSP中小型园区网络拓扑搭建(上)

→b站直通车&#xff0c;感谢大佬← →eNSP中小型园区网络拓扑搭建&#xff08;下&#xff09;← 不带配置命令的拓扑图已上传~ 项目背景&#xff1a; 某公司准备新建一张网络供企业办公使用。写字楼共3层&#xff0c;一层会客大厅、二层行政部及市场部、三层研发部。一层设…

基于java的超级玛丽游戏的设计与实现(论文 + 源码)

Java的超级玛丽游戏.zip资源-CSDN文库https://download.csdn.net/download/JW_559/89313347 基于java的超级玛丽游戏的设计与实现 摘要 近年来&#xff0c;Java作为一种新的编程语言&#xff0c;以其简单性、可移植性和平台无关性等优点&#xff0c;得到了广泛地应用。J2SE称…

容联云零代码平台容犀desk:重新定义坐席工作台

在数智化浪潮的推动下&#xff0c;企业亟待灵活适应市场变化、快速响应客户需求&#xff0c;同时还要控制成本并提升效率&#xff0c;传统的软件开发模式因开发周期长、成本高、更新迭代慢等问题&#xff0c;逐渐难以满足企业灵活多变的业务需求。 容犀Desk&#xff0c;观察到…

Linux网络编程——HTTP协议的理解与运用

目录 前言 一、认识URL 二、认识HTTP样例 三、HTTP的报头内容 1.url 2. Content-Type 3.Method 方法 1.GET方法 2.POST方法 4、状态码 5.cookie和session 前言 我们知道&#xff0c;协议就是一种约定&#xff0c;客户端与服务端统一的用这种约定进行传输数据。我们…

如何快速优雅的免费申请和搭建属于自己的服务器

今天来讲一下如何快速优雅的搭建属于自己的服务器&#xff0c;我们以阿里云的云服务器为例&#xff0c;新用户一般是有三个月使用期限。 首先我们进入官网&#xff0c;选择云服务器ecs 链接直达&#xff1a;https://cn.aliyun.com 打开网页后&#xff0c;往下滑&#xff0c;然…

「AIGC」Python实现tokens算法

本文主要介绍通过python实现tokens统计,避免重复调用openai等官方api,开源节流。 一、设计思路 初始化tokenizer使用tokenizer将文本转换为tokens计算token的数量二、业务场景 2.1 首次加载依赖 2.2 执行业务逻辑 三、核心代码 from transformers import AutoTokenizer imp…

ssrf初步

一&#xff0c;简介 全称&#xff1a;Server-Side Request Forgery&#xff08;中文&#xff1a;服务器端请求伪造&#xff09; 攻击者从服务端发起请求&#xff0c;让服务器连接任意外部系统&#xff0c;从而泄露敏感数据。主要利用各种协议的请求伪造&#xff0c;例如php协…

战网国际服怎么下载 暴雪战网一键下载安装图文教程

战网国际版&#xff0c;或称为Battle.net全球版&#xff0c;是暴雪娱乐构建的一项跨越国界的综合游戏交流平台&#xff0c;它无视地理限制&#xff0c;旨在服务全球每一个角落的游戏爱好者。不同于地区专属版本&#xff0c;国际版为玩家开启了一扇无门槛的大门&#xff0c;让每…

char x[]---char*---string---sizeof

字符串数组 #include <iostream>int main(){char c_str[]"abcd";char c_str1[]{a,b,c,d};std::cout<<sizeof(c_str)<<std::endl;std::cout<<sizeof(c_str1)<<std::endl;return 0; } char*存储的字符串个数 char*字符串所占字节大小 c…

Hadoop3:HDFS副本节点选择逻辑讲解

一、副本节点选择&#xff08;机架感知&#xff09; 说明 第一个副本&#xff0c;因为我们的client可能是web页&#xff0c;也可能是shell终端。 如果是web页&#xff0c;则随机选取一个节点&#xff0c;如果是shell终端&#xff0c;则选择当前shell终端所在的节点。 节点距离最…

【计算机毕业设计】springboot分类信息服务平台移动端的设计与实现

分类信息 服务平台设计的目的是为用户提供活动信息、活动记录等方面的平台。 与PC端应用程序相比&#xff0c;分类信息服务平台的设计主要面向于移动端&#xff0c;旨在为管理员和用户、商铺提供一个分类信息服务平台。用户可以通过Android及时查看活动信息等。 分类信息服务平…