海报图片生成服务在狐友的落地实践

news2025/2/28 2:53:13

433872b93a950ad5de8acc117f64cf48.gif

本文字数:22817

预计阅读时间:58分钟

项目背景

狐友作为搜狐的一款社交产品,在流量传播上有着旺盛的需求点。而在流量传播所需的众多载体之中,海报图片以其简单的分享形式、可定制的视觉体验、自带二维码识别导流等特点,成为了社交产品高频必备的流量载体。

作为狐友的前端开发,生成海报图片就成为了我们工作中持续不断的一个重要需求点。以下是狐友目前的产品前端服务矩阵和海报图片的产品形式。

3a966698dba383530cbb2c01d6d8172c.png

图 1 狐友产品前端服务矩阵和海报图片的产品形式

海报图片实现现状

从上图1可以看到,生成海报图片对于狐友产品矩阵来说是一个高频强需求。海报图片作为分享载体,对于各平台的分享流程对接也非常畅通和直观,例如不同于小程序卡片分享只能拘泥于微信平台,网页分享的链接形式不够直观。

而在海报图片这个重要环节,长期的主要技术手段一直是通过各客户端开发在本地设备上进行绘制,但这种方案存在如下的劣势困扰着我们:

  • 各端无法复用:如图2, 如果要全平台都要做图片分享,那么需要各端分别开发,即使生成的图片一模一样,也要开发iOSAndroidH5、小程序一共4遍,整体开发各端无法互相复用。

  • 长图大图崩溃:客户端限于设备平台或系统限制,对于长图的生成并不友好,会出现长图因为内存或算力限制无法生成的情况,其中小程序尤为明显,在微信的框架下很容易长图生成造成程序直接崩溃。

  • 开发效率较低:客户端本地绘制海报图片,一般需要手写原生代码效率不高。小程序端虽然使用wxml-to-canvasH5端使用html-to-canvas)来绘制减轻了一些手写命令式绘制代码的负担,但这种标记语言转canvas在实现上也存在缺陷,相比HTML+CSS的表达力还是非常受限。所以,海报图片在代码层面开发效率比较低。

为了解决以上问题,我们开始着手调研并实践落地了一套全新的海报图片统一服务,命名为hy-ssr-img

fe93807ae24e034bfab40134257c646b.png

图 2 狐友大前端海报生成各端开发状态

海报图片统一服务

海报图片统一服务是一套基于PuppeteerNode.js后端服务端(SSR)渲染页面并截图生成海报图片的服务,这一服务解决了原有海报图片生成的三大问题:

  • 各端无法复用 -> 4端同时复用:iOSAndroidH5、小程序4端只需要开发一遍即可,效率提升400%,如图3

  • 长图大图崩溃 -> 无惧长图渲染:不需要再担心长图渲染问题,服务端渲染图片能力理论上可以达到上万长度的渲染,针对不超过5屏的图片需求来说绰绰有余

  • 开发效率较低 -> 开发效率最优:使用效率超高、表达力超强的标记语言HTML + CSS渲染,开发效率和表达力都达到最优状态

7aa95f8172be61e55b856f7aa80a893c.png

图 3 海报图片统一服务各端复用

那么,海报图片统一服务是如何建立起来的呢?下面是从项目立项开始的具体设计方案。

项目选型

首先,我们调研了业界现有的图片渲染方案,如下表。通过下表的调研总结可以看出各个方案各有优劣势。由于复用提效是重要需求点,我们把方案锁定了服务端渲染方向上,即入选方案为:

  • Node.js截图方案

  • 服务端图层绘画方案

在对比两种方案,以及参考相关业务实践后,我们最终选用了最常见的【Node.js截图方案】。因为服务端图层渲染选用的图形库在排版表达力上远不及HTML+CSS在浏览器上的表达力,而且系统预期的使用对象是前端开发人员,并不是产品和运营,所以服务端图层渲染方案的拖拽方式并不是必选项,反而由于拖拽渲染表达力的限制,对于实现一些排版复杂的图片非常吃力。而Node.js截图方案则没有这方面的问题,前端开发只需要常规开发页面即可,然后交给Puppeteer渲染后进行截图形成最终用户看到的海报图片。

