当前位置: 首页 > news >正文

高效处理 iOS 应用中的大规模礼物数据:以直播项目为例(1-礼物池)

引言

在现代iOS应用开发中,处理大规模数据是一个常见的挑战。尤其实在直播项目中,礼物面板作为展示用户互动的重要部分,通常需要实时显示海量的礼物数据。这些数据不仅涉及到不同的区域、主播的动态差异,还需要保证高效的加载与渲染,以提供流畅的用户体验。

本篇博客将以直播应用中的礼物面板为例,深入探讨如何高效地管理和处理这些庞大的数据。我们将分享一种基于“礼物池”设计的解决方案,从服务端下载并解压存储数据,再根据实时的面板数据提取礼物信息的方式,确保数据的高效存取与更新。同时,还将讨论如何通过合理的本地缓存和更新机制,进一步提升应用性能,并优化用户体验。

如果你也在处理类似的大数据问题,或者正在开发类似的直播应用,本篇博客将为你提供实用的思路和解决方案。

架构介绍

上面所展示的礼物面板中的所有礼物数据,大概有2-4M,这对于一个请求来说应该算是一个非常大的数据量,而由于针对不同地区不同的主播所展示的礼物也不同,那么我们就可以需要频繁的来请求整个礼物面板的数据,每次都请求这么大的数据显然这并不理智。为此我们可以考虑将它分割成两部分:礼物池和礼物面板。

  • 礼物池:礼物池内存放的是所有的礼物该数据由一个接口下发为固定数据几乎不会变动出发有新类型的礼物加入。
  • 礼物面板:根据层级返回每个一级tab对应下的二级tab,而二级tab下只需要包含礼物的id。

礼物池的数据结构如下:

截图中只展示了礼物列表中的一个礼物,而且数据并没有完全展示出来,那这组json来说一共有1600个这样的礼物,整个礼物池大小为3.8M。

礼物面板的数据结构如下:

我们抛开一级tab直接看二级tab下的gifts里面只保存了礼物的id。这也就大大减小了礼物面板的数据大小、获取到礼物的id之后再从礼物池中读取礼物的完整数据。这样即使频繁请求礼物面板接口也不会造成很大性能影响。

礼物池的存储与更新机制

礼物池json文件的压缩包是通过一个接口获取的,该接口会返回礼物池的压缩文件路径,以及礼物池的当前版本号。

等获取到该接口的数据之后,我们需要做的有三件事:

  1. 对比礼物版本号与当前本地礼物版本号,如果相等则不需要更新json文件,直接加载本地json。
  2. 否则将新的礼物池版本号进行保存。
  3. 开始下载新的礼物池压缩包。
    /// 请求礼物池数据private func requestGiftPoolData() {MWNetworkHelper.request(endpoint: MWAPINormalEndpoint.api_giftPoolData, parameters: [:], modelType: MWGiftPoolModel.self) {[weak self] model, data, error inguard let self = self else { return }guard let model = model else {MWLogHelper.error("请求礼物池数据失败 err:\(error?.description ?? "")", context: "MWGiftPoolManager")return}// 是否需要下载新的zipif model.version == MWUserDefaultsAppHelper.giftPoolVersion {self.loadLocalJson(model: model)return}// 将礼物版本号保存到本地MWUserDefaultsAppHelper.giftPoolVersion = model.versionself.startDownloadZip(zip_url: model.default_zip)}}

本地读取

如果本地已经有了礼物池数据,且当前礼物池版本号与本地版本号相同时,那么我们就可以直接读取本地json,但读取本地json时并不一定就会成功,当读取失败,或者转换失败时,需要重新进行下载。

    /// 加载本地json文件/// - Parameter model: 礼物池modelprivate func loadLocalJson(model:MWGiftPoolModel) {// 本地版本号和服务器版本号一致 直接读取if let jsonPath = MWUserDefaultsAppHelper.giftPoolJsonPath {MWLogHelper.info("读取本地json path:\(jsonPath)", context: "MWGiftPoolManager")// 拼接document路径let document = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first ?? ""let filePath = document.appending("/\(jsonPath)")do {let jsonData = try Data(contentsOf: URL(fileURLWithPath: filePath))let json = try JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers)if let jsonDict = json as? [String:Any] {MWLogHelper.info("读取本地json成功", context: "MWGiftPoolManager")self.jsonDict = jsonDictself.convertModel(jsonDict: jsonDict)}} catch {MWLogHelper.error("读取本地json失败 重新下载 err:\(error.localizedDescription)", context: "MWGiftPoolManager")// 转换失败也需要重新下载self.startDownloadZip(zip_url: model.default_zip)}}}

