Box 迁移到 HHVM 实践

澳门新葡亰娱乐在线 6

引擎和扩展的变化

在Badoo中, 我们有积极的支持和更新的PHP分支,我们在PHP7正式版release之前我们就已经开始切换到php7了. 所以我们不得不在我们的代码树经常整合(rebase)PHP7上游的代码,以便它来更新每个候选发布版。我们每天在工作中所用的补丁和自定义的code都需要在两个版本之间进行移植。

下载和构建依赖库、扩展程序、还包括PHP 5.5和7.0的构建这些过程都是自动化的完成的。这不仅简化了我们目前的工作,也预示着未来:在版本7.1出来时,
也许这一切(解析引擎和扩展等等)都已经准备到位了;

如上所述,我们将注意力转向扩展。我们提供超过70种扩展,已经比基于我们产品改写的开源产品的半数还要多。

为了尽快能够切换到它们,我们已经决定开始同时进展两件事情。第一个是逐一重写各个关键扩展,包括blitz模板引擎,共享内存/APCu中的数据缓存,pinba数据分析采集器,以及其他内部服务的自定义扩展(总的来说,我们已经通过自己的力量完成大概20种扩展的重写了)。

第二个是积极的清理仅仅在架构中那些非关键部分使用的扩展,让整个架构更加简洁。我们已经迅速清理了11种扩展,都是那些无足轻重的!

另外,我们也同那些维护主要开放扩展的作者,一起积极地讨论PHP7的兼容性(特别感谢xdebug的开发者Derick
Rethans)。

我们迟点将进入更详细的关于移植PHP7扩展的技术细节。

开发者已经对PHP7中的内部API做了大量修改,意味着我们可以修改大量的扩展代码了。

下面是几个最重要的变更:

  • zval * ->
    zval。在早期的版本中,zval一直为新变量来分配内存,但是现在引入了栈。
  • char * ->
    zend_string。PHP7的引擎使用了更先进的字符串缓存机制。理由是,当字符串与自身的长度同时存储时,新的引擎可以将普通字符串完整的转换为zend-string格式。
  • 数组API的改变。zend_string作为key来使用,同时基于双向链表的数组实现方法也被替代为普通的数组,需要强调的是,数组占用一个大的文件块,而不是很多小的空间。

所有这些都可以从根本上减少小型内存分配的数量,结果是,提高PHP引擎2%的速度。

我们能够注意到,所有这些修改都至少需要改变所有的扩展(即使不是完全重写)。虽然我们可以依赖内置扩展的作者进行必要的修改,我们也当然有责任自己修改他们,虽然工作量很大。由于内部API的修改,使得只修改一些代码段变得简单。

不幸的是,引入使代码执行速度提升的垃圾回收机制让引擎变得更加复杂并且变得更加难以定位问题。涉及到OpCache的问题。在缓存刷新期间,当可用于别的进程的已缓存的文件字节码在此时损坏,就会导致崩溃。这就是它从外部看起来的样子(zend_string):使用方法名或者常量突然崩溃并且垃圾就会出现。

鉴于我们使用了大量的内部扩展,其中许多处理都是专门针对字符串的,我们怀疑这个问题与如何使用字符串在内部扩展有关。我们写了大量的测试,并进行了大量的实验,但没有得到我们预期的结果。最后,我们从PHP引擎开发人员 Dmitri
Stogov 那里寻求了帮助。
他的第一个问题是“你有没有清除缓存?”我们解释说,事实上,我们每一次都在清除缓存。在这一点上,我们意识到这个问题并不在我们这里,而是opcache。我们很快就转载了这一案例,这有助于我们在几天内回复并解决这个问题。在7.0.4版本,这个修复没有出来,就不可能使php7进入稳定产品。

Box中的PHP

在 Box 里,PHP
是开发栈的核心部分。尽管我们在大量的后台服务中使用了许多语言,然而每个后台服务都要求我们首先和
PHP web 应用进行交互。从 Box 诞生那一天开始,我们产品的核心功能都是使用
PHP 来实现的。

