ホームページ  >  記事  >  バックエンド開発  >  PHP コードを正しく公開する例を共有する

PHP コードを正しく公開する例を共有する

小云云
小云云オリジナル
2018-03-12 14:44:402785ブラウズ


ほぼすべての PHP プログラマーがコードを公開しており、そのコードは FTP または rsync を介して同期したり、svn または git を介して更新したりすることができます。活発なプロジェクトでは、1 日に数回コードがリリースされることがありますが、詳細に注意を払う人はほとんどいないのが現実で、実際には多くの落とし穴があり、知らないうちに落とし穴にはまっている可能性があります。

適切に実装されたパブリッシング システムは、少なくともアトミック パブリッシングをサポートする必要があります。各バージョンが独立した状態を表す場合、リリース期間中は、すべてのリクエストは単一の状態でのみ実行できます。これはアトミック リリースのサポートと呼ばれます。一方、リクエストがリリース中に異なる状態にまたがる場合、それはアトミック リリースとは言えません。説明するための例を示します: include a.php の場合、リクエストに 2 つの <code>include 两个 PHP 文件,分别是 a.phpb.php,当 include a.php 完成后,发布代码,接着 include b.php,如果处理不当的话,那么就可能会导致旧版本的 a.php 和新版本的 b.php 同时存在于同一个请求之中,换句话说就是没有实现原子发布。

开源世界里有很多不错的发布代码工具,比如  ruby 社区的 capistrano,其流程大致就是发布代码到一个全新的目录,然后再软链接到真正的发布目录。

├── current -> releases/v1
└── releases
    ├── v1
    │   ├── foo.php
    │   └── bar.php
    └── v2
        ├── foo.php
        └── bar.php

不过鉴于 PHP 本身的特殊性,如果只是简单套用上面的流程,那么将很难实现真正的原子发布。要理清个中缘由,还需要了解一下 PHP 中的两个 Cache 的概念:

  • opcode cache

  • PHP コードを正しく公開する例を共有する cache

先聊聊 opcode cache,基本就是 apc 或者 zend opcode,关于它的作用,大家都已经很熟悉,不必多言,需要注意的是 apc 的 bug 很多,比如开启了 apc.enable_cli 配置后就会有很多灵异问题,所以说 opcode cache 还是尽可能使用 zend opcache 吧,如果需要缓存数据,可以用 apcu。此外 apczend opcode 对缓存键的选择有所差异:apc 选择的是文件的 inodezend opcode 选择的是文件的 path

再聊聊 PHP コードを正しく公開する例を共有する cache,它的作用是缓冲获取文件信息的 IO 操作,大多数时候它对我们而言是透明的,以至于很多人都不知道它的存在,需要注意的是 PHP コードを正しく公開する例を共有する cache 是进程级别的,也就是说,每一个 php-fpm 进程都有自己独立的 PHP コードを正しく公開する例を共有する cache

假设在发布代码期间,opcode cache 或者 PHP コードを正しく公開する例を共有する cache 里的数据出现过期,那么就会出现一部分缓存是旧文件,一部分缓存是新文件的非原子发布的情况,为了避免出现这种情况,我们应该保证缓存过期时间足够长,最好是除非我们手动刷新,否则永远不过期,对应到配置上就是:关闭 apc.stat、opcache.validate_timestamps 配置,设置足够大的 PHP コードを正しく公開する例を共有する_cache_size、PHP コードを正しく公開する例を共有する_cache_ttl 配置,必要的监控总是有好处的。

相关的技术细节特别琐碎,建议大家仔细阅读如下资料:

  • PHP コードを正しく公開する例を共有する_cache
    PHP’s OPCache extension review
    Atomic PHP コードを正しく公開する例を共有するs at Etsy
    Cache invalidation for scripts in symlinked folders

在采用软链接发布代码的时候,通常遇到的第一个问题多半是新代码不生效!即便调用了 apc_clear_cache 或者 opcache_reset 方法也无效,重启 php-fpm 自然是能够解决问题,不过对脚本语言来说重启太重了!难道除了重启就没有别的办法了么?

事实上之所以会出现这样的问题,主要是因为 opcode cache 是通过 PHP コードを正しく公開する例を共有する cache 获取文件信息,即便软链接已经指向了新位置,但是如果 PHP コードを正しく公開する例を共有する cache 里还保存着旧数据的话,opcode cache 依然无法知道新代码的存在,缺省情况下,PHP コードを正しく公開する例を共有する_cache_ttl 缓存有效期是两分钟,这意味着发布代码后,可能要两分钟才能生效。为了让发布尽快生效,需要以进程为单位清除 PHP コードを正しく公開する例を共有する cache

<?php

    $key = &#39;php.pid_&#39; . getmypid();    if (($rev = apc_fetch($key)) != DEPLOY_VERSION) {        if($rev < DEPLOY_VERSION) {
            apc_store($key, DEPLOY_VERSION);
        }

        clearstatcache(true);
    }