5ee0177b0da3a1a57451a5b365db1203.png

表 1 项目选型方案对比

项目架构流程方案

在确定了【Node.js截图方案】之后,我们对项目整体架构流程进行了设计,首先由于我们的应用的后端Java接口服务已经非常成熟,针对参数的合法性校验及加签解签等防护措施都已建立,所以我们在开发Node.js服务时就没有必要再造轮子,那么怎么把这些基础接口功能和Node.js服务联系起来呢?我们决定把Node.js服务放到内网,通过只允许后端服务直接访问的方式来达到这一目的,这样后端接口层就像一个盾牌,挡在用户和Node.js服务之间,而我们就可以专注实现Node.js的截图功能了。整体方案流程如下图4

e6b3d27356a0879218d9bb835b37b1da.png

图 4 Node.js截图方案整体流程方案

仅首屏SSR渲染方案

整体架构流程方案确定后,我们就需要细化截图相关的详细流程方案。在经过调研之后,我们发现常见的截图开源方案在我们的使用场景中还有很多需要优化的点和不满足需求的部分,因此我们决定自研开发【仅首屏SSR渲染】方案。针对项目特点,每个海报图片只有一张,所以不存在首屏渲染过后还要渲染第二页的场景,因此该方案只包含首屏字符串渲染即可,不需要带有常见 SSR 的客户端激活渲染的打包构建及执行流程。 去掉常见SSR方案中的非首屏渲染逻辑后,页面就只包含了首屏必要的渲染代码(HTML + CSS),去除一切不需要的环节(JS执行激活),保证页面给到 Puppeteer渲染时是最简化的状态,尽可能减少网络I/O和本地磁盘I/O,只包含单纯的渲染过程,以加速渲染速度。以下是常见方案和自研方案的对比。

最常见的截图方案是通过请求网页地址,渲染后截图,如图5

1d539af59efb1f123412f68e7b62c1fc.png

图 5 Node.js截图详细流程常见方案

这种方案不管是CSR(客户端渲染)方式,还是SSR(服务端渲染)方式,都还是有优化空间。我们可以发现,Puppeteer请求网页的网络消耗是可以被节省下来的,如果我们直接使用在本地生成好的HTMLCSS,则请求网页并下载所用的时间就可以节省下来,获得更快的截图速度。

另外页面的动态渲染通过SSR来进行,这样完全不需要客户端JS的存在,直接还可以省去加载客户端JS的时间,获得更优的渲染速度,这种优化后的Node.js截图详细流程方案如图6所示。

826d7aca6e74803b4958f7d8dbdea062.png

图 6 优化后的Node.js截图详细流程方案

该方案还有技术细节需要设计,即如何完成SSR渲染首屏的工作,由于我们的技术栈是Vue,所以选用vue-server-renderer为基础库来完成这一过程,如图7。

2232a29576cfbd414f429a9076a6df5a.png

图 7 仅首屏SSR渲染方案技术流程

项目业务技术流程设计

项目业务技术流程设计

整体架构流程方案确定之后,我们就要进行更细化的业务技术流程设计,这里有很多细节需要考虑:

  • 后端接口层如何和Node.js截图服务进行通信?

  • 截图服务是一个高耗时服务,如何防止长链接堆积?

  • 如何设计海报图片缓存层?如何设计提供海报预渲染?

  • 其他流程细节...

为了解决这些问题,我们设计了如下的流程,如图8

1179836946f0ec7e61752a4c6b3fb891.png

图 8 Node.js截图服务业务技术流程

