直播弹幕系统设计

本文仅提供思路参考,并非完备的详细设计。

特点

其实很类似IM即时通讯系统,是个变种,本质也是在一个空间内收发消息

  • 消息及时性强,过期消息意义不大
  • 用户松散,随时来随时走
  • 可能有瞬时大批量弹幕(比如比赛精彩部分)
  • 流量特点:读多写少

弹幕数据结构

type Bullet struct {UserId    int // 用户IDContent   string // 内容Timestamp int // 弹幕发送时间Extra     *Extra // 效果、样式
}

视频弹幕才有偏移时间,直播应该不用(因果一致性)

MVP版本

整体设计

读写分离架构,短连接+拉模式

  • 写:若不考虑历史弹幕可回放,可以直接使用 Redis 作为唯一存储
  • 读:Redis 主要用于读缓存,缓存直播间最新的弹幕数据

在这里插入图片描述

存储

数据结构选择Redis的ZSet

  • 需要弹幕按时间排序,而ZSet可保证有序
  • 此外,score允许重复,那我们就用timestamp来做score

使用

  • 发弹幕:ZADD 直播间ID, 弹幕val, timestamp(优化:时间只存相对于某个时间点的delta)
  • 拉取弹幕:ZRangeByScore 定时轮询(秒级,准实时即可)

问题

  1. 弹幕怎么持久化(Redis扩容成本高)
  2. 热门直播会有大量瞬时弹幕,挑战Redis并发瓶颈
  3. Redis重复请求多,相同直播间会存在很多重复的轮询请求

问题cover

  1. 持久化

    1. 若考虑支持弹幕的回放,数据还是需要持久化,可以考虑使用 MySQL

      1. 弹幕在直播结束后迁移至 DB(可以专门拿一台从redis做回放)
      2. 异步刷入(开线程/监听redis日志刷/发MQ消费)
    2. 如果有更高性能的写需求,可以考虑NoSQL DB,如:HBase、OpenTSDB 等

    3. 新问题:Write-Behind 这种以cache为主的模式,是可能会丢数据的

      1. DB挂了,少写数据,但是只要数据还在redis能追回来
      2. redis挂了,服务感知到后降级,请求回源,“缓存击穿”
  2. 使用MQ来消化瞬时弹幕,削峰;弹幕返回条数根据直播间的大小自动调整(热门直播间对时间跨度以及消息条数做更严格的限制)

  3. 对弹幕读请求,使用local cache缓存最近5s的数据在应用服务的内存中(过期了才回源redis)

    1. 新问题:如果直播间变多,本地内存使用量随直播间线性膨胀,本地cache的命中率下降,还可能频繁触发GC

    2. 解决:(1)只针对热门直播间使用本地cache使用“一致性hash”,控制同一直播间尽可能打到同一台服务器,降低本地cache使用量

优化:热门直播间

  • 需要对每个直播间进行指标采样

    • 标准:粉丝数、在线粉丝数、是否有活动
    • 弹幕系统需要与直播间系统隔离
    • 请求时带上直播间的“热门”标识
  • 根据服务器机器资源来分配所承载的热门直播间

长连接轮询

此前短连接的方式,每次轮询请求后都要重新建立连接。如果使用长连接轮询,客户端保持连接处于打开状态,就不会一直重新建立链接,减少重复的资源消耗,缩短响应时间。

不过,长连接轮询还是有一些缺点的:

  • 发送者和接收者可能并没有连接到同一个服务器

    • HTTP协议的服务器通常是无状态的,如果使用Round Robin的方式来做负载均衡,接收到弹幕的服务器可能并没有与等待接收消息的客户端保持长轮询连接
  • 服务器没有好的方法来判断客户端有没有断开连接

架构图

在这里插入图片描述

推模式

上面MVP版本是拉模式,适合前期;中后期规模和性能要求上来了,需要引入推模式。

架构图

在这里插入图片描述

长连接推送

