Facebook 技术文集

Mcrouter:基于 Memcached协议的缓存层流量管理工具

共享连接池

后一项更新有效地消除了名为 like-gating的做法,该做法会强制Facebook用户在看到特定的自定义选项卡或应用程序上的内容之前喜欢一个Page。作为一种强制推行这一更新的方法,新的Facebook应用程序将不再获取页面上signed_request对象中的liked布尔值。此外,对于现有的应用程序,在90天内,该值将总是设置为true。

Facebook经验:如何保证不同网络环境下的应用体验

在团队方面,Facebook以产品为中心来组织团队。每个产品都对应一个单独团队,这个团队负责保证产品的性能以及可靠性。另外会有单独的核心团队负责发现、分析并解决性能方面的问题。核心团队和产品团队各司其职,互相促进。

由于地区的差异,Facebook用户的网络环境各不相同,所以访问Facebook应用的延时也各不相同。为了避免由于网络延迟而影响用户体验,Facebook主要做了三方面的工作,具体如下:

  1. 减小图片大小(使用WebP格式的图片)

对于一些不支持WebP的设备,Facebook会在客户端把它转码为JPEG格式的图片。

  1. 网络质量检测

Facebook在每个响应的的HTTP头中都提供了RTT(Round Trip Time)估算,客户端根据平均吞吐量和RTT时间来确定网络连接的质量。网络连接质量会大致被评为几个级别:优秀、良好、一般、差。开发人员针对不同的网络连接级别来调整相应的功能,比如增加/减少压缩调整并行的请求数量、禁止/允许自动播放视频、预加载内容。另外,Facebook还专门开发了Air Traffic Control以支持不同网络环境下的配置文件模拟。

  1. 预加载内容

预加载可以使用空闲时间来预先下载/加载用户接下来很可能会浏览的资源,在高延迟的网络环境下,内容预加载可以减少用户的等待时间。比如在网络连接不好的情况下,Facebook会在应用启动时加载基础数据。使用预加载内容方案时要注意不要让后台网络请求阻塞正常的网络请求,这块Facebook主要使用了一个优先队列来存储请求

How does Facebook Graph API share the same data structure between different microservices?

For example, when I request for an event:

GET /v2.8/{event-id} HTTP/1.1

it returns a field parent_group which is a nested Group structure;

When I request for a group:

GET /v2.8/{group-id} HTTP/1.1

it also returns a single Group structure.

But if the two requests are handled separately by two services Event-Service and Group-Service, how to ensure the Group returned by the two different services have the same fields?

Facebook移动端照片预览背后的技术

http://www.infoq.com/cn/news/2015/08/facebook-photo-preview

当在Facebook移动端上浏览某个人的用户资料或页面时,首先看到的往往是图片。这些图片是构成Facebook体验不可缺少的一部分,但有时候,图片的下载与展示非常慢,在低速或移动网络中尤其如此。而在像印度这样的发展中国家市场上,许多Facebook新用户主要是使用2G网络。近日,Facebook工程师Brian K Cabral和Edward Kandrot撰文描述了Facebook解决这一问题的过程。

封面照片是屏幕上最显眼的部分,但它也是加载最慢的部分之一。这主要有两个原因:一是封面照片的大小常常达到100KB,而2G连接的传输速度可能只有32KB/秒;二是应用程序需要发送两个网络请求才能显示封面照片。它首先向GraphQL服务器发送请求,获得照片URL,然后发送第二个网络请求,使用该URL从CDN获取照片。第二个网络请求的延迟相当长,比第一个长许多。

为了解决上述问题,他们希望能够由原照片生成一张200字节大小的效果图,然后将其作为GraphQL响应的一部分在第一次请求应答中直接返回,这样可以省掉第二次请求,极大地缩短用户资料和页首的显示延迟。当然,他们最终还是要从CDN下载完整照片并进行展示,但这可以在后台进行。至此,问题变成如何将照片压缩成200字节。

他们希望照片的效果图有一种磨砂玻璃的效果。这既有趣,又能与原始照片保持一致。磨砂玻璃效果采用高斯滤波器比较容易实现,而且图片越模糊,分辨率就越低,图片的尺寸就越小。不过,为了提供良好的用户体验,分辨率也不能太低。通过多次尝试,他们得出,42x42的图片可以达到他们想要的效果,而分辨率再高一些并不会带来更好的效果。但是,即使只显示图片的DC分量,每个像素仍然需要3个字节,那么42x42x3就是5292字节,远远超出200字节的目标。

他们开始评估标准的压缩技术,试图找出一种最好的方法,将数据压缩至200字节。遗憾的是,只是对图片进行熵编码(比如zlib)的话,只能将图片压缩一半,仍然太大。他们还评估了其它若干非标准技术,但最终,他们决定试一下JPEG图像编码。遗憾的是,JPEG头本身就有几百个字节的大小。不过,去掉JPEG头,编码的数据有效负荷接近200字节。

于是,他们开始探索,JPEG图片是否有可能使用一个固定的头,那样就可以将其存储在客户端,而不需要传输。JPEG头包含多个表。在Q值一定的情况下,量化表是不变的。通过试验,他们发现,Q20生成的图片可以满足他们的要求。虽然他们的图片不是固定尺寸,但基本上都限制在42x42以下。他们还仔细查看了JPEG头中的其它内容,发现只有Huffman表会随着图片的不同发生变化。Q值、图片数据及图片尺寸决定着Huffman表中的频率值,每一项变化都会导致不同的压缩比和有效负荷字节数。他们在一组图片上进行了试验,并最终找出了一个可以作为标准的Huffman表。

虽然他们处理了大量的图片,但总有一些该方案不适用的情况。为此,他们增加了一个版本号。如果发现任何极端情况,或者未来发现了更好的Huffman表,那么他们就可以更新相关图片的版本号,并将新表发送给客户端。最终的格式包含一个字节的版本号、一个字节的宽度、一个字节的高度和大约200字节的有效负荷。服务器只将这一格式作为GraphQL响应的一部分发送,然后由客户端将JPEG体附加到预定义的JPEG头上,生成一个普通的JPEG图片。经过标准的JPEG解码后,客户端可以运行预定的高斯模糊,并拉伸其尺寸以适应窗口大小。

最终,他们获得一种可以满足需求的格式。在网速缓慢的情况下,这帮助他们将用户资料和页面加载时间缩短了30%。而在网速非常快的情况下,这可以确保用户立即看到封面照片预览,提升了整体体验。