将其推向极限- Symfony2的高性能需求ob娱乐下载
这个案例研究是由Antoni Orfin(联合创始人和软件架构师)撰写的客座文章Octivi.想让你的公司出现在Symfony官方博客上?ob娱乐下载发送一个建议或案例研究到fabien.potencier@sensiolabs.com
对于大多数人来说,使用全栈框架会降低网站的速度。在Octivi,我们认为这取决于为特定项目正确选择合适的工具。
当我们被要求为我们的一个客户优化网站时,我们从头开始分析了他们的设置。结果是:将它们迁移到面向服务的体系结构,并将其核心业务系统提取为单独的服务。
在这个案例研究中,我们将揭示10亿Symfony2应用程序的一些架构细节。ob娱乐下载我们将向您展示项目的大图,然后重点介绍我们在Symfony2中真正喜欢的功能。ob娱乐下载别担心;我们还会讲到我们不怎么用的东西。
关键数字可以让你大致了解所描述的平台的规模:
- 应用程序处理每周达到10亿次请求
- ob娱乐下载Symfony2实例处理700个请求/秒,平均响应时间为30毫秒。
- 清漆-超过12,000 req/s(在压力测试中实现)
- 有160,000,000条记录存储在Redis(我们的主要数据存储!)
- 3亿在MySQL
业务需求
我们的系统开发必须满足以下两个需求:
- 可靠性-平台必须保证高可用性。这意味着,可接受的停机时间处于最小水平,以保持业务的全面运行。
- 性能因为之前的系统有一些性能问题,所以这个新系统必须解决这些问题,并且要准备好容易扩展以应对客户数量的增长。
选择正确的工具
有些人可能想知道为什么选择Symfonyob娱乐下载2而不是另一个产品,即Node.js,它也可以很好地构建快速api。
这是一个商业选择。由于客户端平台是用PHP构建的,所以他们有一个由优秀的PHP开发人员组成的大团队。用Node.js构建一个新系统会导致维护困难,并且需要雇佣Node.js开发人员仅在一个子系统上工作。选择Symfonyob娱乐下载2降低了与IT相关的成本,并使平台更易于维护。
但是,有必要进行概念验证并确认所需的性能水平。执行了性能测试,并确认了计划设置的效率。
应用程序体系结构
通过SOA(面向服务的体系结构)的请求流
正如我们已经说过的,这个平台被构造成独立的web服务,作为REST api, Symfony2应用程序是其中的一个服务。ob娱乐下载
让我们谈谈三个层:前端>> Webservice >>数据存储。
- 前端当终端客户端访问前端类型的网站时,它在内部与特定的Web服务通信(如本文所述)。
- Web服务web服务处理大量的业务逻辑。在许多站点上,功能并没有被划分,但所有的功能都保存在特定的服务中。这是处理软件架构关注点分离原则的一种众所周知的方法
- 数据存储—业务使用自己的数据存储。前端网站不能直接连接到由web服务处理的数据库,因为这将导致绕过对平台逻辑的控制。
(点击图片展开)
应用程序的服务器体系结构
为了保持高性能和高可用性,Symfony2服务使用了多个应用程序的服务器——采用冗余配置。ob娱乐下载
- 请求由HAProxy负载均衡器,它将它们分配到Varnish反向代理。
- 我们使用清漆只是作为缓存层,而不是作为另一个负载均衡器。每个应用程序的服务器上都有单独的Varnish实例。这样我们就不会以SPOF -单点故障结束。它降低了缓存命中率,但在这里它更倾向于可用性而不是性能(这仍然不是问题)。单个Varnish每秒可以处理多达12,000个请求。
- 最后一块拼图是输入服务器与ob娱乐下载Symfony2基于应用程序。我们使用PHP 5.4作为启用APC的PHP- fpm运行。该层处理700 req/s。
为高性能存储数据
当然,每个web应用程序都使用某种数据存储。我们在这里也安排得很好。
- 复述,-它是非常快速的内存数据存储。可以把Memcache看作是类固醇——带有持久性机制、HA设置等等。我们可以储存160000000条记录,包括98%的持久故障,即服务器停机后不会消失。的平均水平15000的点击量/秒.ob娱乐下载Symfony2使用SncRedisBundle与Predis库集成。
- MySQL-最知名的关系数据库。我们主要把它当做最后(第三)缓存层,用于未过期的资源。我们使用教义DBAL为连接。我们的MySQL存储300000000条记录这是400GB的纯数据。
所以,正如你所看到的,这里发生了一些不寻常的事情——Redis是我们的主数据存储MySQL是我们的最后一层缓存.
我们最喜欢的东西
清晰的项目结构——捆扎
ob娱乐下载Symfony2并没有强加给你项目的全部结构,它实际上是相当灵活的。基本上,你可以将你的代码构建成维护Symfony2相关代码的Bundles和处理常见任务的更通用的组件,包括那些不一定严格与Symfonob娱乐下载y2生态系统相关的组件。
遵循这个概念,我们主要使用包将我们的项目划分为逻辑连接的部分。我们几乎没有修改Symfony2标准结构,所以ob娱乐下载对于有Symfony2经验的开发人员来说,我们的代码库非常容易理解。
扩展代码库- EventDispatcher组件
是否需要更改所有控制器中的响应格式?这很简单,只要添加新的ResponseFormatListener
听一下kernel.response
事件。
我们在许多项目中看到了如此多的意大利面条代码,以至于我们非常喜欢Symfony中事件概念的普及。ob娱乐下载这在软件模式中并不是什么新鲜事,只是老的观察者模式,但在以前,在遗留的PHP框架中并不常用。
除了起源Symfony2事件之外,我们还选择ob娱乐下载添加自定义事件。使用事件监听器,我们可以保持代码干净,方法可以分派它们特定的事件,这样代码的新部分就可以连接到现有部分而不需要实际在代码中进行更改.
因为性能在项目中至关重要,我们正在评估性能影响。这种机制的好处是几乎没有任何性能开销。在内部,它是一个存储事件监听器实例的简单数组。几乎无忧!
检索请求的数据- OptionsResolver组件
在设计此应用程序时,我们还考虑从请求内容检索和验证数据的最有效方法。我们需要平滑地将请求的数据转换为DTO(数据传输对象)。这样我们就不会被难以维护的关联数组所困扰,我们将坚持Request类的结构。
基本上,我们以使用查询字符串传递查询结束,并考虑了不同的Symfony2机制…ob娱乐下载
你可以使用表单组件,在表单类型中构建请求结构,并将请求传递给它。它很好,功能丰富,但开销很大。我们也不需要或不想要像嵌套字段这样的高级选项。
另一种方法是在JSON结构中传递请求内容中的数据。使用Serializer组件(如伟大的JMSSerializer或甚至ob娱乐下载Symfony序列化器组件)并验证结果DTO请求的对象。仍然有两点可能导致性能瓶颈:序列化和使用Validator组件进行验证。
因此,我们不需要任何高级验证(只需要检查所需的选项和一些基本的格式验证)。请求的格式结构也设计得很简单,我们选择了…OptionsResolver组件.它与您在为表单创建选项时使用的相同。我们将GET数组传递给它,在输出中我们收到一个经过良好验证的结构化模型对象(这是一个DTO规范化的数组)。
处理验证的一个好处是,异常带有详细消息,因此它们非常适合调试目的和为API端点呈现可读的异常。
处理请求的示例
12 34 56 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
<?php名称空间Octivi\WebserviceBundle\控制器;/ * * *@Route(" / foo ") * /类FooController扩展BaseController{/** *示例请求:* - OK: api.com/foo/bars?type=simple&offset=10 * - OK: api.com/foo/bars?type=simple * -错误:api.com/foo/bars?offset=10 * -错误:api.com/foo/bars?limit=many **@Route(“/酒吧”)* /公共函数barsAction(){$请求=新GetBarsRequest ($这->getRequest ()->查询->());$结果=$这->get (“main.foo”)->getBars ($请求);返回$结果;}}<?php名称空间Octivi\WebserviceBundle\请求;类GetBarsRequest扩展AbstractRequest{受保护的$类型;受保护的$限制;受保护的$抵消;公共函数__construct(数组$选项=数组()){父::__construct ($选项);}受保护的函数setDefaultOptions(OptionsResolverInterface$解析器){父::setDefaultOptions ($解析器);$解析器->setRequired (数组(“类型”));$解析器->setOptional (数组(“限制”,“抵消”));$解析器->setAllowedTypes (数组(“限制”= >数组(“数字”),“抵消”= >数组(“数字”)));$解析器->setDefaults (数组(“限制”= >10,“抵消”= >0));$解析器->setAllowedValues (数组(“类型”= >数组(“简单”,“扩展”)));}/ /……}
很简单,不是吗?我们已经实现了我们想要的一切:
基本验证
- 设置必填字段
- 可选参数
- 处理数据类型(数字)和允许的值
- 默认参数
- 好看的请求DTO表示
在代码中保持配置-注释
是的,我们在高性能应用程序中使用注释.听起来是不是很奇怪?也许吧,但我们喜欢这个机制!
注释只是类似phpdoc的文本。它们在缓存预热过程下进行解析,并转换为普通的PHP文件。事实上,使用XML、YAML还是注释并不重要,因为它们最终都会转换成纯PHP文件。
我们尽可能多地使用注释:
- 路由-正如我们在之前的barsAction中所展示的,我们通过@Route注释来声明我们的路由。我们发现在控制器的代码中保留这样的声明比将其分割到单独的YML/XML文件更干净。这样,开发人员就不必从一个文件跳到另一个文件来搜索路由。请记住,我们已经在全局变量中声明了此类注释的加载
应用程序/配置/ routing.yml
文件。 - 服务-它是下一个东西,你可以放在-代码。使用JMSDiExtraBundle我们不必担心使用Service Container声明来维护YAML文件。一切都以服务类结束。不过,外部类的DI配置是通过XML文件完成的。
配置了注解的事件监听器示例:
12 34 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
<?php名称空间Octivi\WebserviceBundle\EventListener;使用JMS\DiExtraBundle\注释\服务;使用JMS\DiExtraBundle\注释\观察;使用JMS\DiExtraBundle\注释\InjectParams;使用JMS\DiExtraBundle\注释\注入;使用ob娱乐下载\组件\HttpKernel\事件\FilterResponseEvent;/ * * *@ service* /类FormatListener{/** *构造函数使用jmsdiextrabundance进行依赖项注入。* *@InjectParams({* "em" =@ inject("doctrine.orm.entity_manager"), * "security" =@ inject("security.context") *}) */函数__construct(EntityManager$新兴市场, SecurityContext$安全){$这->em =$新兴市场;$这->安全=$安全;}/ * * *@Observe(“内核。响应", priority = 0) */公共函数onKernelResponse(FilterResponseEvent$事件){$这->新兴市场- >…;}}
制作丰富的CLI命令-控制台组件
另一个广泛使用的Symfony组件是ob娱乐下载控制台组件它是我们最常用的组件,因为该应用程序的大多数新特性都来自CLI命令。
在前一代PHP框架或其他流行的基于PHP的软件(如Wordpress, Magento)中,编写CLI命令让我们非常头疼。没有任何标准化的组件,或者它们非常缺乏功能,以至于每个人都必须提出自己的解决方案。
在Syob娱乐下载mfony2中,我们发现了一个很酷的用于生成CLI命令的框架。我们可以设置命令名、必需的选项和参数。我们发现了一个对命令进行准确描述的好实践。它们是自文档化的,没有理由将它们添加到文档的标准形式中。欧宝官网下载app当您使用敏捷方法进行开发时,这尤其有用。当新功能出现得很快时,您最终会得到许多过时版本的纸质文档。欧宝官网下载app
我们使用控制台组件来创建管理工具,甚至是长时间运行的流程。最长的一次花了6天。Symfony生态系统缺乏内存泄漏的好证据!ob娱乐下载
1 2 3 4 5 6 7 8 9 10
$ php app/console octivi:test-command——help用法:octivi:test-command [-l|——limit[=“…”]] [o |——抵消[=“…”参数:table要处理的数据库表。选项:——limit (-l)限制每个SQL查询的数量。(默认的:10——offset (-o)偏移量为第一个语句(默认的:0)
密切关注应用程序- Profiler,秒表和独白三人组
为了保持对潜在的性能泄漏的控制,我们强烈使用HttpKernel的Profiler和Stopwatch Component。更容易发现应用程序方法有意想不到的,更长的执行时间和低效的数据库查询(这里我们谈论的是MySQL和Redis)。
在维护更大的项目时,特别是在SOA环境中,详细日志记录是必须的。否则,您将无法有效地跟踪问题,例如API方法的使用或与第三方Web服务的连接。
为此,我们使用丰富配置的Monolog(全部来自应用程序/配置/ config_prod.yml
!)我们在标准的prod.log中记录所有未预料到的事情,并将更详细的日志留给特定的通道或不同的文件。
我们已经使用该配置制定了一些很好的日志记录模式。由于我们不使用Fingers Crossed处理程序,所以我们总是向每个日志行添加尽可能详细的上下文。这样,对外部Web服务的调用如下所示:
12 3 4 5 6 7 8 9 10 11 12 13 14 16 17 18 19 20 21 22
$异常=零;试一试{$响应=$这->请求($请求);}抓(\异常$e){$异常=$e;}如果(零= = !$异常){$这->日志记录器->错误(“来自XXX的错误响应”,数组(“请求”= >$请求,“响应”= >$响应,“例外”= >(字符串)$异常));扔$异常;}其他的{$这->日志记录器->调试(“XXX的成功回应”,数组(“请求”= >$请求,“响应”= >$响应));}
我们不使用的特性
教义ORM
我们主要使用MySQL作为缓存层,在其中存储序列化的blob。我们不使用Doctrine ORM,因为它会不必要地影响性能。相反,我们只使用Doctrine DBAL来检索关联数组,然后我们有一个Model层来处理记录的反序列化/规范化。
我必须补充一点,这与存储的数据量无关,而是与检索数据的性能要求有关单记录.ORM在水合对象(反序列化)时增加了很大的开销。如果您接受它,即使您有+2亿条记录,也可以使用Doctrine ORM。ORM性能将与有1000个相同。
嫩枝
应用程序主要以JSON格式返回响应。因此,我们在标准请求流中不使用Twig。但是,我们保留它是为了呈现一些管理仪表板。
总结
正如本案例研究中所证明的那样,Symfony2可以用于各种项ob娱乐下载目。您只需聪明地组合Symfony提供的组件,就可以设计出满足您所有需求的系统。ob娱乐下载您的最终结果将是简单,易于维护,高效的设置。
谁是应用程序的幕后推手?
平台是由波兰的软件公司设计的Octivi.我们专注于构建专注于高性能和可用性的可伸缩架构。同时也感谢客户IT部门的配合。
相关文章
- Symfony2每周处理10亿个请求ob娱乐下载-我们对整个应用程序的概述。
- 使用HAProxy, PHP, Redis和MySQL构建一个不断增长的初创公司架构,每周处理10亿次请求-硬件架构的详细信息。
评论
谢谢分享!
“遵循这个概念,我们主要使用捆绑包将我们的项目划分为逻辑连接的部分。”你如何分离前端,api和amdmin的东西。所有在一个例如UserBundle或单独捆绑UserBundle, UserApiBundle, UserAdminBundle,以便您可以激活后端管理服务器上只有管理bundle ?
应该是:“发送一份提案或案例研究到fabien.potencier@sensiolabs.com”或“发送一份提案或案例研究到fabien.potencier@sensiolabs.com”。
很棒的文章,有趣的你如何使用OptionsResolver来验证一个请求:)
虽然没有提到处理这些api的身份验证和授权。是所有东西都是公开的吗?
它不是公开的,对API的访问只授予特定的服务器。
版本控制呢?
等待下一篇嘉宾文章,这将得到澄清:-)
@Gordon因特网
你能举个例子吗?你如何分离前端,api和amdmin的东西。所有在一个例如UserBundle或单独捆绑UserBundle, UserApiBundle, UserAdminBundle,以便您可以激活后端管理服务器上只有管理bundle ?
由于整个应用程序实际上是一个API,我们没有像“UserApiBundle”这样的包,而是“UserBundle”。我们有“MainBundle”,它动态连接“ModuleFooBundle”,“ModuleBarBundle”等。
@All上面
感谢热情的反馈!请继续关注下一篇客座文章(但在更“可扩展”的网站;))。
很高兴您喜欢使用OptionsResolver的想法——在内部它是一个简单的类,没有太多复杂的逻辑——非常适合性能需求。
我们正在使用与这篇博文中描述的相同的方案,所以我可以回答这个问题。HAProxy是一个快速可靠的负载均衡器和代理,就像nginx一样。它有自己的HTTP心跳和TCP ping,因此可以指定多个后端(一个主后端和一个紧急后端),HAProxy将在紧急或不可用的情况下自动在它们之间切换。
此解决方案是实现容错应用程序的典型方案(详细信息仅使用谷歌)。
感谢您提示OptionResolver作为验证器。
Javier Eguiluz is a certified Symfony engineer.
Get certified! Online exams available in all countries.
Register Now