下载zip解压并存储

如果json文件需要更新或者首次下载,那么在下载完成之后需要将json写入到本地,供以后直接读取。

    /// 开始下载zipprivate func startDownloadZip(zip_url:String) {// 获取zip文件名let lastPathComponent = zip_url.components(separatedBy: "/").last ?? ""MWNetworkHelper.downloadFile(url: zip_url,file: "giftPool",fileName: lastPathComponent) { progress in} completion: {[weak self] path, error inguard let self = self else { return }if let path = path {MWLogHelper.info("下载zip成功 path:\(path)", context: "MWGiftPoolManager")self.startUnZipFile(zipURL: path)} else {MWLogHelper.error("下载zip失败 err:\(error?.description ?? "")", context: "MWGiftPoolManager")}}}

此时下载完成时一个zip包,借助Zip进行解压,解压完成之后获取到json,构建数据模型,并将json输入写入到document文件夹,保存相对路径。

    /// 开始解压private func startUnZipFile(zipURL: URL) {do {let unzipUrl = try Zip.quickUnzipFile(zipURL)// 获取文件名let lastPathComponent = zipURL.lastPathComponent.components(separatedBy: ".").first ?? ""MWLogHelper.info("解压zip成功 path:\(unzipUrl)", context: "MWGiftPoolManager")let jsonURL = unzipUrl.appendingPathComponent("\(lastPathComponent).json")MWLogHelper.info("拼接路径 path:\(jsonURL)", context: "MWGiftPoolManager")// 将文件路径存储起来(document以后)let filePath = lastPathComponent.appending("/\(lastPathComponent).json")MWUserDefaultsAppHelper.giftPoolJsonPath = filePathlet jsonData = try Data(contentsOf: jsonURL)let json = try JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers)if let jsonDict = json as? [String:Any] {self.jsonDict = jsonDictself.convertModel(jsonDict: jsonDict)}} catch {MWLogHelper.error("解压zip失败 err:\(error.localizedDescription)", context: "MWGiftPoolManager")}}

json数据转哈希表

从上面的代码中我们还可以看见一个比较重要的方法covertModel(),该方法接收的就是礼物池的原始数据,然后我们通过遍历礼物池下的gifts礼物列表来构建礼物的数据模型。模型构建完成之后,我们使用键值对的形式将礼物的数据模型和礼物id成对的保存到表中。

    /// 礼物池(键值对形式)public var giftPoolMap:[Int:MWGiftModel] = [:]
    /// 转modelprivate func convertModel(jsonDict: [String:Any]) {if let giftPools = jsonDict["gifts"] as? [[String:Any]] {// 遍历giftpools创建一个字典for giftDict in giftPools {let giftModel = MWGiftModel(JSON: giftDict)guard let giftId = giftModel?.giftId else {continue}giftPoolMap[giftId] = giftModel}}giftPoolCallback?(giftPoolMap)MWGiftLoader.shared.startLoadGift(giftPoolMap: giftPoolMap)MWLogHelper.debug("转成模型 :\(giftPoolMap.count)", context: "MWGiftPoolManager")}

方便后续从礼物池中直接读取礼物模型。

结语

在本篇博客中,我们探讨了直播应用中礼物面板的核心架构设计,以及如何通过礼物池实现高效的数据加载与更新。这种基于本地缓存和远程更新机制的方案,不仅提升了应用的响应速度,还有效降低了网络请求对性能的影响。

