常见场景面试题-接口重试策略、二维码扫描原理、幂等性、排行榜(三)

news2025/1/15 16:49:43
typora-copy-images-to: imgs

微信二维码扫描原理:

答:流程:

image.png

总的来说,PC 端需要进行扫码登陆的原理是通过二维码绑定移动端的身份信息以及PC端的设备信息,根据这两个信息生成 token 给 PC 端,PC 端就登陆成功了。

二维码准备:

  1. PC端向服务器发起请求,表示要生成用户二维码,并且把 PC 端设备信息也传递给服务端
  2. 服务端收到请求后,生成唯一的二维码 ID,并将二维码 ID 与 PC 端设备信息进行绑定
  3. 服务端将二维码 ID 返回给 PC 端
  4. PC 端收到二维码 ID 后,生成二维码
  5. PC 端为了及时知道二维码的状态(是否已经扫描,扫描后是否已经确认),会不断轮询服务端,请求服务端当前二维码的状态及相关信息

扫描状态切换:

  1. 用户扫描二维码后,读取到二维码 ID
  2. 向服务端发送请求,并携带移动端的身份信息与二维码 ID
  3. 服务端接收之后将身份信息与二维码 ID 进行绑定,生成临时 token,返回给移动端
  4. 在移动端扫描完之后,PC 端会轮询二维码状态,修改为已扫描,此时二维码 ID 会与账号信息进行绑定

第三步返回给移动端临时 token 是要保证移动端在下一步操作时,使用这个临时 tokne 作为凭证,保证两步操作是同一部设备发出的,临时 token 只可以使用一次就失效。

登陆确认:

  1. 移动端接收到临时 token 后会弹出确认登陆界面,点击确认,移动端会携带临时 token 调用服务端接口
  2. 服务端收到确认后,根据二维码 ID 绑定的设备信息与账号信息,生成 PC 端 token
  3. PC 端轮询二维码状态,修改为已确认
  4. 登陆成功

你知道哪些实现业务解耦的方法?

答:解耦是一种很重要的软件工程原则,它可以提高代码的质量和可复用性,降低系统的耦合度和维护成本。

解耦在日常开发中很常见,如 AOP 可以将需要切入的逻辑(日志、事务、权限)从核心业务中分离出来、IOC 可以将对象的创建和依赖管理交给容器。

可以通过 事件驱动 实现业务之间的解耦,通过事件驱动的实现方式常用的有两种:

  1. 基于发布订阅模式的事件驱动

MQ 就是这样实现解耦,这种方式在解耦的同时,还实现了异步,提高了系统的吞吐量和接口响应速度。

成熟的消息队列的功能一般比较成熟,自带消息持久化、负载均衡、消息高可用。

除此之外 Redis 也有发布订阅功能(pub/sub),但是存在消息丢失、消息堆积等问题,不如专业的消息队列。

  1. 基于观察者模式的事件驱动

常见的基于观察者模式的事件驱动框架有:Spring Event、Guava EventBus 等

Spring Event 和 Guava EventBus 默认是同步的,但也能实现异步,只是功能比较鸡肋。

观察者模式就只有观察者和被观察者,两者是直接进行交互的。

Spring Event 示例:

// 事件发布者
@Component
public class CustomSpringEventPublisher {
    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;
​
    public void publishCustomEvent(final String message) {
        System.out.println("Publishing custom event. ");
        CustomSpringEvent customSpringEvent = new CustomSpringEvent(this, message);
        applicationEventPublisher.publishEvent(customSpringEvent);
    }
}
​
// 事件监听者
@Component
public class CustomSpringEventListener implements ApplicationListener<CustomSpringEvent> {
    @Override
    public void onApplicationEvent(CustomSpringEvent event) {
        System.out.println("Received spring custom event - " + event.getMessage());
    }
}

发布订阅模式和观察者模式对比:

  • 发布订阅模式:发布者和订阅者完全解耦,通过中间件进行消息传递;可以利用中间件(MQ、Redis)来实现分布式的消息传递,可应用于跨应用或跨进程的场景;大多数是异步的;
  • 观察者模式:需要维护观察信息,被观察者和观察者直接交互;基于对象本身的数据变化来通信,不能使用在跨应用或跨进程的场景;大多数是同步的;

接口重试策略如何设计?