为了保障客户端消息的推送性能和实时性,需要引入长连接

  • Push Server:负责推送(不感知直播业务)

    • 存储:从 Redis 中获取用户和直播间的关系以及长连接信息
    • 推送:分批并发推
  • connection proxy:只负责与客户端保持长连接

使用技术

  • WebSocket(HTTP 2.0支持的全双工通信+长连接)
  • Session(基于redis,通过用户会话来推)

优化

  • 有状态服务需要做好路由
  • 两个批量推送:消费 MQ 后批量推;从 push server 到连接代理的每个连接上也批量
  • 推拉结合,可以把短连接的拉作为长连接推的降级方案(服务端监控+客户端上报)

其他挑战

  • 写扩散(m条消息*n次扩散,平方级别)⇒ MQ控制推送速率
  • 丢弹幕 ⇒ push 失败引入重试;落 DB 引入 ACK
  • 带宽压力
  • 灰度拉切推

其他思路

  • 使用 redis 的 Stream 来做弹幕的发送(生产)和阅读(消费)
  • 前端展示上的优化
    • 弹幕去重
    • 发送弹幕后,本地优先展示(即使它丢了 or delay)

参考

  • https://cloud.tencent.com/developer/article/1645888
  • https://www.bilibili.com/video/BV1Uu411u7Cm/

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

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

相关文章

GET与POST:详述HTTP两大请求方法的语义、数据处理机制、安全特性与适用场景

GET和POST方法在HTTP请求中具有明确的角色分工和特性差异。GET适用于读取操作和不敏感数据的传递,强调可缓存性和安全性,而POST适用于写入操作和敏感数据的提交,提供了更大的数据承载能力和更强的隐私保护。本文详细介绍了GET与POST请求方法的…

XTTS数据迁移

文章目录 一、全量迁移1、源端和目标端都需要配置XTTS脚本(源库和目标库都需要进行下列配置)2、源端调用 xttdriver.pl -p做迁移准备3、将源端的数据文件副本和rmanconvert.cmd传到目标端4、在目标端对数据文件拷贝进行字节序的转换 二、XTTS 第1~n次增量…

Web前端 Javascript笔记3

1、垃圾回收机制 内存中的生命周期 1、内存分配 2、内存使用(读写) 3、内存回收,使用完毕之后,垃圾回收器完成 内存泄漏:该回收的,由于某些未知因素,未释放,叫做内存泄漏 栈&#xf…

【DNS】

文章目录 DNS域名解析系统(Domain Name System)DNS系统需要解决的问题DNS域名解析系统(Domain Name System)问题1:DNS名字空间(The DNS Name Space)DNS名字空间(The DNS Name Space)DNS名字空间(The DNS Na…

云手机提供私域流量变现方案

当今数字营销领域,私域流量是一座巨大的金矿,然而并非人人能够轻易挖掘。一家营销公司面临着利用社交、社区、自媒体等应用积累私域流量,并通过销售产品、推送广告等方式实现流量变现的挑战与困境。本文将详细介绍这家公司是如何通过云手机&a…

【MATLAB】基于Wi-Fi指纹匹配的室内定位-仿真获取WiFi RSSI数据(附代码)

基于Wi-Fi指纹匹配的室内定位-仿真获取WiFi RSSI数据 WiFi指纹匹配是室内定位最为基础和常见的研究,但是WiFi指纹的采集可以称得上是labor-intensive和time-consuming。现在,给大家分享一下我们课题组之前在做WiFi指纹定位时的基于射线跟踪技术仿真WiFi…

Spring高手之路17——动态代理的艺术与实践

文章目录 1. 背景2. JDK动态代理2.1 定义和演示2.2 不同方法分别代理2.3 熔断限流和日志监控 3. CGLIB动态代理3.1 定义和演示3.2 不同方法分别代理(对比JDK动态代理写法)3.3 熔断限流和日志监控(对比JDK动态代理写法) 4. 动态代理…

Spring源码刨析之配置文件的解析和bean的创建以及生命周期

public void test1(){XmlBeanFactory xmlBeanFactory new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));user u xmlBeanFactory.getBean("user",org.xhpcd.user.class);// System.out.println(u.getStu());}先介绍一个类XmlBeanFac…