然而,礼物池只是整个礼物实现的一部分,为了真正的完成礼物展示,还需要处理礼物面板的数据解析,动态筛选,以及与礼物池的高效匹配。在下一篇博客中,我们将深入解析礼物面板的实现细节。

相关文章:

高效处理 iOS 应用中的大规模礼物数据:以直播项目为例(1-礼物池)

引言 在现代iOS应用开发中,处理大规模数据是一个常见的挑战。尤其实在直播项目中,礼物面板作为展示用户互动的重要部分,通常需要实时显示海量的礼物数据。这些数据不仅涉及到不同的区域、主播的动态差异,还需要保证高效的加载与渲…...

python的函数与递归

需求: 编写一个函数,计算斐波那契数列的第 N 项,并使用递归实现。 为了计算斐波那契数列的第 N 项,可以使用递归方法。斐波那契数列的定义是: F(0) 0 F(1) 1 对于 n > 2,F(n) F(n-1) F(n-2)&#xf…...

RabbitMQ学习-Seven

再SpringBoot中使用MQ 1.创建SpringBoot项目 除了我们平常使用的一些工具依赖,还需要选择这个Spring for RabbitMQ依赖 2.需要在application.yml文件中进行配置 server:port :9090 spring:application:name:producerrabbitmq:host: 你的主机名port: 5672virtual-…...

中科亿海微SoM模组——波控处理软硬一体解决方案

本文介绍的波控处理软硬一体解决方案主要是面向相控阵天线控制领域,波控处理通过控制不同天线组件的幅相来调整天线波束的方向和增益,实现高精度角度控制和高增益。本方案由波控处理板、波控处理控制软件算法和上位机软件共同构成。波控处理SoM模组原型样…...

开源法律、政策和实践

#一切皆可开源# 木兰社区对《Open Source Law,Policy and Practice 》这本书的第二版进行了翻译,并发布在了gitee上。这本书是对开源文化、开源政策、法律的全面介绍。目录如下: 1 Open Source as Philosophy,Methodology,and CommerceUsing Law with …...

【计算视觉算法与应用】金字塔,下采样Gaussian Pyramid. 上采用 Laplacian Pyramid (code: py)

金字塔(Pyramid)在图像处理中主要用于多尺度分析和图像压缩。常见的图像金字塔有两种: 高斯金字塔(Gaussian Pyramid):用于下采样图像,生成分辨率逐渐降低的图像序列。拉普拉斯金字塔&#xff…...

基于BERT的语义分析实现

✨✨ 欢迎大家来访Srlua的博文(づ ̄3 ̄)づ╭❤~✨✨ 🌟🌟 欢迎各位亲爱的读者,感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢,在这里我会分享我的知识和经验。&am…...

DNS查询工具

DNS查询工具是用于查询和获取域名相关信息的工具。通过这些工具,您可以获取到诸如IP地址、邮件服务器以及域名服务器等信息,这对于排查问题、设置域名配置以及确保网站正常运行都非常重要。 以下是五款常用的DNS记录查询工具: MxToolbox MxTo…...

ODB 框架

目录 概述 基本工作原理 映射C对象到数据库表 从数据库中加载对象 持久化C对象到数据库 ODB常用接口 表创建预处理 #pragma db Object table 数据表属性 id auto column(“xxx”) type("xxx") unique index null default&…...

Ubuntu WiFi检测

ubuntu检测到多个同名wifi,怎么鉴别假冒的wifi? 在Ubuntu中,如果检测到多个同名的Wi-Fi网络,可能存在假冒的Wi-Fi(例如“蜜罐”攻击)。以下是一些鉴别假冒Wi-Fi的方法: 检查信号强度&#xff1a…...

QILSTE H4-108TCG高亮纯lu光LED灯珠 发光二极管LED

型号:H4-108TCG 在电子领域,H4-108TCG LED以其卓越的性能和微小的尺寸1.6x0.8x0.4mm脱颖而出。这款高亮纯绿光LED,采用透明平面胶体,符合EIA标准包装,是环保产品,符合ROHS标准。防潮等级为Level 3&#xf…...

