>  기사  >  백엔드 개발  >  PHP 코드를 올바르게 게시하는 예 공유

PHP 코드를 올바르게 게시하는 예 공유

小云云
小云云원래의
2018-03-12 14:44:402825검색


거의 모든 PHP 프로그래머가 코드를 출시했습니다. 이 코드는 FTP 또는 rsync 를 통해 동기화되거나 svn 또는 git을 통해 업데이트될 수 있습니다. 활발한 프로젝트는 하루에 여러 번 코드를 공개할 수도 있지만, 세부 사항에 주의를 기울이는 사람은 거의 없으며, 실제로는 함정이 많고, 자신도 모르게 수렁에 빠져 있을 가능성이 있습니다.

제대로 구현된 게시 시스템은 최소한 원자 게시를 지원해야 합니다. 각 버전이 독립적인 상태를 나타내는 경우 릴리스 기간 동안 모든 요청은 단일 상태에서만 실행될 수 있습니다. 이를 원자 릴리스 지원이라고 합니다. 반대로, 요청이 릴리스 중에 여러 상태에 걸쳐 있으면 원자 릴리스라고 할 수 없습니다. 설명을 위해 예를 들어 보겠습니다. include a.php일 때 요청에 두 개의 <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 >가 필요하다고 가정해 보겠습니다. 가 완료되면 코드를 게시한 다음 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🎜에 있는 두 캐시의 개념도 이해해야 합니다. 🎜
  • 🎜🎜opcode 캐시🎜 🎜
  • 🎜🎜PHP 코드를 올바르게 게시하는 예 공유 캐시🎜🎜
🎜 먼저 기본적으로 apc 또는 인 <code>opcode 캐시에 대해 이야기해 보겠습니다. > zend opcode, 모두가 이미 그 기능에 익숙합니다. 말할 필요도 없이 apc에는 많은 버그가 있습니다. 예를 들어, apc.enable_cli 구성을 켠 후에는 많을 겁니다. 이상한 문제이므로 opcode 캐시는 최대한 zend opcache를 사용해야 합니다. 데이터를 캐시해야 하는 경우 apcu를 사용하면 됩니다. 또한 apczend opcode에는 서로 다른 캐시 키 선택이 있습니다. apc는 파일의 inode를 선택합니다. zend opcode는 파일의 경로를 선택합니다. 🎜🎜 PHP 코드를 올바르게 게시하는 예 공유 캐시에 대해 다시 이야기해 보겠습니다. 그 기능은 🎜IO🎜 작업을 버퍼링하여 파일 정보를 얻는 것입니다. 대부분의 경우 이는 우리에게 투명하므로 많은 사람들이 이를 모릅니다. 존재하지만 PHP 코드를 올바르게 게시하는 예 공유 캐시는 프로세스 수준에 있다는 점에 유의해야 합니다. 즉, 각 php-fpm 프로세스에는 자체적인 독립적인 PHP 코드를 올바르게 게시하는 예 공유 캐시가 있습니다. 코드> >. 🎜🎜코드를 릴리스하는 동안 opcode 캐시 또는 PHP 코드를 올바르게 게시하는 예 공유 캐시의 데이터가 만료되고 캐시의 일부가 오래된 파일과 캐시의 일부는 새 파일입니다. 원자 게시의 경우 이러한 상황을 방지하려면 캐시 만료 시간을 수동으로 새로 고치지 않는 한 만료되지 않는 것이 가장 좋습니다. 구성은 다음과 같습니다. 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 캐시PHP 코드를 올바르게 게시하는 예 공유 캐시를 통해 파일 정보를 얻기 때문입니다. 소프트 링크가 새로운 위치를 가리켰음에도 불구하고, 그러나 이전 데이터가 여전히 PHP 코드를 올바르게 게시하는 예 공유 캐시에 저장되어 있는 경우 opcode 캐시는 여전히 새 코드의 존재를 알 수 없습니다. 기본적으로 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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.