使用RoadRunner对Magento Rest API超频

使用RoadRunner加速Magento
创建的PHP死了。 一切都会好起来的,但是最近他没有机会这样做。 一年前,在中心上, 宣布了RoadRunner工具,这迫使PHP进程离开了死亡和复活的无尽循环。


RoadRunner的工作原理是保持正在运行的流程并向其中抛出传入的请求,根据开发人员的说法,这可以提高应用程序性能(有时甚至提高40倍)。


自从我与Magento一起工作了很长时间以来,似乎不是在神话般的框架上而是在一个实际的应用程序上测试该工具的好主意,Magento开源对此非常有用。


Magento应用程序的初始化成本


加速RoadRunner应用程序的方法包括通过减少初始化应用程序的开销来减少响应时间(在预热启动之后)。


Anton Tsitou的演示文稿“使用RoadRunner设计混合Go / PHP应用程序”的屏幕截图
Anton Tsitou的演示文稿“使用RoadRunner设计混合Go / PHP应用程序”的屏幕截图


以Magento为例,花在启动上的主要时间是:


  • 作曲家自动加载
  • 自举

Composer自动加载并未引起注意,因为它是PHP应用程序的标准配置。


与Composer相关的分析结果。
与Composer相关的分析结果。


Magento应用程序引导包括初始化错误处理程序,检查应用程序状态等。


最难的部分是IoC容器(用Magento表示“ ObjectManager”)的初始化以及通过它来递归创建依赖项实例以获得应用程序对象。


与引导相关的分析结果。
与引导相关的分析结果。


RoadRunner实施


要启动RoadRunner,您需要创建一个工作器,其中将包含一个用于接受传入请求和发送响应的循环。 此外,该工具可与实施PSR-7的请求和答案配合使用。 从官方文档看,它看起来像这样:


while ($req = $psr7->acceptRequest()) { $resp = new \Zend\Diactoros\Response(); $resp->getBody()->write("hello world"); $psr7->respond($resp); } 

Magento和PSR-7


Magento尚未实现PSR-7,开箱即用地使用其请求和响应实现,它们的方法主要来自于先前的版本。


为了实现RoadRunner,您需要找到一个入口点,该入口点将以某种形式接受请求并返回响应( Symfony示例 )。


Magento \ Magento \ Framework \ AppInterface中有一个问题,只有一个问题,该接口并非旨在接受请求。 但是,等等,它从哪里进入应用程序? 值得回顾的初衷是PHP的诞生 。 因此,在设计和划分层时,绝大多数库,程序包和框架都只是简单地不认为该请求不仅仅是一个全局请求。


Magento传输层基于相同的原理构建。 尽管文档描述了可注入对象/可更新对象之间的差异 ,但实际上,我们已将请求用作全局有状态服务,该服务从全局变量($ _GET,$ _POST)初始化自身。 除了所有这些,可以在内核本身的应用程序的各个级别上看到此服务的注入,更不用说第三方模块的质量了。


基于上述内容,人们希望仅通过将请求从PSR-7样式转换为Magento样式来实施RoadRunner。


实施PSR-7适配器


考虑到收到的信息,我们制定问题。
我想要一个可以接受PSR-7请求并返回PSR-7响应的特定应用程序接口。 还必须创建一个已创建接口的实现,以使此交互格式适合Magento应用程序。


PSR-7适配器
PSR-7适配器


如上所述,magento应用程序已经返回了响应,因此我们只需要将其转换为PSR-7格式即可。


对于请求,您需要一个类来代理对当前请求对象的所有调用,该类将我们放入一个特殊的寄存器中(是的,体系结构之神会原谅此变态)。 另外,发现请求类不是3个,而是1个,因此要求通过容器的IoC配置对其进行重新注册。


Magento中用于查询的一组类
Magento中用于查询的一组类


一个不朽的PHP应用程序的问题