可以看到整个流程设计由接口同步请求和截图异步任务两大块组成。我们把Node.js截图服务当做一个纯渲染图片的服务,用户发起请求给后端服务时会携带渲染页面所需要的动态参数,后端服务则负责参数校验等工作,并且下发给用户一个异步任务ID。然后后端服务会请求截图服务,截图服务收到请求后并不会立刻截图,而是直接返回后端服务,并开启异步任务去进行耗时的截图服务,这样就可以防止长链接堆积造成服务不可用的情况发生。之前下发给用户侧的异步任务ID就是异步截图任务完成后通知后端服务的凭证,这样当截图服务完成后就可以通知后端服务截图完成状态(截图成功或者失败),而用户侧则可通过轮询后端服务接口得知截图是否完成并使用海报图片了,当然可以设置一个超时时间来完成整个截图服务的交互闭环。

对于海报图片的缓存层也是要考虑的,因为很多场景下用户请求的海报图片是一模一样的,比如我们的热榜海报会在固定时间生成一次,那么缓存层可以有效缓解截图耗时操作,并且为预生成海报图片提供了基础。比如我们会在特定时间通过代码自动预渲染一批海报,当第一个用户来访问时就不需要等待耗时的截图服务,直接返回渲染好的海报图片即可。那么命中缓存的条件是什么呢?对于长的一模一样的海报图片当然希望只生成一次后都走缓存层。我们的设计中决定“图片长相”的因素有两个:

1.海报地址:对应不同的海报样式

2.海报参数:对应同一个海报样式中的不同数据

所以,我们通过hash(“海报地址”+“海报参数”)的方式得到缓存命中的key,以此来控制命中缓存。另外截图服务生成海报图片后会直接上传到CDN进行存储,用户侧加载海报图片的速度和稳定性也得到了相应的保障。

项目技术选型及工程化方案

项目技术选型及工程化方案

在业务技术流程方案确定后,就需要为此搭建一套工程化开发环境,来支持项目业务的具体开发。我们基于公司现有基础设施以及技术栈,确定了以下主要技术选型:

  • 接口服务框架:Express

  • 页面渲染框架:Vue

  • 支持SSR渲染:vue-server-renderer

  • 支持浏览器截图:Puppeteer

  • 支持页面开发环境 webpack

  • 图片存储:OSS (公司内部对象存储服务)

  • 服务端日志:Logstash + Kafka+ Elasticsearch + Kibana(公司内部基础日志服务链)

  • 部署:DomeOS (公司内部云服务平台)

如图9,展示了通过以上技术栈及基础设施组建的整个工程化方案。

07c09958d40243cc443aeac283b8b52a.png

图 9 Node.js截图服务工程化方案     

这里需要说明是,一个海报图片对应一个页面,一个页面会有两个入口文件:一个CSR(客户端渲染)用于开发页面时使用,一个SSR(服务端渲染)用于截图时Puppeteer渲染页面使用。这么设计的原因是,CSR渲染使用webpack-dev-server是现成的开源方案,对于开发时热更新等支持不需要自定义开发,开箱即用,如果开发时也使用SSR进行就需要进行针对性的改造,由于开发体验上并没有区别,我们就选用了更高效地搭建方式。而截图时页面渲染方式就是上文提到的【仅首屏 SSR 渲染】。

项目关键优化实践

从用户发起请求到海报图片返回,这整个过程的耗时需要进行“压榨”,以获得更好的用户体验。以下是我们在开发过程中实践和验证的相关重点优化和部分效果收益。从第一版基础开发到最后优化完成的版本,截图服务总用时从1300ms+降低到了600ms+(注:测试数据均取相同开发机10次执行结果的平均值,渲染相同的海报图片,图片为超长图且内容丰富,长宽为:4967×750)。表2为各关键优化点明细。

34291cfbd4b43ab761d60622b9900c65.png

表 2 项目关键优化实践

项目在线文档预览系统

为了方便使用海报图片的开发人员,我们还配套开发了海报生成系统在线文档。开发人员可以通过该系统查看现有的海报图片以及相关参数字段,并可以通过右方的编辑器更改字段的值并实时得到新的渲染图进行预览和下载。如图10,开发人员可以在这个playground里进行所见即所得的预览及操作。

