Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >2 道直播系统设计题,建议收藏

2 道直播系统设计题,建议收藏

原创
作者头像
JavaSouth南哥
发布于 2024-12-19 10:08:28
发布于 2024-12-19 10:08:28
1990
举报
文章被收录于专栏:JavaSouth系列JavaSouth系列

先赞后看,Java进阶一大半

geeksforgeeks 站点给出了一张如何设计 ESPN 直播视频流系统的架构图。

各位hao,我是南哥,相信对你通关面试、拿下Offer有所帮助。

1. 直播礼物系统设计

1.1 表结构设计

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

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

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

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

(1)礼物表

代码语言:sql
AI代码解释
复制
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)用户礼物库存表

代码语言:sql
AI代码解释
复制
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)礼物消费记录表

代码语言:sql
AI代码解释
复制
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端先调用校验接口,校验用户的余额是否充足。这一点很重要,余额不够的则不走下面的流程,减少了大量无效的送礼请求。

代码语言:java
AI代码解释
复制
// 校验接口
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任务的后端服务会处理送礼请求,完成礼物校验、资产校验后,进行实际的用户扣费。

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

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

代码语言:java
AI代码解释
复制
// 异步处理
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

代码语言:shell
AI代码解释
复制
# 用户、直播间是一对多关系的数据结构
live_room_users:room_id : [user_id, user_id]
代码语言:shell
AI代码解释
复制
# 例如
live_room_users:000 : [001, 002, 003]
live_room_users:111 : [004, 005, 005]

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

代码语言:java
AI代码解释
复制
// 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 送礼连击功能

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

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

代码语言:java
AI代码解释
复制
// 批量送礼接口
public void batchSendGift(int userId, int giftId, int totalQuantity, int roomId, int anchorId) {
    // 客户端统计点击次数,作为一次送礼请求
    processGiftAsync(userId, giftId, totalQuantity, anchorId);
}

1.5 事务控制

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

代码语言:java
AI代码解释
复制
// 事务控制
@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);
    }
}

2. 直播弹幕设计

2.1 底层数据结构支持

南友们看看右下角的弹幕列表,这个弹幕列表就是我们要的攻克的对象,至于中间视频直播的走马灯弹幕,它其实也是根据弹幕列表的数据来进行滚动。

南哥观察了下这个直播间,现在有41.5万人在观看!

计算机世界实际上是现实世界的抽象,那弹幕列表我们要用什么数据结构支持。

我希望用的是Redis,Redis官方写着这么霸气的宣传语。

Get the world’s fastest in-memory database from the ones who built it

从构建者那里获取世界上最快的内存数据库

而底层数据结构我们使用Redis五大基本数据类型之一:Zset。Zset是一种有序集合类型,它有一个score值,score值用来存储用户发送弹幕的时间戳,那整个列表就会根据时间戳来进行排序。

而Zset元素的值就作为用户弹幕,例如上图的:剧本演了又演

有了底层数据结构支持,我们来说说这个弹幕列表有什么功能限制。大家有没注意到我们进入直播间,直播间是不会把所有的弹幕内容都

显示出来的,往往只是显示前10条。

那我们也给弹幕列表加上这个特性,在Redis的Zset结构设置只保留前10条的属性。

代码语言:java
AI代码解释
复制
// 创建Zset数据结构并设置10条的限制
public class DanmakuService {

    private Jedis jedis = new Jedis("localhost");

    public void addDanmaku(String roomId, String danmaku, long timestamp) {
        String key = "room:" + roomId + ":danmaku";
        jedis.zadd(key, timestamp, danmaku);
        // 只保留最新的10条弹幕
        jedis.zremrangeByRank(key, 0, -(11));
    }
}

2.2 弹幕列表查询

那用户进入直播间,弹幕列表是怎么查询出来的?

我们按最简单高效来,用户进入直播间,客户端调用API接口去查询出Redis里的弹幕列表。

有南友会问:这只是最近的前10条聊天记录,后面的呢?

别急,有两种方案。

(1)轮询查询

客户端轮询查询API接口,不断抓取出用户发出的新弹幕。具体细节的话,客户端第一次查询出的弹幕列表的数据结构是:[(时间戳1: 弹幕1), (时间戳2: 弹幕2)]