196算法之谜在 JSP 中使用内置对象 request 获取 form 表单的文本框 text 提交的数据。

(1)编写 inputNumber . jsp ,该页面提供一个 form 表单,该 form 表单提供一个文本框 text ,用于用户输入一个正整数,用户在 form 表单中输入的数字,单击 submit 提交键将正整数提交给 huiwenNumber . jsp 页…

LeetCode 57—— 插入区间

阅读目录 1. 题目2. 解题思路3. 代码实现 1. 题目 2. 解题思路 第一步,我们先寻找新区间和原始区间列表的重叠部分。 假设新区间为 [ x 1 , x 2 ] [x_1, x_2] [x1​,x2​],原始区间列表中的其中一个区间为 [ y 1 , y 2 ] [y_1, y_2] [y1​,y2​]&…

产生死锁的四个必要条件

产生死锁的四个必要条件 互斥使用: 一个资源每次只能被一个线程使用。这意味着如果一个线程已经获取了某个资源(比如锁),那么其他线程就必须等待,直到该线程释放资源。 不可抢占: 已经获得资源的线程在释放资源之前,不…

Google Imagen 2对比OpenAI的Dall-E 3 - 同一提示,不同结果

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…

咸鱼之王_手游_开服搭建架设_内购修复无bug运营版

视频演示 咸鱼之王_手游_开服 游戏管理后台界面 源码获取在文章末尾 源码获取在文章末尾 源码获取在文章末尾 或者直接下面 https://githubs.xyz/y28.html 1.安装宝塔 yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh &…

【C++】深度解析---赋值运算符重载(小白一看就懂!!)

目录 一、前言 二、 运算符重载 🍎运算符重载 ① 概念引入 ② 语法明细 ③ 练习巩固 ④ 代码展示 🍇赋值运算符重载 ① 语法说明及注意事项 ② 默认的赋值运算符重载 ③ 值运算符不能重载成全局函数! 三、总结 四、共勉 一、前言…

centos 7.9 nginx本地化安装,把镜像改成阿里云

1.把centos7.9系统切换到阿里云的镜像源 1.1.先备份 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup1.2.下载新的CentOS-Base.repo配置文件 wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo特别…

本地web项目启起来后,无法在浏览器(chrome)看到源码,从而无法打断点;Framework Ignore list

问题描述 本地web项目启起来后,无法在浏览器(chrome)看到源码,从而无法打断点 其他浏览器没看,开发环境一致专注于chrome(其余浏览器有测试同事提缺陷了,才会去看),其余浏览器有没有这个问题&…

linux 内存寻址

(持续更新) 相关概念 查看的书籍为 深入linux内核 内存地址 当使用80x86(32位)微处理器时,一般分为三种不同的地址: 逻辑地址 包含在机器语言指令中用来指定一个操作数或一条指令的地址。每一个逻辑地址…

C#简单工厂模式的实现

using System.Diagnostics.Metrics; using System.Runtime.InteropServices; using static 手写工厂模式.Program;namespace 手写工厂模式 {internal class Program{public interface eats {void eat();}//定义了一个接口public class rice : eats{public void eat() {Console.…

neo4j-01

Neo4j是: 开源的(社区版开源免费)无模式(不用预设数据的格式,数据更加灵活)noSQL(非关系型数据库,数据更易拓展)图数据库(使用图这种数据结构作为数据存储方…

Flutter第七弹 网格列表GridView

1) Flutter提供了网格列表,怎么设置列数? 2)怎么初始化每个列表项Item? 一、GridView简介 Flutter也存在网格列表组建GridView,用于展示多行多列的列表。 1.1 GridView构建 采用GridView.count() 进行构建 1.2 Gr…