手淘图片库新特性解析
云移2020-10-15

随着手淘拉新和用户体量的增加,CDN图片资源的访问量也随之增加。我们知道访问量增加,会带来带宽的增加,服务器成本也随之增加。同时新增的用户,喜好各不相同,为了满足不同用户的喜好,商家会修改主图样式来吸引消费者。如何缓解用户增长带来的带宽压力和满足商家和用户的多样化需求呢?针对这两个问题,图片库和图片空间同学对客户端和服务端做了相应的改造来解决这些问题,主要的做法是H5HEIC化以及图片库磁盘缓存标准化。


HEIC的应用


HEIC是什么


HEIF是一种图像容器格式,它所生成的图像文件相对较小,且图像质量也高于较早的JPEG标准。HEIF这种新的图像格式基于高效视频压缩格式(也称为HEVC或H.265),它通过使用更先进的压缩算法来实现图片的压缩存储。



手淘NativeHEIC化



从2018年开始手淘的native页面已经全面支持HEIC,为CDN侧节省了大量流量,优化了用户的图片体验。native页面可以HEIC化需要满足两个条件:


  1. 业务的图片是存放在手淘图片空间,才能够使用图片空间的图片处理能力。
  2. 业务需要使用图片库的适配库来对原始url进行适配,然后再借助图片库的能力完成图片的加载。


目前从统计来看淘内的图片40%已经使用了HEIC,为什么只有40%的量?一方面是因为手淘部分业务的图片不是放在图片空间做处理,比如有的业务使用TFS来做存储,另一方面是手淘使用的H5内核目前无法解码HEIC图片。说到这里需要简单介绍下手淘的图片的业务流程:

image.png

image.gif以上只是简单的示意,大体展现图片如何从商家流动到用户的,主要的中转便是图片空间它可以对图片进行处理和转化。用户使用手淘客户端访问页面时,业务会将商品图片url传递给图片库,图片库会对传入的url做处理,按照图片空间的规则拼接上HEIC后缀,这样当url到达CDN时,如果有HEIC图片缓存直接返回,如果没有会请求图片空间的服务将原始图片转码成heic图片。以上是native的流程,我们还忽略了另一个流量来源H5,目前手淘H5容器无法使用HEIC解码器,所以没有进行HEIC化。


H5页面HEIC化



为什么今年可以做H5页面HEIC化了呢?主要是以下两个方面:


  1. 内核提供了外接三方解码器的能力
  2. 手淘支持远程下载so


有同学可能会好奇,之前提到的图片空间是什么?图片空间可以看作是提供强大图片处理能力的服务。从上一小节的流程图可以看出CDN后面是图片空间,这里简单介绍下图片空间和CDN之间的关系。当有图片请求访问到CDN时,CDN会查找当前缓存中有没有这个请求需要的图片,如果有直接从CDN中返回,如果没有CDN会去请求图片空间处理原图给出符合要求的图片,然后再返回给CDN,同时CDN会对这张图片做缓存,当下次有相同的请求过来时就直接返回缓存的图片,不再回源到图片空间,缩短请求响应时间。


首先介绍下UC内核外接三方解码器能力,这个能力是如何实现的呢?简单来说就是提供实现UC规定接口的so文件(保证规定的函数符号会被正确导出),UC内核会使用dlopen打开so,同时获取接口函数的函数指针,当需要解码时直接使用函数指针直接调用即可。下面是大体的调用流程以及相关的接口定义:


简短介绍下上图蓝框的流程:image.gif

image.png

  1. Init:初始化图片解码器,并将相应的数据结构透传到UC
  2. Decode:UC会透传相应的数据到解码器,解码器解码完成后将相应的RGBA数据给到UC,UC最终将解码后的数据给到渲染层
  3. Close:回收初始化过程中创建的系统资源


之前从事过PC开发的同学可能会熟悉这这种做法,PC插件化大多是利用dll(可以理解为Linux下的so)来实现,这在PC时代是常用做法,Python调用C++/C代码也可以通过封装为so进行调用。针对UC提供的这种能力,图片库对原有的HEIC解码器做了封装,实现了上面的接口规范,提供给UC调用,这样H5解码HEIC的问题就解决掉了。


真的这么顺利么?答案是No。手淘目前对so集成时的大小有限制,刚好HEIC的32位和64位so加起来将近4.5MB,这已经是严重超标了,是不可以接受的。


那该如何解决呢?架构组提供了远程加载so组件(上图中的远程下载),可以将so放在远程,不需要打进apk包,当需要的时候从远程下载,同时HEIC解码so是独立的没有依赖,直接dlopen打开就可以使用。这样我们的解码功能就顺利完成了,剩下的工作就需要Windvane同学添加相应的降级逻辑和监控逻辑提供必要的稳定性保障,到这里这个H5支持HEIC功能就完成了。


说了这么多从native支持HEIC到H5支持HEIC,收益是什么呢?有两个方面,一个是端上的收益,一个是服务端收益:


客户端


目前端上的计算能力已不是瓶颈,主要是网络的通信时间,图片体积越小,网络耗时越少,同时网络耗时的减少可以抵消端上解码耗时的增加,总体而言图片的加载性能会提高。同理对于H5页面也一样,网络耗时越少,页面性能也会有所提高。


