产品:直播送礼延迟这么大,你就不能快点吗

先赞后看,南哥助你Java进阶一大半

其实抖音的实时音视频技术RTC,是来源于火山引擎RTC的支持,抖音、火山引擎、巨量引擎都属于字节旗下不同的业务板块。

在这里插入图片描述

我是南哥,一个Java学习与进阶的领路人。

相信对你通关面试、拿下Offer进入心心念念的公司有所帮助。

⭐⭐⭐本文收录在《Java学习/进阶/面试指南》:https://github/JavaSouth

1. 直播礼物系统设计

1.1 表结构设计

视频直播领域的企业,比如抖音、快手、虎牙直播、B站直播,企业赚钱的源头往往靠的是粉丝在直播间刷礼物。你是不是像南哥一样只刷免费的小心心呢?我看了下抖音的直播间,现在小心心还要充钱才能送!

在这里插入图片描述

赚钱的业务必须要重视起来,这必然不是一个小小模块,而是一个礼物系统设计。

特别用户送礼有个必要的用户需求,用户送礼是为了和主播互动,送了个嘉年华,主播半小时才反应过来,那我们直播平台得被用户喷si。这就要求直播送礼的实时性了,虽然送礼内部包含了众多逻辑,看起来不可能快。

先看看下礼物系统的表设计。

(1)礼物表

CREATE TABLE `gifts` (`gift_id` INT AUTO_INCREMENT PRIMARY KEY,`gift_name` VARCHAR(255) NOT NULL,`cost` INT NOT NULL,`image_url` VARCHAR(255)
);

(2)用户礼物库存表

CREATE TABLE `user_gifts` (`user_gift_id` INT AUTO_INCREMENT PRIMARY KEY,`user_id` INT NOT NULL,`gift_id` INT NOT NULL,`quantity` INT DEFAULT 1,`acquired_date` TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '获得礼物日期'
);

(3)礼物消费记录表

