Almost every PHP programmer has published code that may have been synced via FTP or rsync , or it may have been updated with svn or git . An active project may have to be published several times a day, but the reality is that few people notice the details, in fact there are a lot of holes in it, and you probably don't know it in the pit.
A properly implemented publishing system should at least support atomic publishing. If each version represents a separate state, then during the release, any request can be executed in a single state. This is called support for Atomic publishing; Conversely, if a request crosses a different state during a release, it cannot be called an atomic release. Let's take an example to illustrate this: assuming that a request requires include
two PHP files, respectively, a.php
and b.php
, when include a.php
done, publish the code, and then include b.php
, if handled improperly, it may result in an older version of the a.php
And the new version b.php
exist in the same request, in other words, the atomic release is not implemented.
There are a lot of good release code tools in the open source world, such as the Capistrano of the Ruby community, where the process is basically to publish the code into a completely new directory, and then soft-link to the real publishing directory.
├──current-releases/v1└──releases ├──v1 │ ├──foo.php │ └──bar.php └──v2 ├──foo.php └──bar.php
However, given the specifics of PHP itself, it would be very difficult to achieve a real atom release if it was simply applied to the above process. To clarify why, you need to understand the two concepts in PHP Cache
:
OpCode cache
Realpath Cache
First chat opcode cache
, basically is apc
or zend opcode
, about its role, we are already very familiar with, needless to say, need to pay attention to is a lot of apc
bugs, such as open the APC.ENABLE_CLI configuration will have a lot of supernatural problems, so say opcode cache
or do can use zend opcache
, if need to cache data, can use APCU. In addition, the selection of the apc
zend opcode
cache key is different: apc
Select the file inode
, zend opcode
Select the file path
.
To talk about realpath cache
, its role is to buffer the access to the file information IO operation, most of the time it is transparent to us, so many people do not know its existence, it is necessary to pay attention to the realpath cache
process level, that is, each php-fpm
of the The process has its own independence realpath cache
.
Suppose that during the release of the code, opcode cache
or the data is out of realpath cache
date, then there will be a part of the cache is old files, some of the cache is a non-atomic release of new files, in order to avoid this situation, we should ensure that the cache expires long enough, preferably unless we manually refresh, Otherwise never expires, corresponding to the configuration is: Turn off Apc.stat, opcache.validate_timestamps configuration, set a large enough realpath_cache_size, Realpath_cache_ttl configuration, The necessary monitoring is always good.
Related technical details are particularly trivial, it is recommended that you read the following information carefully:
When you publish your code with a soft link, the first problem you usually encounter is that the new code doesn't take effect! Even if the call to the Apc_clear_cache or Opcache_reset method is not valid, restarting php-fpm
naturally solves the problem, but the restart is too heavy for the scripting language! Is there no other way than to reboot?
In fact, the problem arises mainly because it opcode cache
is through the realpath cache
acquisition of file information, even if the soft link has pointed to a new location, but if the realpath cache
old data is still stored, there is opcode cache
still no way to know the existence of the new code, by default, Realpath_ The CACHE_TTL cache is valid for two minutes, which means it may take up to two minutes for the code to take effect. In order for the publication to take effect as soon as possible, the process needs to be cleared realpath cache
:
<?php $key = ' php.pid_ '. Getmypid (); if ($rev = Apc_fetch ($key)) = deploy_version) { if ($rev < deploy_version) { apc_store ($key, deploy_version) ; } Clearstatcache (True); }
So in the apc
environment basically can work, but in the zend opcode
environment may also have problems. Because Opcache.revalidate_path is turned off by default, the values of unresolved symbolic links are cached, which can cause the soft links to be changed and not take effect, so zend opcode
when using a soft link, depending on the situation, you may need to opcache.revalidate_path
activation.
For more information, refer to PHP's Opcache extension review.
BTW: If you need to reset manually opcode cache
, you need to be aware that because it is based on the concept of SAPI , so you can not directly at the command line with the Apc_clear_cache or Opcache_reset method to reset the cache, of course, there is always a way, That is to use Cachetool to impersonate the request at the command line fastcgi
.
Analysis here, we may wish to reflect on: in PHP , the sub-publishing is a tricky issue, in the final analysis is due to the contradiction between soft links and caches. Whether opcode cache
or not, realpath cache
PHP 's inherent caching characteristics, based on the objective needs can not be bypassed, so there is a way to bypass the soft link to make it a maginot line of defense? The answer is NGINX $realpath _root:
Fastcgi_param script_filename $realpath _root$fastcgi_script_name; Fastcgi_param document_root $realpath _root;
With $realpath_root
, even if the DOCUMENT_ROOT
directory contains soft links,NGINX will also point to the soft link to the real path to PHP, that is to say, for php , soft links no longer exist! However, at the cost of each request,NGINX has to pass a relatively expensive IO operation to obtain $realpath_root
the value, through the strace
command we can monitor the process, current
from foo
the process:
In this example, the pressure test found that after the use of the $realpath_root
performance of about 5% , but discerning eye a bit can be found, although $realpath_root
led to lstat
and readlink
operation, but the lstat
number of operations is proportional to the depth of the directory, that is, the eye The deeper the recording, the more execution times, and the greater the lstat
performance degradation. If you can reduce the depth of the publishing directory, you can expect to reduce some of the performance losses.
At the end of the Deployer, it is a good tool in PHP , there are many features, such as support for parallel publishing, concrete demonstration such as, the left is serial, the right side is parallel, using "vvv" can get more detailed information:
But Deployer has a slight flaw in the atomic release, see release/symlink
code:
<?php//Deploy:releaserun ("CD {{Deploy_path}} && if [-H release]; then RM release; Fi "), Run (" Ln-s $releasePath {deploy_path}}/release ");//Deploy:symlinkrun (" CD {{Deploy_path}} && LN-SFN {{ Release_path}}, run ("CD {{Deploy_path}} && rm release"); >
In release
the time, it is first removed and then created, is a two-step non-atomic operation, at symlink
the time, appears to 「ln -sfn」
be a single-step atomic operation, is actually also wrong:
shell> strace LN-SFN releases/foo currentsymlink ("Releases/foo", "current") =-1 eexist (File exists) unlink (" Current ") = 0symlink (" Releases/foo "," current ") = 0
As strace
we can clearly see, although surface use 「ln -sfn」
is a one-step operation, but the internal is still in accordance with the deletion and then create the logic of execution, in fact, should be used in conjunction with 「ln & mv」
:
shell> LN-SFN releases/foo current.tmpshell> mv-ft current.tmp Current
First, by ln
creating a temporary soft link, and then by mv
implementing atomic operations, if you use strace
monitoring, the options you find are mv
「T」
actually only performing an rename
operation, so it's atomic.
BTW: Before and 「ln -sfn」
after use, if you use to stat
view the old and new files inode
, you may find that they have the same inode
value, it seems to contradict our conclusions, in fact, it is only the reuse of deleted values (if you want to verify, pay attention to the Linux will be reused and theMac will not be reused).
It is said that 1000 people have 1000 hamlet in their hearts, but I hope that all PHP programmers in the release of PHP code can use a method, that is the method described in this article, the correct method.