PHP의 설명서 페이지 flock()
은 IIS에서 사용하는 것이 안전하지 않음을 나타냅니다. 모든 상황에서 flock
에 의지 할 수 없다면 동일한 방법으로 안전하게 달성 할 수있는 다른 방법이 있습니까?PHP flock() 대체
답변
가상의 모든 가능한 상황에서 안전하게 동일한 대안을 얻을 수있는 대안이 없습니다. 그것은 컴퓨터 시스템과 the job is not trivial for cross-platform code의 디자인에 의한 것입니다.
flock()
을 안전하게 사용해야하는 경우 대신 응용 프로그램 요구 사항을 문서화하십시오.
또는 자신 만의 잠금 메커니즘을 만들 수는 있지만 원자 적이어야합니다. 즉, 잠금을 테스트해야하며 존재하지 않는 경우 잠금을 설정해야합니다. 그 사이에 다른 사람이 잠금을 획득 할 수 없도록해야합니다.
이것은 잠금을 나타내는 잠금 파일을 작성하여 수행 할 수 있지만 존재하지 않는 경우에만 수행 할 수 있습니다. 불행히도 PHP는 그런 방식으로 파일을 생성하는 기능을 제공하지 않습니다.
mkdir()
으로 디렉토리를 생성하고 디렉토리가 생성 될 때 true
을 반환하고 이미 존재하는 경우 false
을 반환 할 수 있으므로 결과로 작업 할 수 있습니다.
완벽합니다. 나는 lockfiles에 대해 생각해 보았지만 PHP에서 구현할 때 문제가있다. 또한 "잠긴"플래그를 설정하고 해제하기 위해 행 잠금을 사용하는 데이터베이스를 사용하려고 생각했지만 느리고 강건하지도 않습니다.그러나, 나는 lockfile 대신에 디렉토리를 사용하는 것을 생각하지 않았습니다! 감사! – Matty
그러나 이것은 *'mkdir()'이 지정된 값을 반환하는 경우에만 작동합니다. 나는 그것이 모든 플랫폼/파일 시스템 조합의 경우인지는 모르겠다. – hakre
'mkdir()'은 적어도 리눅스에서는 제대로 작동합니다. 나는 이것을 Windows 서버에서 테스트 할 것입니다. 예상대로 작동하지 않으면 다시 업데이트하겠습니다. – Matty
내 제안은 flock()
대신 mkdir()
을 사용하는 것입니다. 이 차이를 보여주는 캐시/읽기, 쓰기에 대한 실제 예입니다 : 이제
$data = false;
$cache_file = 'cache/first_last123.inc';
$lock_dir = 'cache/first_last123_lock';
// read data from cache if no writing process is running
if (!file_exists($lock_dir)) {
// we suppress error messages as the cache file exists in 99,999% of all requests
$data = @include $cache_file;
}
// cache file not found
if ($data === false) {
// get data from database
$data = mysqli_fetch_assoc(mysqli_query($link, "SELECT first, last FROM users WHERE id = 123"));
// write data to cache if no writing process is running (race condition safe)
// we suppress E_WARNING of mkdir() because it is possible in 0,001% of all requests that the dir already exists after calling file_exists()
if (!file_exists($lock_dir) && @mkdir($lock_dir)) {
file_put_contents($cache_file, '<?php return ' . var_export($data, true) . '; ?' . '>')) {
// remove lock
rmdir($lock_dir);
}
}
을, 우리는 flock()
와 같은를 달성하려고 :
$data = false;
$cache_file = 'cache/first_last123.inc';
// we suppress error messages as the cache file exists in 99,999% of all requests
$fp = @fopen($cache_file, "r");
// read data from cache if no writing process is running
if ($fp !== false && flock($fp, LOCK_EX | LOCK_NB)) {
// we suppress error messages as the cache file exists in 99,999% of all requests
$data = @include $cache_file;
flock($fp, LOCK_UN);
}
// cache file not found
if (!is_array($data)) {
// get data from database
$data = mysqli_fetch_assoc(mysqli_query($link, "SELECT first, last FROM users WHERE id = 123"));
// write data to cache if no writing process is running (race condition safe)
$fp = fopen($cache_file, "c");
if (flock($fp, LOCK_EX | LOCK_NB)) {
ftruncate($fp, 0);
fwrite($fp, '<?php return ' . var_export($data, true) . '; ?' . '>');
flock($fp, LOCK_UN);
}
}
중요한 부분은 모두 차단 방지하기 LOCK_NB
입니다 연속적인 요청 :
당신이 어 차단하기 위해 무리를()하지 않을 경우 위의 작업 중 하나에 비트 마스크로 LOCK_NB를 추가 할 수도 있습니다 ile 잠금.
코드가 없으면 코드에 큰 병목 현상이 생깁니다.
중요한 추가 부분은 if (!is_array($data)) {
입니다.DB를 쿼리 실패 include
이 경쟁 조건이 발생 빈 문자열의
false
의 결과로
array()
: $ 데이터를 포함 할 수 있기 때문입니다 첫 번째 방문자가이 행을 실행하는 경우 :
,363,210$fp = fopen($cache_file, "c");
다른 방문자가 1 밀리 초 이상이 줄을 실행이 :
if ($fp !== false && flock($fp, LOCK_EX | LOCK_NB)) {
이 첫 번째 방문자가 빈 파일을 생성 의미하지만, 두 번째 방문자는 잠금을 만들고 그래서
include
는 빈 문자열을 반환합니다.$filename = 'index.html'; $loops = 10000; $start = microtime(true); for ($i = 0; $i < $loops; $i++) { file_exists($filename); } echo __LINE__ . ': ' . round(microtime(true) - $start, 5) . PHP_EOL; $start = microtime(true); for ($i = 0; $i < $loops; $i++) { $fp = @fopen($filename, "r"); flock($fp, LOCK_EX | LOCK_NB); } echo __LINE__ . ': ' . round(microtime(true) - $start, 5) . PHP_EOL;
결과 :
file_exists: 0.00949 fopen/flock: 0.06401
P.S.을
그래서 당신은 너무 빨리mkdir()
와의 7 배를 사용을 통해 피할 수있는 많은 함정을 보았다 당신이 볼 수 있듯이mkdir()
앞에file_exists()
을 사용합니다. 이는 my tests (독일어)이 mkdir() 만 사용하여 병목 현상을 유발했기 때문입니다.
"파일을 잠그고 쓰는 동안 스크립트가 멈추는 경우 잠금 파일이 해제됩니다. " - 실행중인 프로세스가 공유 리소스에 대해 갖는 액세스를 제어하기 위해 잠금을 사용하는 경우 이는 매우 바람직한 기능입니다. 프로세스가 종료 된 후에 걸려있는 잠금에 대해 걱정할 필요가 없으므로 처리가 간단 해집니다. 늘 그렇듯이, 궁극적으로 무엇을 성취하려고하는지에 달려 있습니다. – Jason
[@ 연산자] (http://php.net/manual/en/language.operators.errorcontrol.php)를 너무 많이 사용하면 여러 가지 방법으로 폭파 될 수 있습니다. 무슨 일이 있었는지 알아. –
@JosipRodin 답변을 다시 작성했습니다. 나는 여전히 @ 연산자를 사용하지만 설명을 추가했다. – mgutt
mkdir을 기반으로 읽기/쓰기 작업을 중심으로 파일 잠금 - 잠금 해제 패턴을 구현할 수 있습니다. 나는 스트레스를 테스트 해봤고 mgutt와는 달리 병목 현상을 발견하지 못했다. 교착 상태 상황을 돌봐야하는데, 그것은 아마도 mgutt가 경험 한 것일 것입니다. 데드 록은 두 번의 잠금 시도가 서로 대기하는 경우입니다. 잠금 시도시 임의의 간격으로이를 해결할 수 있습니다. 그래서 같이 :
// call this always before reading or writing to your filepath in concurrent situations
function lockFile($filepath){
clearstatcache();
$lockname=$filepath.".lock";
// if the lock already exists, get its age:
[email protected]($lockname);
// attempt to lock, this is the really important atomic action:
while ([email protected]($lockname)){
if ($life)
if ((time()-$life)>120){
//release old locks
rmdir($lockname);
$life=false;
}
usleep(rand(50000,200000));//wait random time before trying again
}
}
그런 다음 파일 경로에서 파일 작업과이 완료되면, 전화 :
function unlockFile($filepath){
$unlockname= $filepath.".lock";
return @rmdir($unlockname);
}
내가 선택한 아니라 최대 PHP의 실행 시간 후 오래된 잠금을 제거 스크립트가 잠금 해제되기 전에 종료됩니다. 더 좋은 방법은 스크립트가 실패 할 때 항상 잠금을 제거하는 것입니다. 이것을위한 깔끔한 방법이 있지만 잊어 버렸습니다.
이 질문에 감사드립니다. 몇 년 전이지만 실용 사례/무리 대치가 건물 가치가 있다고 생각합니다. 나는 이것을 다른 답변에 기반을 두었지만 (비록 PHP 수동 무리 예제를 반영하지만) 파일을 동시에 작성하는 것이 아니라 무리 기능을 대체 할 사람을 찾고있다.) 다음은 충분할 것이라고 생각한다.
function my_flock ($path,$release = false){
if ($release){
@rmdir($path);
} else {
return !file_exists($path) && @mkdir($path);
}
}
- 1. PHP flock() - 내부적으로 무엇입니까?
- 2. 플래시 flv inside flock
- 3. PHP getallheaders 대체
- 4. PHP 정규식 대체 계산
- 5. PHP call_user_func 대체
- 6. PHP Tidy : tidy_setopt() 대체?
- 7. 정규식 패턴 대체 PHP
- 8. PHP - html 숨김 입력 대체
- 9. PHP 다차원 연관 배열 대체
- 10. PHP 정규 표현식을 대체 하시겠습니까?
- 11. PHP/MySQL에서 다중 질의 대체
- 12. PHP 재귀 적 변수 대체
- 13. PHP 대체 구문 강조 표시
- 14. PHP가 아닌 PHP 대체 코드
- 15. PHP : wordpress에서 wpdb 클래스로 대체
- 16. PHP substr 및 strlen 대체
- 17. (monkeypatch) PHP 함수를 대체 할 수 있습니까?
- 18. php 4 함수에서 글로벌 대체 방법
- 19. CGI 스크립트를 대체 할 PHP 스크립트
- 20. Twitter 계정으로의 링크로 @username을 대체 할 PHP
- 21. php : preg 단일 물음표 대체 하시겠습니까?
- 22. 문자열을 PHP file_get_contents의 출력으로 대체 하시겠습니까?
- 23. PHP 대체 제어 구조, 어떤 단점?
- 24. PHP : 사용 preg_replace이다를 대체 할 수있다()
- 25. PHP static :: self를 대체 할 수 있습니까 ::?
- 26. PHP str_replace - 대체 할 두 값?
- 27. PHP 대체 된 문자로 목록 만들기
- 28. PHP DOM 요소를 새 요소로 대체
- 29. PHP가 flock()으로 파일을 잠근 경우 확인 하시겠습니까?
- 30. 대체
'flock()'도 길이가 0 인 파일을 읽지 않도록하려면 불편합니다. 왜냐하면'flock()'은 파일이 생성 된 후에 만 호출 될 수 있기 때문입니다. 새로운 파일을 생성하여 원자 적으로 쓸 수는 없습니다. – rustyx
또한 필수 파일 잠금은 Linux에서 더 이상 사용되지 않으므로 무리는 실제로 이상적이지 않습니다. (설정하기 위해 약간의 작업이 필요합니다) – Antony