IP与“谷子”齐飞,阅文“乘势而上”?

爆火的“谷子经济”,又捧出一只“潜力股”。 近日,阅文集团股价持续上涨,5日累计涨幅达13.20%。这其中,周三股价一度大涨约15%至29.15港元,强势突破20日、30日、120日等多根均线,市值突破280亿港元关口。 …...

Java阶段三05

第3章-第5节 一、知识点 动态代理、jdk动态代理、cglib动态代理、AOP、SpringAOP 二、目标 理解什么是动态代理和它的作用 学会使用JAVA进行动态代理 理解什么是AOP 学会使用AOP 理解什么是AOP的切入点 三、内容分析 重点 理解什么是动态代理和它的作用 理解什么是AO…...

C# yield 关键字

文章目录 前言一、yield 关键字的语法形式及使用场景(一)yield return(二)yield break 二、yield 关键字的工作原理三、yield 关键字的优势与应用场景(一)优势(二)应用场景 前言 在 …...

SpringBoot开发——结合Nginx实现负载均衡

文章目录 负载均衡介绍介绍Nginx实现负载均衡的示例图:负载均衡策略1.Round Robin:2.Least Connections:3.IP Hash :4.Generic Hash:5.Least Time (NGINX Plus only)6.Random:Nginx+SpringBoot实现负载均衡环境准备Nginx 配置负载均衡测试负载均衡介绍 介绍 在介绍Nginx的负…...

RabbitMQ在手动消费的模式下设置失败重新投递策略

最近在写RabbitMQ的消费者,因为业务需求,希望失败后重试一定次数,超过之后就不处理了,或者放入死信队列。我这里就达到重试次数后就不处理了。本来以为很简单的,问了kimi,按它的方法配置之后,发…...

TsingtaoAI具身智能高校实训方案通过华为昇腾技术认证

日前,TsingtaoAI推出的“具身智能高校实训解决方案-从AI大模型机器人到通用具身智能”基于华为技术有限公司AI框架昇思MindSpore,完成并通过昇腾相互兼容性技术认证。 TsingtaoAI&华为昇腾联合解决方案 本项目“具身智能高校实训解决方案”以实现高…...

【Linux】线程池设计 + 策略模式

🌈 个人主页:Zfox_ 🔥 系列专栏:Linux 目录 一:🔥 线程池 1-1 ⽇志与策略模式1-2 线程池设计1-3 线程安全的单例模式1-3-1 什么是单例模式1-3-2 单例模式的特点1-3-3 饿汉实现⽅式和懒汉实现⽅式1-3-4 饿汉…...

网络原理(一):应用层自定义协议的信息组织格式 HTTP 前置知识

目录 1. 应用层 2. 自定义协议 2.1 根据需求 > 明确传输信息 2.2 约定好信息组织的格式 2.2.1 行文本 2.2.2 xml 2.2.3 json 2.2.4 protobuf 3. HTTP 协议 3.1 特点 4. 抓包工具 1. 应用层 在前面的博客中, 我们了解了 TCP/IP 五层协议模型: 应用层传输层网络层…...

Python-链表数据结构学习(1)

一、什么是链表数据? 链表是一种通过指针串联在一起的数据结构,每个节点由2部分组成,一个是数据域,一个是指针域(存放下一个节点的指针)。最后一个节点的指针域指向null(空指针的意思&#xff0…...

【Python】 -- 趣味代码 - 小恐龙游戏

文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...

应用升级/灾备测试时使用guarantee 闪回点迅速回退

1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间, 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点,不需要开启数据库闪回。…...

RocketMQ延迟消息机制

两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后&#xf…...

Zustand 状态管理库:极简而强大的解决方案

Zustand 是一个轻量级、快速和可扩展的状态管理库,特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...

UDP(Echoserver)

网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序

一、开发准备 ​​环境搭建​​: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 ​​项目创建​​: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...

django filter 统计数量 按属性去重

在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...

什么是库存周转?如何用进销存系统提高库存周转率?

你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...

今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存

文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...

Java线上CPU飙高问题排查全指南

一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...