如此在 apc 环境下基本就能工作了,但是在 zend opcode 环境下还可能有问题。因为在缺省情况下 opcache.revalidate_path 是关闭的,此时会缓存未解析的符号链接的值,这会导致即便软链接指向修改了,也无法生效,所以在使用 zend opcode 的时候,如果使用了软链接,视情况可能需要把 opcache.revalidate_pathPHP

ファイル、つまり a.phpb.php > が必要だとします。 が完了したらコードを解放し、include b.php を正しく処理しないと、古いバージョンの a .php が生成される可能性があります。新しいバージョンの b.php が同じリクエスト内に同時に存在します。つまり、アトミック パブリッシングは実装されていません。 🎜🎜オープンソースの世界には、🎜ruby🎜コミュニティのcapistranoなど、優れたコード公開ツールがたくさんあります。そのプロセスは、大まかに言うと、コードを新しいディレクトリに公開してから、実際のリリースディレクトリにソフトリンクします。 🎜
    fastcgi_param SCRIPT_FILENAME $PHP コードを正しく公開する例を共有する_root$fastcgi_script_name;    fastcgi_param DOCUMENT_ROOT $PHP コードを正しく公開する例を共有する_root;
🎜しかし、🎜PHP🎜自体の特殊性を考慮すると、上記の処理を単純に適用しただけでは真のアトミックリリースを実現することは困難です。理由を明確にするには、🎜PHP🎜 の 2 つの Cache の概念も理解する必要があります: 🎜
  • 🎜🎜オペコード キャッシュ🎜 🎜
  • 🎜🎜リアルパス キャッシュ🎜🎜
🎜まず オペコード キャッシュ について話しましょう。基本的には apc または です。 > zend opcode については、誰もがその役割をよく知っています。言うまでもなく、apc には多くのバグがあることに注意してください。たとえば、apc.enable_cli 設定がオンになった後は、奇妙な問題なので、opcode Cache はできるだけ zend opcache を使用する必要があります。データをキャッシュする必要がある場合は、apcu を使用できます。さらに、apczend opcode ではキャッシュ キーの選択が異なります。apc はファイルの inode を選択します。 zend opcode は、ファイルの path を選択します。 🎜🎜 もう一度 PHP コードを正しく公開する例を共有する キャッシュ について話しましょう。その機能は 🎜IO🎜 操作をバッファリングしてファイル情報を取得することです。存在しますが、PHP コードを正しく公開する例を共有する キャッシュ はプロセス レベルであることに注意してください。つまり、各 php-fpm プロセスには独自の独立した PHP コードを正しく公開する例を共有する キャッシュ があります。コード> >。 🎜🎜 コードのリリース中に、opcode Cache または PHP コードを正しく公開する例を共有する Cache 内のデータの有効期限が切れると、キャッシュの一部が古いファイルになり、キャッシュの一部は新しいファイルです。この状況を回避するには、キャッシュの有効期限を手動で更新しない限り期限切れにならないようにするのが最善です。設定は次のとおりです: close apc.stat、opcache.validate_timestamps 設定。 PHP コードを正しく公開する例を共有する_cache_size および PHP コードを正しく公開する例を共有する_cache_ttl 設定を十分な大きさに設定し、必要な監視を実行することが常に有益です。 🎜🎜関連する技術的な詳細は非常に簡単なので、次の情報を注意深く読むことをお勧めします: 🎜
  • <?php// PHP コードを正しく公開する例を共有する:releaserun("cd {{PHP コードを正しく公開する例を共有する_path}} && if [ -h release ]; then rm release; fi");
    run("ln -s $releasePath {{PHP コードを正しく公開する例を共有する_path}}/release");// PHP コードを正しく公開する例を共有する:symlinkrun("cd {{PHP コードを正しく公開する例を共有する_path}} && ln -sfn {{release_path}} current");
    run("cd {{PHP コードを正しく公開する例を共有する_path}} && rm release");?>
🎜 コードを公開するとき。ソフト リンクを使用すると、通常最初に発生する問題は、新しいコードが有効にならないことです。 apc_clear_cache メソッドや opcache_reset メソッドを呼び出しても機能しません。php-fpm を再起動すると自然に解決しますが、スクリプト言語にとって再起動は重すぎます。再起動する以外に方法はないのでしょうか? 🎜🎜実際、このような問題が発生する主な理由は、ソフト リンクが新しい場所を指していても、opcode CachePHP コードを正しく公開する例を共有する Cache を通じてファイル情報を取得するためです。ただし、古いデータがまだ PHP コードを正しく公開する例を共有する キャッシュ に保存されている場合、opcode Cache は新しいコードの存在を認識できません。デフォルトでは、PHP コードを正しく公開する例を共有する_cache_ttl キャッシュの有効期間は 2 分です。コードが発行されてから有効になるまでに最大 2 分かかる場合があります。リリースをできるだけ早く有効にするには、プロセス単位で PHP コードを正しく公開する例を共有する キャッシュ をクリアする必要があります: 🎜
shell> strace ln -sfn releases/foo currentsymlink("releases/foo", "current")      = -1 EEXIST (File exists)unlink("current")                       = 0symlink("releases/foo", "current")      = 0
🎜これは基本的に apc 環境で動作します。ただし、zend opcode 環境に問題がある可能性もあります。 opcache.revalidate_path はデフォルトでオフになっているため、未解決のシンボリック リンクの値がキャッシュされるため、zend opcode を使用する場合、ソフト リンクが有効になりません。 、ソフト リンクが使用されている場合、状況に応じて opcache.revalidate_path をアクティブにする必要がある場合があります。 🎜