减缓由超过150个活跃贡献者编写的和超过75万行代码组成的还在不断增长的
PHP代
码所带来的延迟是最大的挑战。我们不能对我们提供服务的大部分页面进行缓冲处理,因为用户通常希望
Box
上的各种各样的动作都是原子性的。随着我们产品的不断成长、演变,这本身就会增大延迟。我们曾经一直投入巨大的努力来减少延迟,然而似乎一直没有找到好的办法。我们重构了旧的、效率低下的代码;把一些自身可独立做为组件的提取出来做为
PHP
扩展;这样就会大量地缓冲请求时可共享的保持不变的状态,然而,所做的一切都只是微微地减少了延迟,所取的效果很容易由新的功能来替代性的实现。然而自去年我们花时间对
HHVM 进行全面评估开始,这一切就有所改变。

澳门新葡亰娱乐在线 1

获得同等的功能

PHP
是一个非常庞大的语言。仅它的核心运行环境就包含大量的函数,配置设置和大量的类,这些都是十多年的社团贡献累积而来的。这甚至还不包括大量的
PHP 扩展,所有这些扩展也必须移植到 HHVM 上。让 HHVM 具有与默认的
PHP解释器几乎完全相同的功能本身就是惊人的重大壮举。我们试图说明这两个运行时环境行为上的差异。

我们发现大量的运行时差异是在 PHP
中几乎不经常使用的地方。其中一些差异是极易发现的错误,通过单元测试就可发现错误。而一些差异则是隐藏很深的漏洞,这些漏洞可引起非常严重的后果。HHVM
每天都会接近 PHP
解释器所提供的功能,然而移植这么庞大的代码库必然会使更多的行为上的差异浮出水面。自动测试是一种最安全的保障措施,它可以排除那些影响到用户功能的运行时差异。要让
HHVM 通过我们的 PHPUnit
测试套件是要花大力气的,即要进行许多修补才能获得同等功能。手工测试则是另一种必不可少的保障措施,尤其可以找到外部服务和
HHVM 之间交互时出现的错误。在 HHVM
使用在生产环境前,我们通过自动测试和手工测试混合的方法发现了大量功能存在差异的地方。

对 HHVM 运行时环境差异的修补过程非常有趣。HHVM
社团非常活跃,在很短的时间内就可以在
GitHub
或者 HHVM 的 IRC
聊天室获得帮助。HHVM
的代码库充分利用到了现代C++的各种构件,同时理解和给代码库做出贡献也相对容易多了。在发布
HHVM 之前,我们贡献了大约 20 个用于解决功能不等同问题和增强功能的补丁。

响应时间分布:

修正部署

HHVM运行在最佳性能时有两点与众不同的,这两个不同点迫使我们重新思考了一下我们PHP应用的部署方式。第一个不同点是HHVM需要对新编写代码“热身”以后才能达到最佳性能。由于HHVM是一个即时编译器(JIT),因此需要对新编写的代码运行几次后,才能收集到足够的信息,进而对这些代码进行转换,最终达到有效装配。实际上,在对当前请求服务之前,这样的转换是发生在curl请求的几个重大节点上的。第二个不同点是规律性地重新启动HHVM以达到最佳性能。这使得升级HHVM就容易多了,同时也是一个保障HHVM和其连接库出现内存泄漏的好方法。要满足上面两点需要对Apache通常的PHP部署稍作调整。

以前我们是通过Apache
web服务器单个实例为整个站点提供服务的,同时我们通过转换指向当前代码库的符号链接来实现多个代码库之间循环访问的。现在,我们使用三个HHVM实例为各种请求提供不间断的服务。通常情况下,其中一个HHVM实例为当前应用的所有请求提供服务,另外两个实例做为备用,为以前的应用和以前的以前的应用提供服务(见下图)。每个HHVM
web服务器都指向一个绝对路径,因此当我们需要部署新版本的时候,我们只要停止指向旧版本的web服务器,启动指向新版本的服务器,并使用多个curl请求对代码库进行热身,再重定向请求就可以了。这种部署方式满足了上段提到的两个不同点,这样可以很容易地实现回滚和前期测试。要回滚到以前版本,我们可以把请求重定向到提供以前版本代码服务的HHVM实例上(注意这个HHVM实例已经在运行)。要对新部署的应用进行前期测试,我们只要把一部分请求路由到新的HHVM实例上,测试一直持续到我们确信新的部署稳定时为止。这种部署已证明非常强大:在生产环境下可以处理大容量的请求。

澳门新葡亰娱乐在线 2

常见的HHVM服务器

澳门新葡亰娱乐在线 3

部署代码后的HHVM服务器

澳门新葡亰娱乐在线 4