后续查询客户端继续轮询调用API接口,同时携带当前弹幕列表的最大时间戳入参。而后端服务就会根据该时间戳返回比该时间戳大的数据,用户发送的新的弹幕也就会显示出来。

代码语言:java
AI代码解释
复制
// 轮询API接口
public class DanmakuService {

    private Jedis jedis = new Jedis("localhost");

    public Set<String> getRecentDanmaku(String roomId, long lastTimestamp) {
        String key = "room:" + roomId + ":danmaku";
        return jedis.zrangeByScore(key, lastTimestamp + 1, Long.MAX_VALUE);
    }
}

轮询API我们要设置多少时间轮询一次呢?我们先设置 3 秒轮询更新一次弹幕列表,后续再根据用户反馈、服务器资源进行优化调整。

(2)WebSocket技术

视频直播间有个特点,主播和观众是无时不刻在进行互动聊天的,这就要求音视频要实时同步了。那直播弹幕就更应该实时,使用第一种轮询API的方法,可能会有 3 秒延迟的情况发生。

要实时推送新的弹幕,我们可以使用WebSocket技术,客户端和WebSocket服务器保存长连接,用户只要发送新的弹幕消息,WebSocket服务器便会实时推送到客户端上。

第一种方法虽然粗暴,如果轮询查询会空,那本次查询就是一次资源浪费,对服务器资源不友好。但他简单高效,出错情况也少。

如果老板要你半个月上线这个弹幕列表功能,那第一种方法也未尝不可。后续我们再来根据实际情况作出升级调整的策略,例如升级为WebSocket技术。

2.3 系统流程

南哥画下整个系统的流程。

用户通过客户端发送弹幕,通过后端服务把弹幕消息发送到Kafka。使用Kafka消息我们就可以进行流量削峰,弹幕消息有时是成千上万 / 秒,把存储弹幕消息的任务通过消息队列缓存起来,一个个执行,减少服务器瞬时的高压力。

发送到Kafka后,负责监听弹幕Kafka消息的后端服务会把弹幕消息写入Redis。

代码语言:java
AI代码解释
复制
// 监听写入Redis
public class DanmakuListener {

    private Jedis jedis = new Jedis("localhost");

    @KafkaListener(topics = "danmaku", groupId = "group_id")
    public void listen(ConsumerRecord<String, String> record) {
        String roomId = record.key();
        String message = record.value();
        long timestamp = System.currentTimeMillis();
        
        String key = "room:" + roomId + ":danmaku";
        // 将弹幕消息写入Redis Zset
        jedis.zadd(key, timestamp, message);
        // 保留最近10条
        jedis.zremrangeByRank(key, 0, -11);
    }
}

2.4 消息丢失问题

有这么一种情况,用户A发送了(1726406132, 弹幕A),用户B发送了(1726406150, 弹幕B),用户A先发送了弹幕,用户B再发送弹幕。

如果用户B的弹幕先写入到了Redis的Zset列表,其他用户进入直播间查询了第一个弹幕列表。那即使用户A后面成功写入了弹幕,其他用户也不会获取到用户A的弹幕。

因为客户端只会更新比弹幕B时间戳更大的弹幕消息。这怎么处理?

我们要解决的是弹幕B先于弹幕A成功写入的问题,不考虑其他特殊情况,可以给写入Redis的方法加上分布式锁的功能,保证先获取锁的弹幕消息写入的过程中,不会有其他弹幕消息写入的干扰。

代码语言:java
AI代码解释
复制
// 在写入弹幕时获取分布式锁
public class DanmakuService {

    private RedisLockUtil redisLock = new RedisLockUtil();
    
    private Jedis jedis = new Jedis("localhost");

    public void addDanmakuWithLock(String roomId, String danmaku, long timestamp) {
        String lockKey = "lock:" + roomId;
        try {
            if (redisLock.acquireLock(lockKey)) {
                String key = "room:" + roomId + ":danmaku";
                jedis.zadd(key, timestamp, danmaku);
                jedis.zremrangeByRank(key, 0, -11);
            }
        } finally {
            redisLock.releaseLock(lockKey);
        }
    }
}
代码语言:java
AI代码解释
复制
public class RedisLockUtil {