通过RoadRunner启动的应用程序与任何长期存在的php进程都存在相同的问题,在文档中对此进行了描述( https://roadrunner.dev/docs/usage-production


开发人员应监视的应用程序级别的主要问题是:



在Magento的上下文中,应该特别注意状态管理,因为在内核和第三方模块中,在服务内部缓存当前用户/产品/类别是一种非常常见的方法。


  protected function getCustomer(): ?CustomerInterface { if (!$this->customer) { if ($this->customerSession->isLoggedIn()) { $this->customer = $this->customerRepository->getById($this->customerSession->getCustomerId()); } else { return null; } } return $this->customer; } 

内核使用对象状态的方法的示例。


通过RoadRunner运行Magento Rest API服务器


考虑到全球状态的潜在问题,基于开发Magento前端部分的经验,选择了最合适,最轻松的WebApi部分进行发布。


首先要做的是创建我们的工作人员,该工作人员将在RoadRunner上运行并无限期地(几乎)生活。 为此,请从RoadRunner指南中获取一段代码,然后将我们的应用程序包装在一个PSR-7适配器中。


 $relay = new StreamRelay(STDIN, STDOUT); $psr7 = new PSR7Client(new Worker($relay)); $bootstrap = \Magento\Framework\App\Bootstrap::create(BP, []); /** @var \Magento\Framework\App\Http $app */ $app = $bootstrap->createApplication(\Magento\Framework\App\Http::class); /** @var ApplicationInterface $psr7Application */ $psr7Application = $bootstrap->getObjectManager()->create( \Isxam\M2RoadRunner\Application\MagentoAppWrapper::class, [ 'magentoApp' => $app ] ); while ($request = $psr7->acceptRequest()) { try { $response = $psr7Application->handle($request); $psr7->respond($response); } catch (\Throwable $e) { $psr7->getWorker()->error((string)$e); } } 

while循环之前的代码将在worker的开头执行,其中包含循环内的所有内容-以及每个新请求。


工作人员准备就绪后,我们将继续配置用Go编写的RoadRunner服务器。 这里的一切都很好,而且很快,只需要下载下载可执行文件的composer程序包就可以了。 我们创建服务器的配置-最简单的配置如下所示。


 http: address: 0.0.0.0:8086 workers: command: "php worker.php" pool: numWorkers: 1 

RoadRunner配置。


该文档包含大量设置,可让您灵活地配置服务器,以免出现完全编译二进制文件的愿望。


 ./rr serve -v -d 

服务器启动


解决方案测试


工具


为了方便测试,我们以简单的东西为例,例如artillery.io。


我们将在一个用户顺序执行查询的帮助下测试性能(RoadRunner还支持多个线程中的查询执行,我们将把这个问题留给其他研究人员使用)


在输入中,我们拥有带有两个环境的火炮配置文件-Apache和RoadRunner。 它们都使用相同的Magento实例,因此在这里它们处于平等的地位。


测试场景


以下方案用于衡量两种解决方案的性能。


方案1.创建类别
  - name: "S1. Create category" flow: - loop: - post: url: "/rest/V1/categories" json: category: name: "name-{{prefix}}-{{ $loopCount }}" parent_id: 2 is_active: true count: 100 

方案2.获取国家列表
  - name: "S2. Countries list" flow: - loop: - get: url: "/rest/V1/directory/countries" count: 100 

方案3:列出产品类型
  - name: "S3. Product types list" flow: - loop: - get: url: "/rest/V1/products/types" count: 100 

方案4.获取属性集列表
  - name: "S4. Product attribute sets list" flow: - loop: - get: url: "/rest/V1/products/attribute-sets/sets/list?searchCriteria" count: 100 

方案5.获取类别
  - name: "S5. Category get" flow: - loop: - get: url: "/rest/V1/categories/2" count: 100 

方案6.产品创建
  - name: "S6. Create product" flow: - loop: - post: url: '/rest/V1/products' json: product: sku: "sku-{{prefix}}-{{ $loopCount }}" name: "name-{{prefix}}-{{ $loopCount }}" attribute_set_id: 4 price: 100 type_id: "simple" count: 100 

方案7.检索产品列表
  - name: "S7. Get product list" flow: - loop: - get: url: "/rest/V1/products?searchCriteria[pageSize]=20" count: 100 

结果


通过RoadRunner和Apache交替运行所有脚本后,获得了查询持续时间的中位数。 根据中位数,所有方案的速度大约相差约50ms。


性能测试结果。
性能测试结果。


总结


一个动手实验证实了有关特定应用程序上RoadRunner性能稳定增长的假设。 使用此工具可以使应用程序请求的处理速度保持恒定,该常数等于环境和依赖项的初始化时间。


在轻量级处理程序上,这使您有时可以加快应用程序的速度,而在重度上几乎无法产生明显的效果。 如果您的代码很慢,那么车前草很可能不会帮助他。


如果您的应用程序写得很好,那么通过RoadRunner进行操作的操作很可能不会出现问题,但是如果应用程序需要进行改编以用于RoadRunner,则很可能在没有RoadRunner的情况下也需要同样的操作,以便更清楚地观察体系结构的各个层并遵守该领域的开发标准。


Magento开源通常适合在给定环境中启动,但是,它需要改进传输层并更正业务逻辑,以防止在同一过程中重复请求期间出现错误行为。 而且,使用RoadRunner会对开发方法施加某些限制,但是,它们与公认的惯例并不矛盾。


最后是一个不错的屏幕截图。 您何时仍会在此响应时间看到Magento请求?

震撼


参考文献


  1. 文章中的示例
  2. RoadRunner官方网站

Source: https://habr.com/ru/post/zh-CN466105/


All Articles