2ce380e2da15cae451d52433077d2c5b.png

图 10 海报在线文档预览系统

项目日志实践

海报图片服务作为一个后端服务,日志采集分析和监控是必不可少的,我们可以通过日志得到以下信息:

  • 海报图片生成的访问情况

  • 海报图片生成的性能优劣

  • 海报图片生成失败的原因

  • 海报图片生成异常的报警

所以,我们封装了3log日志用于海报图片服务:

  • access:记录每次请求的请求日志

  • debug:记录生成图片的关键节点信息的日志

  • error:记录生成图片失败及原因的错误日志

日志处理完成后,我们接入了公司现有日志基建服务来完成后续的日志采集、存储和分析等功能,如图11所示,通过这一套日志流程,我们就可以更加放心地上线并时刻关注我们服务的运行情况,轻松做到快速排查和分析问题。

cec8bf3ddd11c41f21d186a0ea7acf08.png

图 11 海报系统日志服务

项目部署方案

海报生成是一个耗时任务,其绘制速度依赖于服务器的CPU和内存。经过线上数据评估,截图服务qps最大支持60即可,经使用JMeter并发测试:

  • 当使用单实例(CPU1 + 内存 2G)单进程部署项目时,并发为1时生成5000像素的图片需要耗时约2s,并发数超过1时,后面的请求得等待前面请求生成完图片,才会生成,生成图片时间会随并发数成倍增加。

  • 当使用单实例(CPU1 + 内存 2G)多进程部署项目时,并发为1时生成5000像素的图片需要耗时约2s,并发数超过1时,当并发数少于进程数时,会同时生成图片,并发为5时,图片返回时间约为9s,此时CPU占用会超过40%,内存占用20%,虽然CPU和内存在并发数量为5的时候占用率不高,但是生成图片的速度会大幅下降,所以需要增大单实例CPU和内存的大小。

  • 当使用单实例(CPU4 + 内存 6G)多进程部署时,并发为1时生成5000像素的图片耗时约1s,并发数为5的时候,图片返回时间约为3s,当并发为10的时候,图片返回时间约为8s。此时CPU占用会超过20%,内存占用10%

所以为了保证图片的生成速度和稳定性,使并发量少的时候图片生成速度尽可能快,并发量大的时候图片生成速度在可接受时间内,采用了多实例加多进程的部署方式,如图12,项目部署于DomeOS平台,部署了5 CPU4 + 内存 6G 实例,每个实例通过pm2启动4个进程。通过负载均衡可以使请求平均打到每个实例的每个进程上,让并发少的时候最快的生成图片,并发大的时候充分利用所有实例,加快整体生成图片时间。生成5000像素的图片,当并发数小于5时,图片可以在1s左右返回,当并发数为20,图片可以在3s左右返回,当并发数为60时,图片可以在8s之内返回。

23f2a7fbc618167d732e027add5fed8c.png

图 12 海报系统部署方案

项目成果

截止到2023年初,海报图片服务已上线海报10+个。每个海报只需开发1遍就可供给H5、小程序、AndroidiOS4端进行使用。每天平均生成海报图片6000+,每张海报图片平均生成时间400ms左右,支持超长图可达10000+像素。

未来展望

随着项目迭代,海报服务未来可能有更大的需求诉求,下面列出海报服务未来进化的一些展望:

1、赋能外部开发人员

目前项目是以普通项目的开发模式进行研发,如果提供给非内部研发人员使用则有很多流程和规范上的问题难以解决。未来可以支持渲染远程组件,通过远程组件的方式下发给外部研发进行开发,以此隔绝海报服务核心逻辑和业务方逻辑,使得业务方只需关心业务,也防止业务方无意间可能影响到海报核心逻辑。此外,在整个过程中还可以增加审核远程组件等项目管理能力。

2、赋能非研发人员

