简而言之,这是它的工作形式:
● 为了预加载文件,您需求编写一个自界说PHP剧本
● 该剧本正在效劳器启动时执行一次
● 一切预加载的文件正在内存中均可用于一切申请
● 正在从新启动效劳器以前,对预加载文件所做的更改没有会孕育发生任何影响
让咱们深化理解它。
#Opcache
尽管预加载是建设正在opcache之上的,但它并非齐全同样的。Opcache将猎取您的PHP源文件,将其编译为“ opcodes”,而后将这些编译后的文件存储正在磁盘上。
您能够将操作码看做是代码的底层示意,正在运转时很容易诠释。因而,opcache会跳过源文件以及PHP诠释器正在运转时实际需求之间的转换步骤。微小的成功!
但咱们另有更多的播种。Opcached文件没有晓得其余文件。假如类a是从类B扩大而来的,那末依然需求正在运转时将它们链接正在一同。别的,opcache执行反省以查看源文件能否被修正,并将基于此使其缓存生效。
因而,这就是预加载施展作用之处:它不只将源文件编译为操作码,并且还将相干的类、特色以及接口链接正在一同。而后,它将这个“已编译”的可运转代码blob(即:PHP诠释器能够应用的代码)保留正在内存中。
如今,当申请抵达效劳器时,它能够应用曾经加载到内存中的局部代码库,而没有会孕育发生任何开支。
那末,咱们所说的“代码库的一局部”是甚么呢?
#理论中的预加载
为了进行预加载,开发职员必需奉告效劳器要加载哪些文件。这是用一个简略的PHP剧本实现的,的确不甚么艰难。
规定很简略:
● 您提供一个预加载剧本,并应用opcache.preload饬令将其链接到您的php.ini文件中。
● 您要预加载的每一个PHP文件都应该通报到opcache_compile_file(),或许正在预加载剧本中只要要一次。
假定您想要预加载一个框架,例如Laravel。您的剧本必需遍历vendor/laravel目次中的一切PHP文件,并将它们一个接一个地增加。
正在php.ini中链接到此剧本的办法以下:
opcache.preload=/path/to/project/preload.php
这是一个虚构的完成:
$files = /* An array of files you want to preload */; foreach ($files as $file) { opcache_compile_file($file); }
#正告:无奈预加载未链接的类
等等,有一个正告!为了预加载文件,还必需预加载它们的依赖项(接口,特色以及父类)。
假如类依赖项有任何成绩,则会正在效劳器启动时告诉您:
Can't preload unlinked class Illuminate\Database\Query\JoinClause: Unknown parent Illuminate\Database\Query\Builder
看,opcache_compile_file()将解析一个文件,但没有执行它。这象征着假如一个类有未预加载的依赖项,它自身也不克不及预加载。
这没有是一个致命的成绩,您的效劳器能够失常工作。但你没有会失去一切你想要的预加载文件。
侥幸的是,另有一种确保链接文件也被加载的办法:您能够应用require_once替代opcache_compile_file,让已注册的autoloader(多是composer的)担任其他的工作。
$files = /* All files in eg. vendor/laravel */; foreach ($files as $file) { require_once($file); }
另有一些需求留意之处。例如,假如您试图预加载Laravel,那末框架中的一些类依赖于其余尚没有存正在的类。例如,文件零碎缓存类\ lighting \ filesystem \ cache依赖于\League\Flysystem\Cached\Storage\AbstractCache,假如您从未应用过文件零碎缓存,则可能无奈将其装置到您的名目中。
测验考试预加载一切内容时,您可能会遇到“class not found”谬误。侥幸的是,正在默许的Laravel装置中,只有多数这些类,能够随意马虎疏忽。为了不便起见,我编写了一个小小的preloader类,以使疏忽文件更易,以下所示:
class Preloader { private array $ignores = []; private static int $count = 0; private array $paths; private array $fileMap; public function __construct(string ...$paths) { $this->paths = $paths; // We'll use composer's classmap // to easily find which classes to autoload, // based on their filename $classMap = require __DIR__ . '/vendor/composer/autoload_classmap.php'; $this->fileMap = array_flip($classMap); } public function paths(string ...$paths): Preloader { $this->paths = array_merge( $this->paths, $paths ); return $this; } public function ignore(string ...$names): Preloader { $this->ignores = array_merge( $this->ignores, $names ); return $this; } public function load(): void { // We'll loop over all registered paths // and load them one by one foreach ($this->paths as $path) { $this->loadPath(rtrim($path, '/')); } $count = self::$count; echo "[Preloader] Preloaded {$count} classes" . PHP_EOL; } private function loadPath(string $path): void { // If the current path is a directory, // we'll load all files in it if (is_dir($path)) { $this->loadDir($path); return; } // Otherwise we'll just load this one file $this->loadFile($path); } private function loadDir(string $path): void { $handle = opendir($path); // We'll loop over all files and directories // in the current path, // and load them one by one while ($file = readdir($handle)) { if (in_array($file, ['.', '..'])) { continue; } $this->loadPath("{$path}/{$file}"); } closedir($handle); } private function loadFile(string $path): void { // We resolve the classname from composer's autoload mapping $class = $this->fileMap[$path] ?? null; // And use it to make sure the class shouldn't be ignored if ($this->shouldIgnore($class)) { return; } // Finally we require the path, // causing all its dependencies to be loaded as well require_once($path); self::$count++; echo "[Preloader] Preloaded `{$class}`" . PHP_EOL; } private function shouldIgnore(?string $name): bool { if ($name === null) { return true; } foreach ($this->ignores as $ignore) { if (strpos($name, $ignore) === 0) { return true; } } return false; } }
经过正在相反的预加载剧本中增加此类,咱们如今能够像这样加载整个Laravel框架:
// … (new Preloader()) ->paths(__DIR__ . '/vendor/laravel') ->ignore( \Illuminate\Filesystem\Cache::class, \Illuminate\Log\LogManager::class, \Illuminate\Http\Testing\File::class, \Illuminate\Http\UploadedFile::class, \Illuminate\Support\Carbon::class, ) ->load();
#无效吗?
这当然是最首要的成绩:一切文件都正确加载了吗?您能够简略地经过从新启动效劳器来测试它,而后将opcache_get_status()的输入转储到PHP剧本中。您将看到它有一个名为preload_statistics的键,它将列出一切预加载的函数、类以及剧本;和预加载文件耗费的内存。
# Composer支持
一个颇有出路的特点多是基于composer的主动预加载处理计划,它曾经被年夜少数古代PHP名目所应用。人们在致力正在composer.json中增加预加载设置装备摆设选项,它将为您天生预加载文件!今朝,此性能仍正在开发中,但您能够正在此处存眷。
#效劳器要求
正在应用预加载时,对于devops方面另有两件更首要的事件需求说起。
您曾经晓得,需求正在php.ini中指定一个条款能力进行预加载。这象征着假如您应用同享主机,您将无奈自在地设置装备摆设PHP。实际上,您需求一个公用的(虚构)效劳器,以便可以为单个名目优化预加载的文件。记住这一点。
还要记住,每一次需求从新加载内存文件时,都需求从新启动效劳器(假如应用php-fpm就足够了)。这对年夜少数人来讲仿佛是不言而喻的,但依然值患上一提。
#功能
如今到最首要的成绩:预加载真的能进步功能吗?
谜底是一定的:Ben Morel分享了一些基准测试,能够正在以前链接的相反的composer成绩中找到。
风趣的是,您能够决议仅预加载“hot classes”,它们是代码库中常常应用的类。Ben的基准测试显示,只加载约莫100个抢手类,实际上能够取得比预加载一切类更好的功能收益。这是功能晋升13%以及17%的区分。
当然,应该预加载哪些类取决于您的特定名目。理智的做法是正在开端时尽可能多地预加载。假如您的确需求大批的百分比增进,您将不能不正在运转时监督您的代码。
当然,一切这些工作均可以主动化,未来可能会完成。
如今,最首要的是要记住composer将增加支持,这样您就不用本人制造预加载文件,而且只需您齐全管制了此性能,就能够正在效劳器上轻松设置此性能。
翻译:https://stitcher.io/blog/preloading-in-php-74
以上就是PHP 7.4中的预加载(Opcache Preloading)的具体内容,更多请存眷资源魔其它相干文章!
标签: php7开发教程 php7开发资料 php7开发自学 Php 7.4
抱歉,评论功能暂时关闭!