CREATE TABLE `gift_consumption_records` (`record_id` INT AUTO_INCREMENT PRIMARY KEY,`user_id` INT NOT NULL,`gift_id` INT NOT NULL,`anchor_id` INT NOT NULL COMMENT '主播id',`quantity` INT NOT NULL,`consumed_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

1.2 送礼流程设计

简单来看,一次送礼请求需要经过的步骤可以简化为:

用户送礼 -> 礼物校验、资产校验 -> 用户扣费 -> 直播间礼物通知 -> 更新礼物排行榜、记录消费日志。

上文我有说了送礼要快/准/恨,这么长的业务链条,实时性要怎么保障?

(1)校验接口

用户点击送礼,App端先调用校验接口,校验用户的余额是否充足。这一点很重要,余额不够的则不走下面的流程,减少了大量无效的送礼请求。

// 校验接口
public boolean validateGiftAndBalance(int userId, int giftId, int quantity) {// 查询用户余额int userBalance = getUserBalance(userId);// 查询礼物价格int giftCost = getGiftCost(giftId);// 校验用户余额是否充足if (userBalance < giftCost * quantity) {return false;}return true;
}

(2)消息队列

如果余额校验成功,App端将送礼请求发送到后端服务,后端服务把所有送礼请求都统一转发到消息队列Kafka上,同时返回成功给客户端,但客户端仍然不进行礼物展示。

通过消息队列把送礼请求任务化,大大减少了送礼高峰对服务器资源的冲击。而用户送礼成功后的直播间礼物显示留在下一步中。

(3)异步处理

监听Kafka任务的后端服务会处理送礼请求,完成礼物校验、资产校验后,进行实际的用户扣费。

当扣费成功后,后续的流程还有:直播间礼物通知 -> 更新礼物排行榜、记录消费日志,甚至更多杂七杂八新增的业务逻辑。

但要保障实时性,扣费成功后的后续步骤完全可以异步化,异步进行直播间礼物通知、异步更新礼物排行榜、记录消费日志。

// 异步处理
public void processGiftAsync(int userId, int giftId, int quantity, int anchorId) {CompletableFuture.runAsync(() -> {// 直播间礼物通知notifyLiveRoom(userId, giftId, quantity, anchorId);// 更新礼物排行榜updateGiftRanking(userId, giftId, quantity);// 记录消费日志recordGiftConsumption(userId, giftId, quantity, anchorId);});
}

(4)多实例负载均衡

保证处理送礼请求的后端服务资源充足,根据实际送礼流量增加消费实例进行负载均衡。

1.3 直播间礼物通知

欸,用消息队列处理送礼请求,前面在送礼请求接口都返回成功给客户端了,直播间礼物还没有显示出来那什么时候才显示出来?

这里我们用到的技术是服务器主动推送技术,例如现如今很火的WebSocket实时推送。WebSocket的创始人叫Michael Carter,听说现在每天全球有超过 20 亿台设备在使用WebSocket。

Michael designed the initial WebSocket protocol for HTML5, a technology that is used on more than 2 billion devices across the world every day.

推送直播间礼物显示前,我们得先知道推送给谁,直播间所有用户、主播、送礼的粉丝都是推送的对象。

这些在直播间的用户和直播间是一对多的关系,不可能把这个关系存储到MySQL数据库,毕竟我们要快。业界一般把它存储在内存数据库:Redis。

# 用户、直播间是一对多关系的数据结构
live_room_users:room_id : [user_id, user_id]
# 例如
live_room_users:000 : [001, 002, 003]
live_room_users:111 : [004, 005, 005]

知道了推送对象,我们就可以异步进行推送通知。

// WebSocket通知房间里所有用户
public void notifyLiveRoom(int userId, int giftId, int quantity, int roomId) {// 获取房间中所有用户Set<Integer> users = getUsersInLiveRoom(roomId);String message = String.format("User %d sent %d of gift %d", userId, quantity, giftId);// 推送消息给所有用户for (Integer user : users) {webSocketService.sendMessageToUser(user, message);}
}

1.4 送礼连击功能

用户在直播间送礼往往有一个习惯,第一节提到的免费小心心礼物,用户会疯狂连击。一次送礼点击就作为一次送礼请求,很明显对我们的服务器资源很不友好

在客户端设置一个时间窗口,只要用户在时间段内连续点击送礼按钮,客户端统计出点击次数,作为一次送礼请求。

在这里插入图片描述

// 批量送礼接口
public void batchSendGift(int userId, int giftId, int totalQuantity, int roomId, int anchorId) {// 客户端统计点击次数,作为一次送礼请求processGiftAsync(userId, giftId, totalQuantity, anchorId);
}

1.5 事务控制

礼物校验、资产校验、用户扣费,这些涉及资金的业务最好加上严格的事务控制,只要有一丁点出错,所有的操作进行回滚。

// 事务控制
@Transactional
public boolean processGiftTransaction(int userId, int giftId, int quantity, int anchorId) {try {// 礼物校验、资产校验if (!validateGiftAndBalance(userId, giftId, quantity)) {return false;}// 扣费deductUserBalance(userId, giftId, quantity);// 其他异步业务逻辑processGiftAsync(userId, giftId, quantity, anchorId)return true;} catch (Exception e) {throw new RuntimeException("Gift processing failed, transaction rolled back", e);}
}

戳这,《JavaSouth》作为一份涵盖Java程序员所需掌握核心知识、面试重点的神秘文档。

我是南哥,南就南在Get到你的点赞点赞。

在这里插入图片描述

创作不易,不妨点赞、收藏、关注支持一下,各位的支持就是我创作的最大动力❤️

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

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

相关文章

俄罗斯电商Ozon实用运营工具推荐

想要在俄罗斯最大的跨境电商平台 Ozon 上大展拳脚&#xff0c;却对俄语感到无从下手&#xff1f;又或是担心难以把握俄罗斯市场的热点趋势&#xff1f;别担心&#xff01;在这篇文章中&#xff0c;我们将为你介绍一系列实用工具&#xff0c;涵盖翻译、运营和图片处理等方面&…

有源滤波器UAF42

有源滤波器模块&#xff0c;在电路板上同时实现了低通&#xff0c;高通&#xff0c;带通 滤波器&#xff0c;可选其一进行输出&#xff0c;并可通过改变滑变阻值&#xff0c;轻松调节其滤波器中心频率&#xff0c;Q值&#xff0c;通带增益等&#xff0c; 也可方便实现Butterwo…

深度学习基础案例5--VGG16人脸识别(体验学习的痛苦与乐趣)

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 前言 这次目标本来要达到60%&#xff0c;但是却非常稳定的达到了40%&#xff0c;​&#x1f622;​​&#x1f622;​​&#x1f622;​​&#x1f622;​&am…

实战17-NavBar+Vip布局

NavBar.ets import { PADDING } from ../../constants/size import rvp from ../../utils/resposive/rvIndexComponent export default struct NavBar {StorageProp(topHeight) topHeight: number 0;build() {Row() {Row({ space: rvp(6) }) {Text(请选择地址).fontSize(rvp(1…

Java-测试-Mockito 入门篇

之前很长一段时间我都认为测试就是使用SpringBootTest类似下面的写法&#xff1a; SpringBootTest class SysAuthServiceTest {AutowiredSysRoleAuthMapper sysRoleAuthMapper;Testpublic void test() {QueryWrapper<SysRoleAuth> queryWrapper new QueryWrapper<&g…

Web开发:Thymeleaf模板引擎

1. Thymeleaf 简介 Thymeleaf 是一个现代的服务器端模板引擎&#xff0c;用于生成 HTML、XML、JavaScript 和 CSS。它的设计理念是使模板能够自然地在 Web 浏览器中呈现&#xff0c;同时允许动态生成内容。 2. 最佳实践总结 2.1 项目结构和模板组织 保持清晰的目录结构&…

Electron-vue asar 局部打包优化处理方案——绕开每次npm run build 超级慢的打包问题

背景 因为组员对于 Electron 打包过程存在比较迷糊的状态&#xff0c;且自己也没主动探索 Electron-vue 打包细节&#xff0c;导致每次打包过程都消耗 5-6 分钟的时间&#xff0c;在需要测试生产打包时&#xff0c;极其浪费时间&#xff0c;为此针对 Electron-vue 打包的几个环…

C++ —— 关于vector

目录 链接 1. vector的定义 2. vector的构造 3. vector 的遍历 4. vector 的扩容机制 5. vector 的空间接口 5.1 resize 接口 5.2 push_back 5.3 insert 5.4 erase 5.5 流插入与流提取 vector 并不支持流插入与流提取&#xff0c;但是可以自己设计&#xff0c;更…

MSF的使用学习

一、更新MSF apt update # 更新安装包信息&#xff1b;只检查&#xff0c;不更新&#xff08;已安装的软件包是否有可用的更新&#xff0c;给出汇总报告&#xff09; apt upgrade # 更新已安装的软件包&#xff0c;不删除旧包&#xff1b; apt full-upgrade # 升级包&#x…

深度学习-18-深入理解BERT实战使用预训练的DistilBERT模型

文章目录 1 预训练的BERT模型2.1 单词级的嵌入表示2.2 句子级的嵌入表示2.3 从最顶层编码器层中抽取嵌入表示2.3.1 预处理输入2.3.2 获得嵌入表示2.4 从所有的编码器层中抽取嵌入表示2.4.1 预处理输入2.4.2 嵌入表示3 为下游任务微调BERT3.1 文本分类3.1.1 原理(微调BERT模型)3…

MTK芯片机型的“工程固件” 红米note9 5G版资源预览 写入以及改写参数相关步骤解析

小米机型:小米5 小米5x 米6 米6x 米8 米9 米10系列 米11系列 米12系列 mix mix2 mix2s mix3 max max2 max3 note3 8se 9se cc9系列 米play 平板系列等分享 红米机型:红米note4 红米note4x 红米note5 红米note6 红米note7 红米note8 红米note8pro 红米s2 红米note7pro 红米…

大数据概念与价值

文章目录 引言大数据的概念高德纳咨询公司的定义麦肯锡全球研究所的定义什么是大数据&#xff1f; 大数据的特征Volume&#xff08;体积&#xff09;Variety&#xff08;种类&#xff09;Velocity&#xff08;速度&#xff09;Value&#xff08;价值&#xff09;Veracity&#…

OpenCV 1

前言&#xff1a;开新坑辽&#xff0c;&#xff0c; 目录 计算机眼中的图像 视频的读取与处理 ROI区域 边界填充 数值计算 腐蚀操作 膨胀操作 开运算与闭运算 梯度计算 礼貌与黑帽 Sobel算子 梯度计算方法 scharr与laplacian 计算机眼中的图像 灰色图片&#xff0…

微服务——网关路由(Spring Cloud Gateway)

网关路由 1.什么是网关 网关又称网间连接器、协议转换器&#xff0c;是在网络层以上实现网络互连的复杂设备&#xff0c;主要用于两个高层协议不同的网络之间的互连。网关就是网络的关口。数据在网络间传输&#xff0c;从一个网络传输到另一网络时就需要经过网关来做数据的路由…

MYSQL登录失败,确保密码正确,常见问题

今天登录MYSQL时&#xff0c;发现登录不进去,我能确保密码没有错误&#xff0c;并且我昨天以这样的方式登录成功&#xff0c;我已经重启过mysql服务&#xff0c;但是依旧登录不进去。 C:\Users\user>mysql -u root -p Enter password: ****** ERROR 1045 (28000): Access …

(已解决)vscode如何选择python解释器

文章目录 前言解决方案 前言 有的时候可能有不同版本的编译器&#xff0c;以适用不同年份的项目。所以&#xff0c;怎么在vscode中换python解释器呢&#xff1f; 解决方案 对着要运行的python文件进行右键&#xff0c;比如我是要运行main文件&#xff0c;点击那个命令选项版…

为什么7kw交流充电桩主板是充电桩运行的关键

7kw交流充电桩主板是电动汽车充电站中的一个核心组件&#xff0c;负责管理和控制充电过程。它是一种专门为7kw功率设计的交流充电设备的控制中枢&#xff0c;包含了电力电子、微处理器、通信模块等多种元件&#xff0c;以确保安全、高效地为电动汽车提供电能。 7kw与3.5kw主板的…

音视频入门基础:AAC专题(10)——FFmpeg源码中计算AAC裸流每个packet的pts、dts、pts_time、dts_time的实现

音视频入门基础&#xff1a;AAC专题系列文章&#xff1a; 音视频入门基础&#xff1a;AAC专题&#xff08;1&#xff09;——AAC官方文档下载 音视频入门基础&#xff1a;AAC专题&#xff08;2&#xff09;——使用FFmpeg命令生成AAC裸流文件 音视频入门基础&#xff1a;AAC…

【CSS in Depth 2 精译_034】5.4 Grid 网格布局的显示网格与隐式网格(下)

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第一章 层叠、优先级与继承&#xff08;已完结&#xff09; 1.1 层叠1.2 继承1.3 特殊值1.4 简写属性1.5 CSS 渐进式增强技术1.6 本章小结 第二章 相对单位&#xff08;已完结&#xff09; 2.1 相对…

在 React 中模拟输入

需求 与 Bug 项目的 C# 桌面端使用 CefSharp 内嵌了一个三方网站&#xff0c;在外部实现了一个登录控件&#xff0c;外部登录后希望内嵌的三方网站自动登录&#xff0c;实现代码如下&#xff1a; browser.ExecuteScriptAsync($"document.getElementsByName(username)[0]…