狐友作为搜狐的一款社交产品,在流量传播上有着旺盛的需求点。而在流量传播所需的众多载体之中,海报图片以其简单的分享形式、可定制的视觉体验、自带二维码识别导流等特点,成为了社交产品高频必备的流量载体。
作为狐友的前端开发,生成海报图片就成为了我们工作中持续不断的一个重要需求点。以下是狐友目前的产品前端服务矩阵和海报图片的产品形式。
图 1 狐友产品前端服务矩阵和海报图片的产品形式
从上图1可以看到,生成海报图片对于狐友产品矩阵来说是一个高频强需求。海报图片作为分享载体,对于各平台的分享流程对接也非常畅通和直观,例如不同于小程序卡片分享只能拘泥于微信平台,网页分享的链接形式不够直观。
而在海报图片这个重要环节,长期的主要技术手段一直是通过各客户端开发在本地设备上进行绘制,但这种方案存在如下的劣势困扰着我们:
为了解决以上问题,我们开始着手调研并实践落地了一套全新的海报图片统一服务,命名为hy-ssr-img。
图 2 狐友大前端海报生成各端开发状态
海报图片统一服务是一套基于Puppeteer的Node.js后端服务端(SSR)渲染页面并截图生成海报图片的服务,这一服务解决了原有海报图片生成的三大问题:
图 3 海报图片统一服务各端复用
那么,海报图片统一服务是如何建立起来的呢?下面是从项目立项开始的具体设计方案。
首先,我们调研了业界现有的图片渲染方案,如下表。通过下表的调研总结可以看出各个方案各有优劣势。由于复用提效是重要需求点,我们把方案锁定了服务端渲染方向上,即入选方案为:
在对比两种方案,以及参考相关业务实践后,我们最终选用了最常见的【Node.js截图方案】。因为服务端图层渲染选用的图形库在排版表达力上远不及HTML+CSS在浏览器上的表达力,而且系统预期的使用对象是前端开发人员,并不是产品和运营,所以服务端图层渲染方案的拖拽方式并不是必选项,反而由于拖拽渲染表达力的限制,对于实现一些排版复杂的图片非常吃力。而Node.js截图方案则没有这方面的问题,前端开发只需要常规开发页面即可,然后交给Puppeteer渲染后进行截图形成最终用户看到的海报图片。
表 1 项目选型方案对比
在确定了【Node.js截图方案】之后,我们对项目整体架构流程进行了设计,首先由于我们的应用的后端Java接口服务已经非常成熟,针对参数的合法性校验及加签解签等防护措施都已建立,所以我们在开发Node.js服务时就没有必要再造轮子,那么怎么把这些基础接口功能和Node.js服务联系起来呢?我们决定把Node.js服务放到内网,通过只允许后端服务直接访问的方式来达到这一目的,这样后端接口层就像一个盾牌,挡在用户和Node.js服务之间,而我们就可以专注实现Node.js的截图功能了。整体方案流程如下图4:
图 4 Node.js截图方案整体流程方案
整体架构流程方案确定后,我们就需要细化截图相关的详细流程方案。在经过调研之后,我们发现常见的截图开源方案在我们的使用场景中还有很多需要优化的点和不满足需求的部分,因此我们决定自研开发【仅首屏SSR渲染】方案。针对项目特点,每个海报图片只有一张,所以不存在首屏渲染过后还要渲染第二页的场景,因此该方案只包含首屏字符串渲染即可,不需要带有常见 SSR 的客户端激活渲染的打包构建及执行流程。 去掉常见SSR方案中的非首屏渲染逻辑后,页面就只包含了首屏必要的渲染代码(HTML + CSS),去除一切不需要的环节(JS执行激活),保证页面给到 Puppeteer渲染时是最简化的状态,尽可能减少网络I/O和本地磁盘I/O,只包含单纯的渲染过程,以加速渲染速度。以下是常见方案和自研方案的对比。
最常见的截图方案是通过请求网页地址,渲染后截图,如图5。
图 5 Node.js截图详细流程常见方案
这种方案不管是CSR(客户端渲染)方式,还是SSR(服务端渲染)方式,都还是有优化空间。我们可以发现,Puppeteer请求网页的网络消耗是可以被节省下来的,如果我们直接使用在本地生成好的HTML和CSS,则请求网页并下载所用的时间就可以节省下来,获得更快的截图速度。
另外页面的动态渲染通过SSR来进行,这样完全不需要客户端JS的存在,直接还可以省去加载客户端JS的时间,获得更优的渲染速度,这种优化后的Node.js截图详细流程方案如图6所示。
图 6 优化后的Node.js截图详细流程方案
该方案还有技术细节需要设计,即如何完成SSR渲染首屏的工作,由于我们的技术栈是Vue,所以选用vue-server-renderer为基础库来完成这一过程,如图7。
图 7 仅首屏SSR渲染方案技术流程
整体架构流程方案确定之后,我们就要进行更细化的业务技术流程设计,这里有很多细节需要考虑:
为了解决这些问题,我们设计了如下的流程,如图8。
图 8 Node.js截图服务业务技术流程
可以看到整个流程设计由接口同步请求和截图异步任务两大块组成。我们把Node.js截图服务当做一个纯渲染图片的服务,用户发起请求给后端服务时会携带渲染页面所需要的动态参数,后端服务则负责参数校验等工作,并且下发给用户一个异步任务ID。然后后端服务会请求截图服务,截图服务收到请求后并不会立刻截图,而是直接返回后端服务,并开启异步任务去进行耗时的截图服务,这样就可以防止长链接堆积造成服务不可用的情况发生。之前下发给用户侧的异步任务ID就是异步截图任务完成后通知后端服务的凭证,这样当截图服务完成后就可以通知后端服务截图完成状态(截图成功或者失败),而用户侧则可通过轮询后端服务接口得知截图是否完成并使用海报图片了,当然可以设置一个超时时间来完成整个截图服务的交互闭环。
对于海报图片的缓存层也是要考虑的,因为很多场景下用户请求的海报图片是一模一样的,比如我们的热榜海报会在固定时间生成一次,那么缓存层可以有效缓解截图耗时操作,并且为预生成海报图片提供了基础。比如我们会在特定时间通过代码自动预渲染一批海报,当第一个用户来访问时就不需要等待耗时的截图服务,直接返回渲染好的海报图片即可。那么命中缓存的条件是什么呢?对于长的一模一样的海报图片当然希望只生成一次后都走缓存层。我们的设计中决定“图片长相”的因素有两个:
1.海报地址:对应不同的海报样式
2.海报参数:对应同一个海报样式中的不同数据
所以,我们通过hash(“海报地址”+“海报参数”)的方式得到缓存命中的key,以此来控制命中缓存。另外截图服务生成海报图片后会直接上传到CDN进行存储,用户侧加载海报图片的速度和稳定性也得到了相应的保障。
在业务技术流程方案确定后,就需要为此搭建一套工程化开发环境,来支持项目业务的具体开发。我们基于公司现有基础设施以及技术栈,确定了以下主要技术选型:
如图9,展示了通过以上技术栈及基础设施组建的整个工程化方案。
图 9 Node.js截图服务工程化方案
这里需要说明是,一个海报图片对应一个页面,一个页面会有两个入口文件:一个CSR(客户端渲染)用于开发页面时使用,一个SSR(服务端渲染)用于截图时Puppeteer渲染页面使用。这么设计的原因是,CSR渲染使用webpack-dev-server是现成的开源方案,对于开发时热更新等支持不需要自定义开发,开箱即用,如果开发时也使用SSR进行就需要进行针对性的改造,由于开发体验上并没有区别,我们就选用了更高效地搭建方式。而截图时页面渲染方式就是上文提到的【仅首屏 SSR 渲染】。
从用户发起请求到海报图片返回,这整个过程的耗时需要进行“压榨”,以获得更好的用户体验。以下是我们在开发过程中实践和验证的相关重点优化和部分效果收益。从第一版基础开发到最后优化完成的版本,截图服务总用时从1300ms+降低到了600ms+(注:测试数据均取相同开发机10次执行结果的平均值,渲染相同的海报图片,图片为超长图且内容丰富,长宽为:4967×750)。表2为各关键优化点明细。
表 2 项目关键优化实践
为了方便使用海报图片的开发人员,我们还配套开发了海报生成系统在线文档。开发人员可以通过该系统查看现有的海报图片以及相关参数字段,并可以通过右方的编辑器更改字段的值并实时得到新的渲染图进行预览和下载。如图10,开发人员可以在这个playground里进行所见即所得的预览及操作。
图 10 海报在线文档预览系统
海报图片服务作为一个后端服务,日志采集分析和监控是必不可少的,我们可以通过日志得到以下信息:
所以,我们封装了3种log日志用于海报图片服务:
日志处理完成后,我们接入了公司现有日志基建服务来完成后续的日志采集、存储和分析等功能,如图11所示,通过这一套日志流程,我们就可以更加放心地上线并时刻关注我们服务的运行情况,轻松做到快速排查和分析问题。
图 11 海报系统日志服务
海报生成是一个耗时任务,其绘制速度依赖于服务器的CPU和内存。经过线上数据评估,截图服务qps最大支持60即可,经使用JMeter并发测试:
所以为了保证图片的生成速度和稳定性,使并发量少的时候图片生成速度尽可能快,并发量大的时候图片生成速度在可接受时间内,采用了多实例加多进程的部署方式,如图12,项目部署于DomeOS平台,部署了5台 (CPU4核 + 内存 6G) 实例,每个实例通过pm2启动4个进程。通过负载均衡可以使请求平均打到每个实例的每个进程上,让并发少的时候最快的生成图片,并发大的时候充分利用所有实例,加快整体生成图片时间。生成5000像素的图片,当并发数小于5时,图片可以在1s左右返回,当并发数为20,图片可以在3s左右返回,当并发数为60时,图片可以在8s之内返回。
图 12 海报系统部署方案
截止到2023年初,海报图片服务已上线海报10+个。每个海报只需开发1遍就可供给H5、小程序、Android和iOS共4端进行使用。每天平均生成海报图片6000+,每张海报图片平均生成时间400ms左右,支持超长图可达10000+像素。
随着项目迭代,海报服务未来可能有更大的需求诉求,下面列出海报服务未来进化的一些展望:
1、赋能外部开发人员
目前项目是以普通项目的开发模式进行研发,如果提供给非内部研发人员使用则有很多流程和规范上的问题难以解决。未来可以支持渲染远程组件,通过远程组件的方式下发给外部研发进行开发,以此隔绝海报服务核心逻辑和业务方逻辑,使得业务方只需关心业务,也防止业务方无意间可能影响到海报核心逻辑。此外,在整个过程中还可以增加审核远程组件等项目管理能力。
2、赋能非研发人员
为了海报图片渲染极具灵活性,我们把开发人员作为首要满足对象。未来除了开发人员可以开发海报页面,同时可以支持非研发人员通过拖拽编辑等低代码方式完成海报图片的生产,这样针对简单海报图片的场景,运营、产品等非研发人员也可以进行海报图片的制作,进一步提高生产效能。
作者:狐友FE
来源:微信公众号:搜狐技术产品
出处:https://mp.weixin.qq.com/s/D1N9qxHYGRBUlCGePvqj3w
之前做的 WordPress 插件,更多涉及的是管理功能,比如用户管理,分类管理这些插件等等,这些主要这些功能比较纯粹,然后 WPJAM Basic 已经实现了 WordPress 后台的各种底层类库和交互组件,只要功能需求就能快速做好。
但是最近在更新 Autumn Pro 5.0 的功能的时候,也开始有所涉猎 WordPress 的前端开发。 我就把一些常用的前端功能整理成统一的插件,一是方便日后更新,也是方便其他主题的集成。
今天就来介绍这批插件中的第一个,一个全能的 WordPress 弹窗插件,也可以叫模态框插件,目前已经实现了海报 / 打赏 / 分享 / 搜索弹窗,接下来还会实现登录 / 公告等弹窗,也提供弹窗接口,只要简单几行代码也可以实现你自己的弹窗。
插件激活之后,在后台「WPJAM」菜单下就又会有「页面弹窗」的子菜单,当然如果已经使用 Autumn Pro 主题,这个在菜单在「Autumn」菜单下,总之不在「WPJAM」菜单,就是在主题设置菜单下,点击进去就进入弹窗的设置页面了:
如上图所示,文章海报弹窗,需要你设置 logo 和标语,然后选择自动插入(Autumn Pro 主题已经通过代码插入,上面的自动插入选项也会没有),就可以在页面上显示「生成海报」按钮:
当然其他主题你也可以自己通过 wpjam_get_poster_button($args=[]) 来手工插入,这里 $args 的默认值是:['title'=>'生成海报', 'class'=>'poster-button', 'icon'=>'ri-image-line'],你也可以自定义修改它:
总之最后在页面上出现「生成海报」按钮,点击它之后就会生成海报弹窗:
样式还是可以的,当然你也自己改一下 CSS,去调整海报的样式,这里再多说两句,海报是通过 HTML2Canvas 生成图片的,这样做好处,就是无需在服务端去生成海报图片了,能够显著降低服务端的消耗,同时海报上的二维码也是使用 jQuery.qrcode 前端 jQuery 插件实现,也不是服务端生成的图片,更进一步降低服务器的消耗,本来这些功能就应该在前端实现的嘛,现在的浏览器能力也是越来越强了。
我们继续看会最上面的设置图,文章打赏弹窗需要设置「打赏标题 / 标语和收款码」这三个选项,之后就会出现「打赏作者」的按钮:
同样它的自定义调用函数是wpjam_get_donate_button($args=[]),$args的默认值是:['title'=>'打赏作者', 'class'=>'donate-button', 'icon'=>'ri-money-cny-circle-line']。
点击之后的弹窗如下:
文章分享弹窗没有设置选项,它的自定义调用函数是 wpjam_get_share_button($args=[]),$args 的默认值是:['title'=>'分享', 'class'=>'share-button', 'icon'=>'ri-share-forward-2-fill']。
点击生成的分享按钮之后弹窗如下:
最后一个是文章搜索弹窗,通过它可以在后台设置常用搜索词:
搜索按钮不能自动插入,它只能通过 wpjam_get_search_button($args=[]) 函数手动插入,$args 的默认值是:['title'=>'', 'class'=>'search-button', 'icon'=>'ri-search-line']。
比如 Autumn 的搜索按钮就如下:
点击它之后就可以进行文章搜索:
除了显示搜索框和后台设置的热门搜索之外,还显示当前用户的搜索历史,是不是有种大站才有搜索的感觉,哈哈。
日常的工作中(比如微信小程序),我们经常有这样的需求,就是需要使用程序生成推广海报,然后海报里要包含指定的二维码,这样用户分享出去别人扫码之后就可以确定用户推荐关系。
单独生成海报背景或者单独生成二维码通常还算比较简单,但如果要将两者结合到一起那还是需要花一点心思的。
那现在怎么解决呢?如何使用PHP在服务器生成推广海报呢?对的,使用PHP中的GD库。具体过程请往下看。
1、海报背景图。背景图一般存服务器,程序本地读取;
2、推广二维码。可以是二维码图片链接,也可以是字符串图像流。如果自己生成二维码,详见phpqrcode官网,地址:https://sourceforge.net/projects/phpqrcode。
3、开启PHP的GD扩展
首先熟悉一下几个关键的PHP函数:
1、getimagesize:取得图像大小。getimagesize() 函数将测定任何 GIF,JPG,PNG,SWF,SWC,PSD,TIFF,BMP,IFF,JP2,JPX,JB2,JPC,XBM或 WBMP 图像文件的大小并返回图像的尺寸以及文件类型和一个可以用于普通 HTML 文件中 IMG 标记中的 height/width 文本字符串。如果不能访问 filename 指定的图像或者其不是有效的图像,getimagesize() 将返回 FALSE 并产生一条 E_WARNING级的错误。
2、image_type_to_extension:取得图像类型的文件后缀。
3、imagesx:取得图像宽度。
4、imagesy:取得图像高度。
5、imagecreatetruecolor:新建一个真彩色图像。imagecreatetruecolor() 返回一个图像标识符,代表了一幅大小为 x_size 和 y_size 的黑色图像。是否定义了本函数取决于 PHP 和 GD 的版本。从 PHP 4.0.6 到 4.1.x 只要加载了 GD 模块本函数一直存在,但是在没有安装 GD2 的时候调用,PHP 将发出致命错误并退出。在 PHP 4.2.x 中此行为改为发出警告而不是错误。其它版本只在安装了正确的 GD 版本时定义了本函数。
6、imagecolordeallocate:取消图像颜色的分配。
7、imagefill:区域填充。imagefill() 在 image 图像的坐标 x,y(图像左上角为 0, 0)处用 color 颜色执行区域填充(即与 x, y 点颜色相同且相邻的点都会被填充)。
8、imagecopyresampled:重采样拷贝部分图像并调整大小。imagecopyresampled() 将一幅图像中的一块正方形区域拷贝到另一个图像中,平滑地插入像素值,因此,尤其是,减小了图像的大小而仍然保持了极大的清晰度。
9、getimagesizefromstring:从字符串中获取图像尺寸信息。同 getimagesize() 函数。 区别是 getimagesizefromstring() 第一个参数是图像数据的字符串表达,而不是文件名。
10、imagecopymerge:拷贝并合并图像的一部分。
11、imagejpeg:输出图象到浏览器或文件。
12、imagedestroy:销毁一图像。
熟悉完了这十几个核心方法后,我们就可以编码了。核心代码如下:
以上代码是利用PHP生成推广海报的主要部分,有了它我们就可以很容易的生成推广海报了。
参考案例:
1、生成带有二维码的海报。
2、生成带有图像,昵称和二维码的海报。
从以上示例可以看出,只要我们定义好核心代码之后,后面生成不同的推广海报,只需要调整config配置参数即可。
*请认真填写需求信息,我们会在24小时内与您取得联系。