这一切到位,处理时间减少了一半,从而提高整体响应时间约40%,由于一定量的请求处理时间是花在与数据库和守护进程通信。从逻辑上讲,我们不希望这部分加快切换到php7。除此之外,由于超线程技术,集群的整体负载下降到50%以下,进一步促进了令人印象深刻的结果。广义而言,当负载增加超过50%,HT-engines,而不是作为有用的物理引擎开始工作。但这已经是另一篇文章的主题。此外,记忆的使用,这从来没有一个瓶颈,我们,减少了大约八倍以上!最后,我们节省了机器的数量。换句话说,服务器的数量可以承受更大的负载,从而降低获取和维修设备的费用。在剩余的聚类结果相似,除云上的收益是一个更温和的(大约40%个CPU),由于opcache操作的减少。

来算算我们能节省多少费用呢?大致测算一下,一个Badoo应用服务器集群大概包含600多台服务器。如果cpu使用率减半,我们可以节省大约300台服务器。考虑服务器的硬件成本和折旧,每台大约4000美元。总的算下来我们能节省大约100万美元,另加每年10万的主机托管费。而且这还没有计算对服务云性能的提升带来的价值,这个结果很令人振奋。

另外,您是否也考虑切换到PHP 7.0版本呢?
我们很希望听听您关于此问题的观点,而且非常愿意在下面的评论中回答您的疑问。

Badoo 团队

有时候,我们会听说关于一些公司采用 Facebook 的开源项目的事情。Box
团队近期给我们发送了他们是如何使用 HHVM
的故事,是一个很好的文章。所以我们把他贴在这里,
我们感谢他们以这种方式发给我们.。我们也会寻求反馈意见.。你们可以在Facebook
Engineering 主页
或者在 GitHub联系到我们。

By Joe Marrama, class=”wp_keywordlink”>软件工程师,Box团队

介绍

我们成功的把我们的应用迁移到了php7上面(数百台机器的集群),而且运行的很好,据说我们是第二个把如此规模的应用切换到php7的企业,在切换的过程我们发现了一些php7字节码缓存的bug,庆幸的是这些bug现在已经被修复了,现在我们把这个激动人心的消息分享给所有的php社区:php7现在已经可以稳定的运行在商用环境上,而且比以前更加节省内存,性能也有的很大的提高。

澳门新葡亰娱乐在线 5

下面我会详细的介绍下我们是如何把应用前移动php7的,我们在这中间遇到的问题及处理情况,还有最终的结果。但首先让我们回头看看一些更常见的问题:

Web项目的瓶颈在于数据库持久化这是一个常见的误解。一个设计良好的系统应该是平衡的:当访问量增长时,由系统的各个部分分摊这些压力,同样的,当达到系统阀值时,系统的所有组件(不仅仅包括硬盘数据库,还有处理器和网络)共同分摊压力。基于这个事实,应用集群的处理能力才应该是最重要的因素。在很多项目中,这种集群由数以百计甚至数以千计的服务器组成,这是因为花时间去调整集群的处理能力更加经济实益(我们因此节省一百多万)。

PHP的Web应用,处理器的消耗跟其他动态高级语言一样多。但是PHP开发者面对着一个特别的障碍(这让他们成为其他社区恶意攻击的的受害者):缺少JIT,至少没有一个像C/C++语言那样的可编译文本的生成器。PHP社区无力在核心项目框架上去实现一个类似的解决方案更是树立了一种不良的风气:主要的开发成员开始整合他们的解决方案,所以HHVM在Facebook上诞生了,KPHP在VKontakte上诞生,还有其他类似的方案。幸运地是,在2015年,随着PHP7的正式发布,PHP要开始”Grow
up”啦。虽然还是没有JIT,但很难去评定这些改变在”engine”中有多重要。现在,尽管没有JIT,PHP7可以跟HHVM相匹敌( Benchmarks
from the LightSpeed
blog  or PHP
devs
benchmarks)。新的PHP7体系架构将会让JIT的实现变得简单。

澳门新葡亰娱乐在线,在Badoo的平台开发者已经非常关注近些年出现的每一次问题,包括HHVM试点项目,但是我们还是决定等待很有前途的PHP7的到来。现在我们启动了已经基于PHP7的Baboo!这是一个史诗般的项目,拥有300多万行的PHP代码,并且经历了60000次的测试。我们为了处理这些挑战,提出了一个新的PHP引用测试框架(当然,也是开源的),并且在整个过程中节省了上百万美元。

HipHop 虚拟机(HHVM)