为了海报图片渲染极具灵活性,我们把开发人员作为首要满足对象。未来除了开发人员可以开发海报页面,同时可以支持非研发人员通过拖拽编辑等低代码方式完成海报图片的生产,这样针对简单海报图片的场景,运营、产品等非研发人员也可以进行海报图片的制作,进一步提高生产效能。

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

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

相关文章

入门与 Follow GPT 的路径分析:LLM 道阻且长,行则将至

动手点关注 干货不迷路 本文只用于技术交流,仅代表作者个人观点。 作为 CEO,Sam 将 OpenAI 的内部氛围组织的很好,有位 OpenAI 的前员工告诉拾象团队,当 2018 年 GPT-2 的论文被驳回时,Sam 在团队周会上将拒信的内容朗…

ONES X 高测股份|用数字化,重构新材料企业的研发管理体系

近日,ONES 签约高硬材料切割的领军企业——高测股份,助力高测股份建立有效、规范的研发测试管理体系,实现项目管理、测试管理、知识库管理、工时管理、组织效能管理等端到端的研发管理,提升测试效率和产品交付质量,并进…

用Python+OpenCV+Yolov5+PyTorch开发的车牌识别软件(包含训练数据)

目录 演示视频 软件使用说明 软件设计思路 演示视频 这是一个可以实时识别车牌的软件,支持图片和视频识别,以下是软件的演示视频。 车牌识别软件 点击查看代码购买地址 软件使用说明 1. 下载源码后,首先安装依赖库。项目所用到的依赖库已…

bash shell 基础命令

章节目录: 一、浏览文件系统1.1 Linux 文件系统1.2 遍历目录 二、列出文件和目录三、处理文件3.1 创建文件3.2 复制文件3.3 命令行补全3.4 链接文件3.5 文件重命名3.6 删除文件 四、管理目录4.1 创建目录4.2 删除目录 五、查看文件内容5.1 查看文件类型5.2 查看整个…

【Linux】Linux编辑神器vim的使用

目录 一、Vim的基本概念 二、Vim的基本操作 1、进入vim 2、正常模式切换至插入模式 3、插入模式切换至正常模式 4、正常模式切换至底行模式 5、退出Vim编辑器 三、Vim正常模式命令集 1、移动光标 2、删除文字 3、复制 4、替换 5、撤销 四、Vim底行模式命令集 1、列出行号 2、光…

【Linux】进程信号捕捉

前言 在【Linux】进程信号及信号产生中,我们提到,进程接收到信号,并不是立刻处理,而是在合适的时候才执行相应的动作,那合适的时候是什么时候呢,进程捕捉信号的过程究竟是怎么样的呢?本篇博客就…

Python入门(七)if语句(二)

if语句(二) 1.if语句1.1 简单的if语句1.2 if-else语句1.3 if-elif-else结构1.4 使用多个elif代码块1.5 使用多个elif代码块 2.使用if语句处理列表2.1 检查特殊元素2.2 确定列表不是空的2.3 使用多个列表 作者:xiou 1.if语句 前面我们理解了…

React Native技术探究:开发高质量的跨平台移动应用的秘诀

作为一个跨平台移动应用开发框架,React Native在开发过程中能够有效提高开发效率、降低开发成本、缩短上线时间,因此备受开发者的欢迎。然而,如何使用React Native开发出高质量的跨平台移动应用呢?本文将探究这个问题,…

(一)PUN 2基本介绍

一、开始 (一)基本简介 1.简介 Photon Unity Networking (PUN) 是一个用于多人游戏的 Unity 包。灵活的配对让您的玩家进入可以通过网络同步对象的房间。 RPC、自定义属性或“低级别”光子事件只是其中的一些功能。快速且(可选)可靠的通信是通过专用的…

选卡攻略!一分钟教你选择一款好的流量卡!

很多流量卡看似便宜,但用起来套路不少,所以,挑选一款流量卡并不是只看资费,而且要了解它的各个方面,比如:优惠期、合约期等等,今面,小编就带大家一块了解如何选择一款好的流量卡。 …