    private Jedis jedis = new Jedis("localhost");
    
    private static final int EXPIRE_TIME = 5000; // 5秒

    // 获取锁
    public boolean acquireLock(String lockKey) {
        long currentTime = System.currentTimeMillis();
        String result = jedis.set(lockKey, String.valueOf(currentTime), "NX", "PX", EXPIRE_TIME);
        return "OK".equals(result);
    }

    // 释放锁
    public void releaseLock(String lockKey) {
        jedis.del(lockKey);
    }
}

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

看了就赞,Java进阶一大半。点赞 | 收藏 | 关注,各位的支持就是我创作的最大动力❤️

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
设计了简单高效的弹幕系统!老板直接加薪
弹幕系统最早起源于日本,流行于视频网站niconico。我们认识的初音未来(Hatsune Miku)就是在niconico平台上爆红的!!
JavaSouth南哥
2024/09/18
2960
设计了简单高效的弹幕系统!老板直接加薪
🔥产品:直播送礼延迟这么大,你就不能快点吗
其实抖音的实时音视频技术RTC,是来源于火山引擎RTC的支持,抖音、火山引擎、巨量引擎都属于字节旗下不同的业务板块。
JavaSouth南哥
2024/09/19
1800
🔥产品:直播送礼延迟这么大,你就不能快点吗
B站直播弹幕ws协议分析
Chrome DevTools可以直接查看binary类型数据包 连接 通过Chrome DevTools可以看到,网页版B站直播的弹幕通过 wss://tx-sh3-live-comet-04.ch
戴兜
2023/02/17
2.9K0
B站直播弹幕ws协议分析
面试官:如何设计一个排队系统、pk系统
排队的一个特点是一个元素排在另一个元素的后面,形成条状的队列。List结构、LinkedList链表结构都可以满足排队的业务需求,但如果这是一道算法题,我们要考虑的是性能因素。
JavaSouth南哥
2024/12/28
1991
面试官:如何设计一个排队系统、pk系统
体育直播系统开发:原生Android客户端Java架构设计与功能模块划分
该体育赛事直播系统由“东莞梦幻网络科技”结合原生 Android 客户端(Java开发),本文将从整体架构设计、模块功能划分到技术选型,系统性讲解体育直播系统 Android 端的开发思路与实现细节,力求为开发者、创业者及相关从业者提供清晰、实用的参考。
用户10027718
2025/04/28
910
玩转直播系列之消息模块演进(3)
即时消息(IM)系统是直播系统重要的组成部分,一个稳定的,有容错的,灵活的,支持高并发的消息模块是影响直播系统用户体验的重要因素。IM长连接服务在直播系统有发挥着举足轻重的作用。
2020labs小助手
2021/05/24
5670
项目没亮点?那就来学下pk功能设计吧
麻省理工学院开源的Redis adapter适配器,可以将事件广播到多个单独的 socket.io 服务器节点。这一点和下文精彩的内容相关。
JavaSouth南哥
2024/09/29
1020
项目没亮点?那就来学下pk功能设计吧
模仿UP主,用Python实现一个弹幕控制的直播间!
up主通过代码实现了实时读取直播间里的弹幕内容,进而控制自己的电脑,把弹幕翻译成指令操控《赛博朋克2077》游戏。
蛮三刀酱
2021/12/02
2K0
模仿UP主,用Python实现一个弹幕控制的直播间!
玩转直播系列之从 0 到 1 构建简单直播系统(1)
随着5G时代的到来,音视频行业也可能迎来一个行业的春天,直播则是新视频行业一直以来的一个重要的产品形态,从最初的秀场直播,游戏直播,到今年由于疫情,目前比较火的在线教育直播,带货直播等,各类新的直播形式则是越来越多的展示在大众面前。
2020labs小助手
2021/05/07
1.6K0
捣鼓一个电商功能设计
谷歌系统设计面试有一道题是关于如何设计秒杀架构,国外一位老哥给出了5种方法,下图是其中一种。
JavaSouth南哥
2024/10/16
1730
捣鼓一个电商功能设计
这个排队系统设计碉堡了
关于系统设计的问题,大家还是要多多思考,可能这道题考的不是针对架构师的职位,而是关于你的业务设计能力。如果单单只会用开源软件的API,那似乎我们的竞争力还可以再强些。学习设计东西、创作东西,把我们设计的产品给别人用,那竞争力一下子提了上来。
JavaSouth南哥
2024/11/13
1510
这个排队系统设计碉堡了
更新弹幕系统的心得体会
16年开始很多互联网公司都开始在涉足直播,直播业务中有弹幕的系统。今天就要给大家介绍一下弹幕系统优化的新的体会。随着直播业务的变化与发展,我司弹幕系统从最初的版本优化了好几个版本,这里面有我的血泪。
云计算爱好者
2018/01/16
1.2K0
更新弹幕系统的心得体会
Bilibili直播信息流:连接方法与数据解析
如今,市面上已经有不少开源项目可以用于连接 B 站直播 WebSocket 获取信息流。
苏琢玉
2025/03/12
1040
Bilibili直播信息流:连接方法与数据解析
视频直播:基于流计算 Oceanus(Flink) 的实时大屏分析
本方案结合腾讯云 CKafka、流计算 Oceanus、私有网络 VPC、商业智能分析BI等,对视频直播行业数字化运营进行实时可视化分析。分析指标包含观看直播人员的地区分布、各级别会员统计、各模块打赏礼物情况、在线人数等。
吴云涛
2021/08/17
2.7K0
视频直播:基于流计算 Oceanus(Flink) 的实时大屏分析
假如让你从0到1实现一个直播弹幕系统
直播业务现在特别火爆,也给人们的互动方式带来了很多新的改变,比如刷礼物、弹幕、排行榜等等。面对巨大的流量规模,直播技术的发展也备受关注。作为一个技术爱好者,相信你也会对直播的技术比较感兴趣,于是我去翻了几篇文章,了解了直播的技术方案,发现涉及到的技术细节太多,有部分已经是知识盲区,如音频、视频的编码传输等。
用户5397975
2020/06/16
6K0
假如让你从0到1实现一个直播弹幕系统
MLVBLiveRoom 方案 - 管理后台RoomService接口文档
本文用于介绍移动直播 MLVBLiveRoom 方案的管理后台部分,MLVBLiveRoom 方案包含了两部分内容:客户端 MLVBLiveRoom 组件 + 房间管理服务 RoomService。MLVBLiveRoom 组件说明见 https://cloud.tencent.com/developer/article/1488540
腾讯云-chaoli
2019/08/16
21.5K31
MLVBLiveRoom 方案 - 管理后台RoomService接口文档
如何使用Redis搭建自己的视频直播间信息系统
视频直播间作为直播系统对外的表现形式,在整个系统中处于核心地位。通常除了视频直播窗口外,直播间还包含在线用户,礼物,评论,点赞,排行榜等信息。直播间消息,时效性高,互动性强,对系统时延有着非常高的要求,非常适合使用Redis等缓存服务来处理。
Tinywan
2023/11/05
3250
如何使用Redis搭建自己的视频直播间信息系统
消息未读之点不完的小红点(Node+Websocket)
这个项目本来是我学生时代为了找工作的一个练手项目,但是没想到受到了很多的关注,star也快要破K了,这也激励着我不断去完善他,一方面是得对得起关注学习的人,另一方面也是想让自己能过通过慢慢完善一个项目来让自己提高。
秋风的笔记
2020/10/27
2.3K0
消息未读之点不完的小红点(Node+Websocket)
支撑百万用户同时在线的高并发直播弹幕系统是如何炼成的?
51CTO技术栈
2017/11/14
3.9K0
支撑百万用户同时在线的高并发直播弹幕系统是如何炼成的?
接入bilibili直播开放平台
    在前几天,无意间看到了b站直播互动平台开放了开发者接入的功能,所以继接入qq和baidu登录授权功能后决定研究一下b站的直播互动平台接入有哪些不同。对于这篇文章酝酿了好些天,因为实在是有些不大好下手,不知道怎样写才能讲的更加清晰、易懂。
时光潜流
2022/12/26
5.4K0
接入bilibili直播开放平台
推荐阅读
相关推荐
设计了简单高效的弹幕系统!老板直接加薪
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档