常见的重试策略有两种:

  1. 固定间隔时间重试:实现简单、但是可能导致重试过于频繁或稀疏,从而影响系统性能。如果重试间隔太短,可能导致雪崩效应;如果太长,可能影响用户体验
  2. 梯度间隔重试:根据重试次数去延长重试间隔时间。例如第一次重试间隔1s,第二次2s,第三次4s。能有效提高重试的几率,也能通过梯度增加间隔时间来避免对下游系统造成更大压力。此种策略需要设置合理的上下限值,否则可能导致延长时间过长。

重试策略对分布式系统来说是自私的,客户端认为他的消息很重要,并要求服务端花费更多资源来处理,盲目的重试设计不可取。

重试策略最佳实践:

  • 合理设置消费的最大超时时间和次数(尽快向客户端返回成功或失败,不要以超时或者异常抛出来代替消费失败)
  • 重试会导致相同的消息进行重复消费,消费方应该有一个良好的幂等设计

支付系统中补单操作如何完成:https://mp.weixin.qq.com/s/9Z-N3cfWu7oMVJsTDkbb-Q

简单来讲,补单利用 RocketMQ 对操作失败进行补偿操作,但不能一直进行补偿操作,需要设置一个最大重试次数,在多次补偿失败之后,需要延缓补偿频率,这些都通过 RocketMQ 进行实现,这里还存在几个问题:

  1. 如果异常消息发送失败,上游没有重试机制,这笔订单就会卡住,因为系统并不知道需要去补偿
  2. 在补偿消息时失败
  3. 如果重试达到最大次数仍然没有成功,该如何处理?

针对问题1,可以将异常消息落库,存在异常消息表中,记录订单号、当前重试次数、一场分类、记录状态、消息体等字段,设置定时任务去扫描该表进行处理。对当前 MQ 的可用性,异常数据很少出现。

针对问题2,如果补偿失败,会向上抛出 error,利用 RocketMQ 的梯度重试机制,当消费次数上限后会进入死信队列。这种情况一般是网络出现问题,恢复之后,可以从死信队列拉取这些消息再统一处理。如果 MQ 和 DB 都失败了,为极端情况,人工介入即可。

针对问题3,如果达到最大次数仍然没有成功,将他放入异常表。

还可以有一些在业务低峰期的兜底任务,扫描业务表,对未完成的订单进行补偿。兜底任务可能造成信息的短暂堆积,影响线上补偿流程推进,可以使用独立的队列隔离开。

场景题:实时排行榜,几千万的流量!要高可用高并发

答:假如我们要对前100名用户进行实时排行,在数据库中创建一张用户总分表。总分表里会存入用户头像,姓名,总分,用户id。将总分表的前500名放到 Redis 的 zset 集合中。

  • 当用户访问排行榜接口时,会从 Redis 中获取前 100 名用户的信息。
  • 当用户的分数发生变化时,会拿当前用户分数和第100名用户的分数对比,如果大于,则放入Redis中。

排行榜只有100名用户,我们将前500名用户都放入 Redis 有必要吗?有必要,数据冗余一些可以避免频繁的更新数据,也能保证数据的准确性(否则,就需要加全局锁保证数据的准确性)。

可以加一个定时器,隔一段时间从数据库重新取数据,避免时间长了,redis中存储的数据越来越多。

image.png


Map<String, Double> map = new HashMap<>();
for (int i = 0; i < 300000; i++) {
    map.put("userId" + i, Double.valueOf(i));
}
// zadd 批量添加,或者单个添加,或者更新
jedis.zadd("ranking", map);
jedis.zadd("ranking", 10.00, "userA");
User user = new User();
// 单独往hash中添加数据
jedis.hset("user-list", "userA", JSON.toJSONString(user));
Map<String, String> map1 = new HashMap<>();
for (int i = 0; i < 10; i++) {
    map1.put("userId" + (char)('A' + i), JSON.toJSONString(user));
}
// 批量添加
jedis.hset("user-list", map1);
// 设置过期时间
jedis.expire("user-list", 5 * 60);
jedis.expire("ranking", 5 * 60);
// hash获取
jedis.hget("user-list", "userA");
// 查看某个用户排名,zset是按照分数从小到大排列,所以排行榜要使用zrevrank
jedis.zrevrank("ranking","userA")
// 查看前10名,并查出分数
jedis.zrevrangeWithScores("ranking",0,9)

幂等性如何设计?

答:幂等性的设计有以下几种方案:

方案一:唯一索引或唯一组合索引

对订单的幂等性设计,可以使用订单号作为唯一索引,这样如果多次插入的话,就会报错 DuplicatedKeyException, 那么我们就可以捕获该错误,来返回友好提示。

方案二:乐观锁