HHVM 是由 Facebook 牵头开发的开源 PHP 解释器。它诞生初期是做为 PHP 到
C++ 的编译器,是对 Facebook 的 PHP
代码库进行大量的裁剪基础上产生的,不过,最近几年它已经成长为一个即时(JIT)编译器。简言之,即时编译器就是以统一的方式对经常需要执行的
PHP 代码块进行编译并装载。演进为即时编译器也使得 HHVM 获得了与通常 PHP
解释器几乎相同的功能,同时 HHVM 现在还支持更多 PHP
语言的动态机制。例如,旧的 PHP 到 C++ 的编译器就无法运行 PHP
的”eval”语句(”eval”是把字符串当做 PHP 代码来执行,而 C++
不支持这样的功能),而新版本的 HHVM 就可以。由于 HHVM
已经成长为即时编译器,因此它已经逐渐大量替换了标准的 PHP 解释器。

一年多以前,我们就留意到 HHVM 团队把大量的精力都集中在获得与普通的 PHP
解释器同样的功能上。过去,我们曾经对 HHVM 进行评估,不过证明要让 HHVM
正确地运行我们开发的 web
应用非常困难。然而,这次我们重新迎接这一挑战,对 HHVM
进行全面评估,确定它在延迟方面的效果。把 HHVM 合并到我们的开发栈里和让
HHVM
运行我们部分代码是一项非常重要的任务,不过潜在回报很快证明我们的努力是值得的。我们最初的试验显示:HHVM
运行一个核心端点要比默认的 PHP 解释器快四倍多。

这标志着为期一年的把我们的产品安全地移植并运行在 HHVM
上的奋斗开始了。在移植过程中,我们在开发栈的许多地方都遇到各种各样的挑战。我们遇到大部分重大困难其他人在运行时也会遇到。在接下来的一部分,我将详细说明运行
HHVM 时通常遇到四个难点:解决存在在 HHVM
和默认的解释器之间的意想不到的不兼容性;回避二者之间可预料的不兼容性;对
PHP 的部署进行修补;确保在这一混合环境下一切可以非常良好的运行。

澳门新葡亰娱乐在线 6

现存的混合状态

毫不奇怪,迁移到 HHVM
过程中最危险的部分是将其推出应用。没有足够的测试以确保 HHVM
在生产环境中可以完美处理所有请求并与后续系统完美配合。HHVM
在预生产环境中的重度测试中表现良好,但是我们任然对此表示怀疑。此外,我们不能提供一个独立的只读生产环境供
HHVM 测试,而且我们不能容许有任何的停机时间。对我们来说,应用 HHVM
唯一可行的方式是具有长期,充分观察的实验过程的可控方式。这需要使 HHVM
运行在与 Apache 和默认 PHP
解释器相同的环境中。只有在这种情况下最终存在,才可以使我们成功放出
HHVM,而不会使 HHVM 对用户有负面影响。

我们的 PHP
代码库与大量的不同后端系统交互。幸运的是,绝大多数交互的发生是通过 curl
对内部 REST
APIs 的请求,curl
是经过非常充分测试和稳定的 HHVM curl 扩展。我们使用 PDO
扩展来与我们的
MySQL 数据库服务器交互,同样表现出了与默认 PHP
解释器相同的功能行为。可能有潜在麻烦的后端系统是
Memcached,为了确保两个运行时中完整的互操作性,两个运行时都必须具有功能相等的
Memcached
扩展而且都必须是相同的序列化对象。如果任何一个运行时的序列化对象有不同行为,在另一个运行时中从
Memcached
中回复对象序列化在不同格式时就会轻易造成严重破坏。我们在混合环境中进行了众多的实验以确保两个运行时都不会毒化
Memcached
或任何其他后端存储。所有事情都表现的很好,除了一个小问题:ArrayObjects。在标准的
PHP 解释器中,实现 ArrayObject 的扩展定义了一个自定义的序列格式,然而
HHVM 中只使用标准的对象序列。我们需要在我们的应用中禁用 ArrayObjects
缓存以确保 Memcached
的互操作性。幸运的是,我们在推出之前或过程中不会再遇到任何其他的互操作性问题。

在上线过程中,一个非常有用的处理工具就是我们简单到爆的主机转换法。尽管使转换到
HHVM
的过程尽可能简单是一件再明显不过的事,但它任然值得我们在所有场合拿出来炫耀一下。我们决定通过
puppet 将 HHVM 部署在一个 host-by-host 基础上,转换过程是通过 puppet
中可以由主机名调整的一个标志控制的。主机转换到 HHVM
和回滚的操作仅仅需要调整标志的条件,不需要从负载平衡器上移除主机,也不需要做其它任何事。完整的回滚操作可以五分钟内完成,多主机一次性快速迁移和回滚是必须的。