服务端


对于服务端而言,图片的体积更小,占用的带宽也更小,单位时间内处理的请求增加,简单来说就是提高了QPS。


H5支持HEIC这个功能已经灰度了几个版本,后续会逐步全量,对于H5页而而言,页面性能也会得到提升。我们可以先参考下native页面下HEIC图片和WEBP图片的下载时间和解码时间的对比数据:


Format网络时间(ms)解码时间(msHE)
HEIC14127.5
WEBP64530



image


数据说明:样本数在几十万级别,基本可以抹平图片尺寸带来的统计差异。


从上面的数据也可以表明,网络时间的优化可以抵消部分解码时间的增加,有的同学看到数据会有疑问,解码时间并没有增加反而有所优化,这主要是多媒体算法团队对HEIC解码做了优化,同时图片空间空间同学对HEIC的封装做了优化(优化的后的封装,仍然是符合HEIF文件标准的)。


图片库缓存标准化



图片库磁盘缓存标准化


用过Phenix图片库或者三方开源图片库的同学都知道,图片库会有三层缓存(内存、磁盘、网络),磁盘缓存一般是持久化的,除非超过磁盘缓存大小被LRU淘汰掉,为了满足图片空间的需求,图片库对磁盘缓存做了改造。大体流程如下:


image


简单来说图片库的磁盘缓存可以指定过期时间,过期后图片请求会打到后端。图片空间会在图片响应头里添加过期字段来控制本次请求到的图片在磁盘缓存中存在的时长。目前实现的缓存控制是在原先LRU基础上实现的,即首先会判断这张图片有没有被LRU淘汰掉,如果没有被淘汰掉会走到缓存控制逻辑,如果已经被LRU淘汰,保持原有逻辑。有同学可能会疑惑这么做有什么意义的呢?之前LRU策略图片更新时间是不确定的,有可能很快就过期了也有可能要等几十小时才能过期,完全取决于用户访问的频次以及用户的操作习惯,过期时间字段可以精确到小时级别,让图片及时过期,请求到最新的图片。


URL不变更新图片


看到这个标题很多同学可能会有疑惑,URL不变更新图片是什么意思?简单来说就是访问相同的图片url图片的内容可能不同,经过这么一解释有的同学可能更加疑惑,为什么要这么做?


带着这个疑问我们来解释为什么会有这样的功能,促销对手淘来说比较常见,商家也会抓住这个机会尽可能吸引消费者去下单,提高成交量。图片作为手淘商品的主要信息载体,有活动的时候商家为了能够传递更多的信息给消费者,商家会频繁修改商品主图的促销样式和信息来吸引消费者去点击,更多的点击意味这更高的流量,从而带来更高的成交量。商家通过图片空间修改商品主图,图片空间会将修改后的主图URL更新到商品服务,但是这给商品服务带来了挑战,手淘承载着成千上万商家,如果促销期间大量商家去更新商品主图URL,对商品服务带来的压力不可小觑(不止主图更新会调用商品服务,其他业务也会去调用),因此在大促期间为了保证商品服务的稳定性,会进行必要的限流。大体流程如下图:image.gif

image.png

这样商家在特定的时间是不能及时修改商品图的,这会导商家和消费者的体验下降,因为主图的促销信息不能及时更新,导致主图显示的优惠信息和实际购买的优惠信息有可能会有偏差。这该如何解决呢?活动期间商家改图后商品主图URL不变,图片空间会将修改的图片更新到CDN,不再去频繁刷商品服务,商品服务的压力也会减小,消费者的体验也得到了保障。修改后的流程如下图:


image


有同学可能会有疑问,为什么不是提升商品服务的性能呢?以下是我简单的分析,不一定准确:数据库的写性能是有限的,无论再怎么优化再怎么加机器,大量的并发过来总会达到瓶颈。有的同学可能会说,分布式、分库、一致性哈希,或许可以做也可以解决问题,可是大促是短期行为,做这么大的改造耗费大量的人力成本,但是对非大促,这样的改造没有收益,同时也会增加系统的复杂度。


通过这样小成本的改造,将一部分流量转移到CDN侧,减轻了商品服务的压力同时也能提升商家和消费者的体验,商家也可以随时更新商品主图。


说了这么多图片库做了什么?一开始介绍的图片库磁盘缓存标准化,大促期间图片空间会在下发图片的Response中带上过期时间字段,设置一个合理的过期时间,便于大促期间商品主图的磁盘缓存隔一段时间过期一次,过期后请求新的图片,这样用户看到的图片始终是最新的,虽做不到实时但也可以达到小时级,能够满足业务的诉求。


总结



今年图片库的主要改动有两个方面:H5支持HEIC化以及图片库磁盘缓存标准化,这两个改造从本质上来说是将服务端的优化通过端上整合触达到用户。HEIC图片native端在2018年就已经开始支持,但是由于H5容器UC内核当时没有HEIC解码能力,所以H5页面没有使用HEIC,今年随着UC对外提供了三方解码插件的功能,利用这个契机将H5页面进行HEIC化。图片库磁盘缓存标准化。