使用乐观锁会给数据库表增加一个版本号 version字段,查询数据时,读取到 version,当更新数据时判断数据库版本号和自己拿到的版本号是否相同,相同则更改,每次更新操作对 version 字段加 1。

update order set name = #{name}, version=#{version}+1 where id=#{id} and version=#{version}

方案三:Token + Redis

针对调用方重试接口的情况,例如重复提交订单,这种幂等性设计可以使用 Token 机制来防止重复提交。

调用方在调用接口时,先向后端请求一个 Token,该 Token 存储在 Redis 中并设置过期时间,在调用时携带上 Token(放入Header存储),后端在 Redis 中检查该 Token 是否存在,如果存在表示是第一次请求,删除token中的缓存 (使用 lua 脚本,保证操作的原子性) ,如果不存在,表示重复请求,直接返回。

如果第一次调用接口失败了,可以通过设计来重新生成 token,再次尝试调用。


public void invoke(){
  String token = genToken();
  // 提交订单信息
  submitOrder(token, order);
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

重建大师如何调用GPU?

答&#xff1a;会调用GPU的cuda和显存&#xff0c;不同处理的阶段&#xff0c;占用会不一样。 重建大师是一款专为超大规模实景三维数据生产而设计的集群并行处理软件&#xff0c;输入倾斜照片&#xff0c;激光点云&#xff0c;POS信息及像控点&#xff0c;输出高精度彩色网格模…

Dreamweaver2019安装教程从零基础入门到精通(非常详细附安装包)看完这一篇就够了

软件介绍 Dreamweaver简称“DW”&#xff0c;是一款专业的网页设计软件&#xff0c;集网页制作和网站管理于一身的即时检索的网页代码编辑器&#xff0c;利用对 HTML、CSS、JavaScript 等内容的支持&#xff0c;设计人员和开发人员可以在几乎任何地方快速制作并发布网页。借助…

Vue小案例(一)

目录 案例一&#xff1a;点击按钮切换图片 案例二&#xff1a;书架 案例三&#xff1a;成绩表 案例一&#xff1a;点击按钮切换图片 现在有5张图片&#xff0c;我想通过点击上一张 / 下一张的按钮来进行图片切换。 使用&#xff1a;v-bind、v-on、v-show 1、首先创建好Vu…

使用FreeMarker导出word文档(支持循环导出实时多张图片)

续上一期的更新内容 &#xff0c;导出的是单张图片&#xff0c;直接在路径的src 里面填写对应的占位符&#xff0c;就可以了&#xff0c;随着需求的变化&#xff0c;那么今天我们继续往下写一个循环导出多张图片到word里面。 使用FreeMarker导出word文档(支持导出单张图片) …

如何评估大语言模型是否可信?这里总结了七大维度

源自&#xff1a;机器之心发布 作者&#xff1a;刘扬&#xff0c;Kevin Yao 实际部署中&#xff0c;如何 “对齐”&#xff08;alignment&#xff09;大型语言模型&#xff08;LLM&#xff0c;Large Language Model&#xff09;&#xff0c;即让模型行为与人类意图相一致…

荣耀推送服务业务介绍

概述 荣耀推送服务&#xff08;HONOR Push&#xff09;是荣耀公司向开发者提供的消息推送服务&#xff0c;通过服务端与客户端建立一条稳定、可靠的长连接通道&#xff0c;向荣耀手机系统上的APP应用客户端实时推送消息的服务。无论应用进程是否存在&#xff0c;均可正常收到消…

【AIFEM案例教程】水轮机活动导叶强度分析

AIFEM是由天洑自主研发的一款通用的智能结构仿真软件&#xff0c;助力用户解决固体结构相关的静力学、动力学、振动、热力学等实际工程问题&#xff0c;软件提供高效的前后处理工具和高精度的有限元求解器&#xff0c;帮助用户快速、深入地评估结构的力学性能&#xff0c;加速产…

LAS Spark+云原生:数据分析全新解决方案

更多技术交流、求职机会&#xff0c;欢迎关注字节跳动数据平台微信公众号&#xff0c;回复【1】进入官方交流群 随着数据规模的迅速增长和数据处理需求的不断演进&#xff0c;云原生架构和湖仓分析成为了现代数据处理的重要趋势。在这个数字化时代&#xff0c;企业面临着海量数…

leetcode:101.对称二叉树

借用二叉树是否相同的代码改动左右孩子相等对应关系&#xff0c;即为是否对称。 /*** Definition for a binary tree node.* struct TreeNode {* int val;* struct TreeNode *left;* struct TreeNode *right;* };*/bool isSameTree(struct TreeNode* p, struct Tr…

纳米软件科普|什么是运放芯片?运放芯片测试方法是什么?

运放芯片是一种电路元件&#xff0c;它可以用于信号放大、滤波、积分、微分等电路中。在多媒体音箱领域&#xff0c;运放芯片主要负责音量、音调和周边效果调节的运算功能。例如&#xff0c;音响中的前级和耳机放大器(耳放)中都会使用集成运算放大器。常见的运放芯片有OPA1612和…

nvm 安装 node 安装不上 npm

遇到一个问题 nvm install 18.18.2 node -v 安装上了 npm -v 发现没有安装上 解决办法 nvm -v 查看到自己的 nvm 版本号是 1.1.7 NVM下载 - NVM中文网 下载最新版本的 nvm .exe 文件 nvm list 查看手里 node 的所有版本 nvm uninstall 各个版本只保留一个最低版本 点…

中文编程开发语言工具编程案例:计时计费管理系统软件连接灯控器编程案例

中文编程开发语言工具编程案例&#xff1a;计时计费管理系统软件连接灯控器编程案例 中文编程开发语言工具编程案例&#xff1a;计时计费管理系统软件连接灯控器编程案例 中文编程系统化教程&#xff0c;不需英语基础。学习链接 https://edu.csdn.net/course/detail/39036

Messari发布Moonbeam简报,每日交易量稳步增长,首次公布利润数据

区块链数据公司Messari首次发布Moonbeam项目分析简报&#xff0c;从项目市值、链上数据表现、质押以及Moonbeam的技术优势XCM使用量等角度全面分析。这个再熊市初期上线的项目一直在默默开发&#xff0c;并在跨链互操作领域拥有了相当的实操成绩。我们翻译了Messari简报中的部分…

分类预测 | Matlab实现WOA-BiLSTM鲸鱼算法优化双向长短期记忆神经网络的数据多输入分类预测

分类预测 | Matlab实现WOA-BiLSTM鲸鱼算法优化双向长短期记忆神经网络的数据多输入分类预测 目录 分类预测 | Matlab实现WOA-BiLSTM鲸鱼算法优化双向长短期记忆神经网络的数据多输入分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 1.Matlab实现WOA-BiLSTM鲸鱼算法…

AirPods Max奇特的关机模式!如何关闭AirPods Max

对于一家专注于让事情变得更简单的公司来说&#xff0c;苹果已经把关闭最高端耳机这样的基本问题过于复杂了。耳机没有电源按钮。别担心&#xff1b;如果你想弄清楚情况&#xff0c;你并不孤单。今天&#xff0c;我们将教你如何关闭AirPods Max。 如何关闭AirPods Max AirPods…

万界星空科技离散型制造企业MES解决方案

MES即制造企业生产过程执行系统,面向制造企业车间执行层的生产信息化管理系统。是精益生产、数字化工厂、智慧工厂的支撑平台。帮助企业对生产全流程进行管控并进行信息追溯,实现柔性化生产、精细化管理。 一、企业生产场景总览: 二、MES系统架构图: 帮助企业实现制造执行自动…

QT的QStringList的使用

初始 化 默认构造函数创建一个空列表。可以使用初始值设定项列表构造函数创建包含元素的列表&#xff1a; QStringList fonts { "Arial", "Helvetica", "Times" }; 添加字符串 可以使用insert 、append&#xff08;&#xff09; 和 operator…

CH7-任务管理

7.1 任务管理概述&#xff08;TASK MANAGEMENT OVERVIEW&#xff09; 什么是任务&#xff1f; 任务是操作系统中的一个基本执行单位&#xff0c;可以是一个进程或者一个线程&#xff0c;代表了系统中的一个活动。 80x86 提供了哪些硬件支持&#xff1f; 80x86 提供了任务状态段…

解决IDEA中SpringBoot项目创建多个子模块时配置文件小绿叶图标异常问题

在新建子模块下创建配置文件&#xff1a; 在子模块gateway中新建的配置文件,正常情况下配置文件左侧是小树叶标识&#xff0c;而这次新建application-dev.yml是个小树叶标识&#xff0c;bootstrap.yml是个方框。 看其他方案都是在project structure中设置&#xff0c;但未显示…

HTML 表单笔记/练习

表单 概述 表单用于收集用户信息&#xff0c;用户填写表单提交到服务器 一般传参方式&#xff1a; GETPOSTCookie 传参要素 传参方式 GETPOST 参数的名字目标页面内容的数据类型&#xff08;只有在上传文件的时候&#xff09; 提示信息 一个表单中通常还包含一些说明性的文…