• 如何快速的合成各种样式的图片


    在某Q和某信中都有我们熟悉的公众号和结构化消息,例如

    又或者这样:

    这些图片都是固定的,每个用户看到的图片都是同一张。第一张,实在没有太多的点击欲望!第二张就还算凑合吧,不过哪来天天这么多福利图!

    如果想让每个用户看到的消息有所不同,因人而异,我们需要依赖终端做相应的开发。

    例如某某运动的消息:

    这种方式就比固定图片好很多了,用户的点击欲望明显加强。非常好!除了开发周期慢一点之外。

    呃,不妙,很快又要过阅兵节,然后是月饼节!老板想要做更炫酷的消息,怎么办?马上打电话给终端同学:“喂!喂?喂!3天能给我修改好模版吗?”。不过终端同学不是神,做得出来也发布不了。。。

    那么,我们聪明的产品同学就想到了“动态合成图片”的高招,马上找到我们前后台开发。我们一碰面,又马上敲定可以搞起,问题就是怎么搞起了。

    好啦,废话说完,该进入正题了。

    消息的样式:

    设计同学是毫不手软啊,真当这里是网页一样,各种设计各种字体各种效果各种进度条。

    项目情况:

    • 100万活跃用户
    • 2小时内推送200万个消息

    200万个消息也就是200万个图片,2小时内推送完成,也就是每秒大概300张图片。3ms一张图片?开什么玩笑?你以为每个服务器都有博尔特这么快吗!你要知道,这个图片并不小(630*350),要拼图,还要编码为JPG/PNG什么的。

    挑战是存在的,需求是要做的,任务是要完成的。我们开始研究合成图的各种现成的方案(那些从零开始实现各种尖端算法的思路就算了),包括:

    • C++图形库;
    • 浏览器截图;
    • Flash。

    什么?浏览器截图?什么?Flash?服务器生成图片,跟浏览器和Flash什么事?

    别急,我们慢慢说明。

    C++图形库:

    后台同学哪个不是精通C++,所以我们的后台同学就开始研究各种C++方案。列出来一大堆:Boost.GIL、CImg、CxImage、FreeImage、Magick++(ImageMagick)、GDCM、ITK、OpenCV、VIGRA、VTK。各种高级术语,吓你一跳。

    首先,尝试的是专业的Boost.GIL,但发现api羞涩难懂。后又转到街知巷闻的ImageMagick,很多重构同学都是用这个库做图片压缩。于是,后台同学浴血奋战,拼出了第一个效果:

    ,看来离目标效果不远了。虽然这给人感觉win10和win95的感觉,但要知道,win95升级4、5次就到win10了。

    不过,悲剧的还不是这个丑,悲剧的是,合成一张jpg一共需要耗时300ms!

    请允许我掐指一算,300ms一张,一秒3张,要达到一秒300张的目标,就需要100台机器。嗯,大老板这么有钱,应该不会介意的。

    好吧,开个玩笑,后台同学彻底放弃了。

    浏览器截图:

    为什么要想浏览器截图?其实以前在项目中用过,只不过当时并没有这么高的速度要求。毕竟设计稿就非常适合用网页实现,如果浏览器截图的速度能达到要求,那么做这个动态图片的成本就很低了。

    有很多linux命令行工具,可以对网页截图,原理是启动webkit渲染网页,然后截图。例如gnome-screenshot、wkhtmltoimage。

    实际情况是让人沮丧的,截图随便需要1秒2秒的时间。

    不过,这个也是能理解的,毕竟要启动webkit,网页要刷新,再截图,能不慢吗?

    Flash:

    笔者本身做Flash出身,所以对Flash生成图片情有独钟,既然如此,何不拿Flash测试一下呢?

    经过测试,我们发现Flash不单能轻松的完美复现设计的效果,而且截图效率非常高,最终也选择了这个方案。

    不过,要让Flash运行在linux服务器上,倒是要下一番功夫。

    研究的内容包括:

    • Flash player or Air?
    • Flash和C++的通信?
    • 高效压缩图片?

    #Flash player or Air?

    player和air只是swf运行的两种形式而已,对速度不会有影响。研究这个目的是尝试实现原来的通信架构,因为Air模式才能在flash侧运行ServerSocket。如果Flash能运行ServerSocket,那么Flash就称为服务提供者,C++需要合成图的时候,只需要连接socket,传输参数,然后接收图片即可。

    不过,Adobe于2011年宣布从air 2.7开始不再支持linux版本,所以否决了Air,还是继续使用Flash player。另外,要让flash正常运行起来,还需要安装xvfb服务。

    #Flash和C++的通信?

    为了保证高效的通信,避免每次截图都重启Flash player,我们设计了这样的通信机制:

    C++控制Flash的生命周期,定期重启Flash。Flash启动后,马上链接C++提供的socket服务。连接成功后,C++给Flash分配任务,传输相应的用户数据;Flash接收数据后,拉取用户头像、生成图片并压缩为JPG,再以二进制形式在socket中回传给C++。回传完毕后,Flash保持socket连接,等待新的任务。

    #高效压缩图片?

    图片动态拼接完成后,需要压缩为png或者jpg,又或者更多其他格式。当然,在当前的软件环境来看,jpg和png是唯二的选择了。

    我们做了很多测试,包括:

    • as3core压缩80%的jpg和无损png,也就是as3代码做编码运算
    • 改进版pngencoder:https://github.com/cameron314/PNGEncoder2
    • flascc(alchemy c++加速)压缩80%的jpg(as3_jpeg_wrapper)
    • png8和有损png24,使用的是blooddy(https://github.com/kenkozheng/blooddy),其中也有flascc加速。

    大致的情况如下:

    image

    综合文件大小和压缩时间,我们暂时选择了flascc压缩的jpg。

    但清晰度方面略有欠缺,80%质量的jpg在呈现文字时,边缘会略有模糊。不过这只在大屏机器上有细微的感觉。后续可能会考虑改为blooddy压缩的有损png24,虽然文件大小和耗时都比现有方案增加1倍,但图片要清晰一些。不得不赞扬一下blooddy的作者,俄罗斯人做软件要么就不做,要做就是很牛逼的。

    有一个基础数据还没列出,就是Flash拼接生成画面的时间。这个倒是快的惊人,不算加载图片的时间,只需要8ms左右。

    那么最终,Flash生成一张图的时间大概就是40ms。

    最后,请再允许我掐指一算。默默的打开计算器。。。

    40ms*2000000/1000 = 80000s = 22.2小时

    那么如果同时有10台机器,就大概可以在2小时内发送完成了。虽然100台机器搞不到,10台机器还是有办法的

    当然,实际情况还有图片传输的耗时(实际上这里更大,要传输到公众号平台),实际需要200ms一张图片,不过这些都是异步的,我们单机启动25个Flash进程,总体运行平滑。

  • 相关阅读:
    解决DataGridView绑定List后不能排序的问题
    最新的皮肤下载
    我收录的名言
    HttpRequest访问Https带有证书并使用WSDL文档生成代理类方案(2)
    最新的Functions 类
    华兴软通短信接口简单使用WebServices版
    最新的SqlHelper 类
    闲来没事写个记事本玩玩!!!
    "基础连接已经关闭: 未能为 SSL/TLS 安全通道建立信任关系"证书验证失败的解决过程(3)
    FCK配置
  • 原文地址:https://www.cnblogs.com/kenkofox/p/4762439.html
Copyright © 2020-2023  润新知