上线过程中我们所有网络应用的登录和监控系统是至关重要的,但是有一种形式的监控被证明特别有用;监控
HHVM 错误日志以得到新的错误。我们维护了一个内部数据库,它包含有我们从
Apache 错误日志中观测到的所有独特的 PHP 错误。当上线 HHVM
时,这个系统告诉我们所有发生在 HHVM
上的新错误,通过对错误进行分类,并使用 PHP
错误数据库判断其是否是一个之前存在的错误。这使我们更多地了解到 HHVM
是否导致了任何新的倒退。

HHVM的试验

在切换到PHP7之前,我们曾花了不少时间来寻找优化后端的方法。当然,第一步就是从HHVM下手。在试验了几周之后,我们获得了值得关注的结果:在给框架中的JIT热身之后,我们看到速度与CPU使用率上升了三倍。

另一方面,HHVM 被证实有一些严重的缺点:

  • 部署困难而且慢。在部署过程中,你不得不首先启动JIT-cache。当机器启动的时候,它不能负载产品流量,因为所有的事情进行的相当慢。HHVM
    团队同样不推荐启动并行请求。顺便一提,大量聚类操作在启动阶段并不快速。此外,对于几百个机器构成的大集群你必须学习如何分批部署。这样体系结构和部署过程相当繁琐,而且很难估算出所需要的时间。对于我们来说,部署应该尽可能简单快捷。我们的开发者将在同一天提供两个开发版并且释出许多补丁。
  • 测试不便。我们非常依赖runkit扩展,但是它在HHVM中却不可用。稍后我们将详细介绍runkit,但是无需多言,它是一个能让你几乎随心所欲更改变量、类、方法、函数行为的扩展。这是通过一个抵达PHP核心的集成来实现的。HHVM引擎仅仅显示了略微相像的PHP外观,但是他们各自的核心十分不同。鉴
    于扩展的特定功能,在HHVM上独立地实现runkit异常困难,而且我们不得不重写数万测试用例以确保HHVM和我们的代码正确的工作。这看起来似乎不
    值得。公平的说,我们以后在处理所有其他选项时也会遇到同样的问题,而且我们在迁移到PHP7时仍然要重做许多事情包括摆脱runkit。但是以后会更多。
  • 兼容性。主要问题是不完全兼容PHP5.5(参考此处)
    ,并且不兼容现有的扩展(许多PHP5.5的)。这些所有的不兼容性导致了这个项目的明显缺点:
    HHVM
    不是被大社区开发的,相反只是Facebook的一个分支。在这种情况下公司很容易不参考社区就修改内部规则和标准,而且大量的代码包含其中。换句话说,
    他们关起门来利用自己的资源解决了问题。因此,为了解决相似的问题,一个公司需要有Facebook一样的资源不仅投入最初的实现同样要投入后续支持。这
    个提议不仅有风险而且可能开销很大,所以我们决定拒绝它。
  • 潜力。尽管Facebook是一个大公司而且拥有无数顶尖程序员,我们仍然怀疑他们的HHVM开发者比整个PHP社区更强。我们猜想PHP的类似于HHVM的东西会很快出现,而前者将慢慢淡出我们的视野。

让我们耐心等待PHP7。

切换到新版本的PHP7解释器是一个重要和艰难的过程,我们准备建立一个精确的计划。这个计划包括三个阶段:

  • 修改PHP构建/部署的基础设施和为大量的扩展调整现有的code
  • 改变基础设施和测试环境
  • 修改PHP应用程序的代码。

我们稍后会给出这些这些阶段的细节。

减少延迟和增加我们的基础设施的能力一直是 Box
最优先考虑的问题。我们努力以最有效的方式提供最好的用户体验,并且以前我们的
PHP
还选择不与这些目标一致。我很高兴地说,对于这两个目标我们最近取得了非常显著的进步,成功的部署了
HHVM(HipHop虚拟机)作为我们 PHP
代码的独家引擎服务;在这篇文章的其余部分,我将详细介绍如何使用PHP,如何使用HHVM,我们所面临的挑战是HHVM迁移,和提供卓越的性能。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

相关文章

网站地图xml地图