详细介绍参考:PHP’s OPCache extension review。

BTW:如果需要手动重置 opcode cache,需要注意的是因为它是基于 SAPI 的概念,所以不能直接在命令行下调用 apc_clear_cache 或者 opcache_reset 方法来重置缓存,当然办法总是有的,那就是使用 CacheTool 在命令行下模拟 fastcgi 请求。

分析到这里,我们不妨反思一下:在 PHP 中原子发布之所以是一个棘手的问题,归根结底是因为软链接和缓存之间的的矛盾。不管是 opcode cache 还是 PHP コードを正しく公開する例を共有する cache,都是 PHP 固有的缓存特性,基于客观需要无法绕开,如此说来是否有办法绕开软链接,使其成为马奇诺防线呢?答案是 NGINX 的 $PHP コードを正しく公開する例を共有する_root:

    fastcgi_param SCRIPT_FILENAME $PHP コードを正しく公開する例を共有する_root$fastcgi_script_name;    fastcgi_param DOCUMENT_ROOT $PHP コードを正しく公開する例を共有する_root;

有了 $PHP コードを正しく公開する例を共有する_root,即便  DOCUMENT_ROOT 目录中含有软链接,NGINX 也会把软链接指向的真正的路径发给 PHP,也就是说,对 PHP 而言,软链接已经不存在了!不过作为代价,每一次请求,NGINX 都要通过相对昂贵的 IO 操作获取 $PHP コードを正しく公開する例を共有する_root 的值,通过 strace 命令我们能监控这一过程,下图从 currentfoo 的过程:

PHP コードを正しく公開する例を共有する

在本例中,压测发现使用 $PHP コードを正しく公開する例を共有する_root 后,性能下降了大约 5% 左右,不过明眼人一下就能发现,虽然  $PHP コードを正しく公開する例を共有する_root  导致了 lstat  和 readlink 操作,但是 lstat 操作的次数是和目录深度成正比的,也就是说目录越深,执行的 lstat 次数越多,性能下降也就越大。如果能够降低发布目录的深度,那么可以预计还能降低一些性能损耗。

结尾介绍一下 Deployer,它是 PHP 中做得比较好的工具,有很多特色,比如支持并行发布,具体演示如下图,左边是串行,右边是并行,使用「vvv」能得到更详细信息:

PHP コードを正しく公開する例を共有する

不过 Deployer 在原子发布上有一点瑕疵,具体见 release/symlink 代码:

<?php// PHP コードを正しく公開する例を共有する:releaserun("cd {{PHP コードを正しく公開する例を共有する_path}} && if [ -h release ]; then rm release; fi");
run("ln -s $releasePath {{PHP コードを正しく公開する例を共有する_path}}/release");// PHP コードを正しく公開する例を共有する:symlinkrun("cd {{PHP コードを正しく公開する例を共有する_path}} && ln -sfn {{release_path}} current");
run("cd {{PHP コードを正しく公開する例を共有する_path}} && rm release");?>

release 的时候,它是先删除再创建,是一个两步的非原子操作,在 symlink 的时候,看上去「ln -sfn」是单步原子操作,实际上也是错误的:

shell> strace ln -sfn releases/foo currentsymlink("releases/foo", "current")      = -1 EEXIST (File exists)unlink("current")                       = 0symlink("releases/foo", "current")      = 0

通过 strace 我们能清晰的看到,虽然表面上使用「ln -sfn」是一步操作,但是内部依然是按照先删除再创建的逻辑执行的,实际上这里应该搭配使用「ln & mv」

shell> ln -sfn releases/foo current.tmpshell> mv -fT current.tmp current

先通过 ln 创建一个临时的软链接,再通过 mv 实现原子操作,此时如果使用 strace 监控,会发现 mv「T」 选项实际上仅仅执行了一个 rename 操作,所以是原子的。

BTW:在使用「ln -sfn」前后,如果使用 stat 查看新旧文件的 inode 的话,可能会发现它们拥有一样的 inode 值,看上去和我们的结论相悖,其实不然,实际上只是复用删除值而已(如果想验证,注意 Linux 会复用,Mac 不会复用)。

据说一千个人的心中就有一千个哈姆雷特,不过我希望所有的 PHP 程序员在发布 PHP 代码的时候都能采用一种方法,那就是本文介绍的方法,正确的方法。

相关推荐:

php代码标志基础讲解

提高PHP代码质量的方法

JS和PHP代码实现用户输入数字后显示最大的值

以上がPHP コードを正しく公開する例を共有するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。