默认的HuggingFace模型

介绍 在本文中,我将探讨表征学习中常见的一种做法——使用预训练神经网络的冻结状态作为学习特征提取器。 具体而言,我感兴趣的是研究使用这些提取的神经网络特征训练的简单模型的性能与使用迁移学习初始化的微调神经网络的性能的比较。预期受众主要是数…

一款功能强大的多合一聚合支付Discuz插件,可直接替换DiscuzX 3.5自带的支付接口

源码介绍: Discuz论坛多合一聚合支付接口插件,该插件直接替换了自带的支付接口功能, 增强了支付的扩展性,自带支持支付宝、微信、QQ钱包官方支付, 以及彩虹易支付、我爱支付、虎皮椒等支付通道,并且可以…

专家共话:存力一体成“东数西算”全场景利器

从2022年2月17日正式全面启动至今,“东数西算”工程已经度过了一周年。一年多的实践探索证明,激活数据要素,推动算力成为核心生产力,离不开“存力先行”。其中,东西部存力一体化,是支撑“东数西算”全场景应…

「读书感悟系列」友者生存:与人为善的进化力量(我会解释人类善良和暴力的进化原因)...

作者 | gongyouliu 编辑 | gongyouliu ‍ 最近花了不长的时间读完了这本『友者生存:与人为善的进化力量』,由于我对进化生物学非常感兴趣,又有4年的生物学学习背景,读起来还是非常容易的,这本书算是人类进化学的科普读…

tcp cubic 与随机丢包

前面提到过一个 AIMD 的修正方法,“二次机会 MD”:首次丢包只 MD 收缩一个相对较小的比例,再次丢包时再继续收缩,直到 beta * Wmax。 效果如下图: 大意是在检测到丢包时,先 MD 一个相对小的缩放比例&…

DS215KLDCG1AZZ03A如何编写温度比例的代码?

DS215KLDCG1AZZ03A如何编写温度比例的代码&#xff1f; 可编程逻辑控制&#xff0c;简称PLC&#xff0c;由美国机械工程师迪克莫利于1年1968月<>日首次设计。PLC最初是为了减少汽车行业员工的工作量而开发的&#xff0c;从那时起&#xff0c;它们已被用于所有其他恶劣环境…

常用linux多场景查找文件的方法及对比

波哥整理了在linux中根据文件名称查文件&#xff0c;根据文件内容查找文件&#xff0c;根据进程号查找文件的方法及命令详细解释。 一、find命令 find命令可以在指定目录下查找文件&#xff0c;其语法如下&#xff1a; find <目录> <选项> <匹配模式> 选项和…

vue-4:注册组件,组件传参,特殊属性ref $parent $root,透传,内置组件,自定义指令,mixins混入

注册组件&#xff0c;使用组件&#xff08;项目中都是写component中&#xff09; 为什么用组件&#xff1a;组件可以复用&#xff0c;每一个组件都是独立的&#xff0c;模板&#xff0c;数据&#xff0c;css样式互不影响 全局注册组件component&#xff1a;请勿滥用全局组件in…

一个打通基于XML管理Bean

目录 准备工作 添加依赖 引入配置文件 获取bean ①方式一&#xff1a;根据id获取 ②方式二&#xff1a;根据类型获取 ③方式三&#xff1a;根据id和类型 ④注意的地方 ⑤扩展知识 依赖注入之setter注入 ①创建学生类Student ②配置bean时为属性赋值 ③测试 依赖注…

Powerlink协议在嵌入式linux上的移植和主从站通信(电脑和linux板通信实验)

使用最新的openPOWERLINK 2.7.2源码&#xff0c;业余时间搞定了Powerlink协议在嵌入式linux上的移植和测试&#xff0c;并进行了下电脑和linux开发板之间的通信实验。添加了一个节点配置&#xff0c;跑通了源码中提供的主站和从站的两个demo。这里总结下移植过程分享给有需要的…