2008-11-28 35 views
17

Tôi đang cố gắng cập nhật một biến trong APC và sẽ có nhiều quy trình cố gắng thực hiện điều đó.cách tốt nhất để lấy khóa trong php

APC không cung cấp chức năng khóa, vì vậy tôi đang xem xét sử dụng các cơ chế khác ... những gì tôi đã tìm thấy cho đến nay là GET_LOCK() của tôi và tệp php(). Bất cứ điều gì khác đáng xem xét?

Cập nhật: Tôi đã tìm thấy sem_acquire, nhưng dường như đây là khóa chặn.

+0

Biến chứa, chính xác; tại sao bạn lo lắng về khóa? Bạn có thể giải quyết được vấn đề. – Rob

+1

Cảnh báo từ (muộn): MySQL GET_LOCK() có hành vi rất nguy hiểm.Một GET_LOCK() thứ hai lặng lẽ giải phóng khóa cũ trên cùng một kết nối. MySQL chỉ có thể giữ một khóa cho mỗi kết nối. Khóa lồng nhau là không thể với chứng khoán MySQL. Nó không nên được sử dụng cho mục đích chung khóa. – korkman

Trả lời

2

Nếu bạn không ngại đặt khóa trên hệ thống tệp, bạn có thể sử dụng fopen() với chế độ 'x'. Dưới đây là một ví dụ:

$f = fopen("lockFile.txt", 'x'); 
if($f) { 
    $me = getmypid(); 
    $now = date('Y-m-d H:i:s'); 
    fwrite($f, "Locked by $me at $now\n"); 
    fclose($f); 
    doStuffInLock(); 
    unlink("lockFile.txt"); // unlock   
} 
else { 
    echo "File is locked: " . get_file_contents("lockFile.txt"); 
    exit; 
} 

Xem www.php.net/fopen

+0

Miễn là bạn không bao giờ cần NFS, đây có lẽ là giải pháp dễ nhất. Mặc dù có một cơ hội tốt để có được một điều kiện chủng tộc hoặc tệ hơn một đống lên nếu kịch bản khóa bị treo trước khi giải phóng đàn. – David

+1

Có, bạn có thể nhận được một đống lên nếu kịch bản bị treo, nhưng có nhiều cách để làm việc xung quanh đó, hoặc ít nhất là phát hiện vấn đề bằng cách sử dụng PID và thời gian được ghi bên trong tệp khóa và gửi email. –

3

Trên thực tế, kiểm tra xem nếu điều này sẽ làm việc tốt hơn sau đó gợi ý Phêrô.

http://us2.php.net/flock

sử dụng một khóa độc quyền và nếu cảm thấy thoải mái với nó, đặt tất cả mọi thứ khác mà cố gắng để khóa các tập tin trong một giấc ngủ thứ hai 2-3. Nếu được thực hiện đúng, trang web của bạn sẽ bị treo liên quan đến tài nguyên bị khóa nhưng không phải là một loạt các tập lệnh chiến đấu để lưu lại quá trình lấy mẫu.

4

Nếu điểm của khóa là ngăn chặn nhiều quá trình cố gắng điền một khóa bộ nhớ cache trống, tại sao bạn không muốn có khóa chặn?


    $value = apc_fetch($KEY); 

    if ($value === FALSE) { 
     shm_acquire($SEMAPHORE); 

     $recheck_value = apc_fetch($KEY); 
     if ($recheck_value !== FALSE) { 
     $new_value = expensive_operation(); 
     apc_store($KEY, $new_value); 
     $value = $new_value; 
     } else { 
     $value = $recheck_value; 
     } 

     shm_release($SEMAPHORE); 
    } 

Nếu bộ nhớ cache tốt, bạn chỉ cần cuộn với bộ nhớ cache. Nếu không có gì trong bộ nhớ cache, bạn sẽ nhận được một khóa. Một khi bạn có khóa, bạn sẽ cần phải kiểm tra lại bộ nhớ cache để đảm bảo rằng, trong khi bạn đang chờ để có được khóa, bộ nhớ cache đã không được repopulated. Nếu bộ nhớ cache được repopulated, sử dụng giá trị đó & giải phóng khóa, nếu không, bạn thực hiện tính toán, điền vào bộ nhớ cache & rồi nhả khóa của bạn.

+1

Lý do không sử dụng khóa chặn là vì sẽ có vô số các quá trình đó, nó sẽ làm chậm đáng kể chúng. Tôi muốn họ không cập nhật các biến hơn là chờ đợi và gây ra một cuộc khủng hoảng khi họ tích lũy. – tpk

0

Những gì tôi thấy, thực ra, là tôi không cần bất kỳ khóa nào ... cho những gì tôi đang cố tạo là bản đồ của tất cả các class => path associations cho autoload, nó không không quan trọng nếu một quá trình ghi đè lên những gì người khác đã tìm thấy (nó rất khó, nếu mã hóa đúng), bởi vì dữ liệu sẽ đạt được điều đó cuối cùng anyway. Vì vậy, giải pháp hóa ra là "không có khóa".

+4

Nếu đúng như vậy, bạn nên đóng câu hỏi. – Powerlord

1

Tôi nhận ra đây là một năm cũ, nhưng tôi chỉ vấp khi đặt câu hỏi trong khi thực hiện một số nghiên cứu bản thân mình về khóa trong PHP.

Nó xảy ra với tôi rằng một giải pháp có thể là có thể bằng cách sử dụng APC chính nó. Hãy gọi cho tôi điên, nhưng điều này có thể là một cách tiếp cận hoàn toàn khả thi:

function acquire_lock($key, $expire=60) { 
    if (is_locked($key)) { 
     return null; 
    } 
    return apc_store($key, true, $expire); 
} 

function release_lock($key) { 
    if (!is_locked($key)) { 
     return null; 
    } 
    return apc_delete($key); 
} 

function is_locked($key) { 
    return apc_fetch($key); 
} 

// example use 
if (acquire_lock("foo")) { 
    do_something_that_requires_a_lock(); 
    release_lock("foo"); 
} 

Trên thực tế tôi có thể vứt chức năng khác trong đó để tạo ra một chìa khóa để sử dụng ở đây, chỉ cần để ngăn chặn va chạm với một chìa khóa APC hiện có, ví dụ:

function key_for_lock($str) { 
    return md5($str."locked"); 
} 

Thông số $expire là một tính năng tốt đẹp của APC để sử dụng, vì nó ngăn khóa của bạn bị giữ mãi nếu tập lệnh của bạn bị chết hoặc tương tự.

Hy vọng câu trả lời này hữu ích cho bất kỳ ai khác tình cờ gặp ở đây một năm sau đó.

+4

Vì 'aquire_lock' không phải là nguyên tử, nó không thực sự hữu ích khi bạn cần khóa vì truy cập đồng thời vào một số tài nguyên. – cweiske

+2

Không phải giải pháp an toàn chủ đề – zerkms

+1

Nếu tập lệnh chết, APC có phát hành không? –

13
/* 
CLASS ExclusiveLock 
Description 
================================================================== 
This is a pseudo implementation of mutex since php does not have 
any thread synchronization objects 
This class uses flock() as a base to provide locking functionality. 
Lock will be released in following cases 
1 - user calls unlock 
2 - when this lock object gets deleted 
3 - when request or script ends 
================================================================== 
Usage: 

//get the lock 
$lock = new ExclusiveLock("mylock"); 

//lock 
if($lock->lock() == FALSE) 
    error("Locking failed"); 
//-- 
//Do your work here 
//-- 

//unlock 
$lock->unlock(); 
=================================================================== 
*/ 
class ExclusiveLock 
{ 
    protected $key = null; //user given value 
    protected $file = null; //resource to lock 
    protected $own = FALSE; //have we locked resource 

    function __construct($key) 
    { 
     $this->key = $key; 
     //create a new resource or get exisitng with same key 
     $this->file = fopen("$key.lockfile", 'w+'); 
    } 


    function __destruct() 
    { 
     if($this->own == TRUE) 
      $this->unlock(); 
    } 


    function lock() 
    { 
     if(!flock($this->file, LOCK_EX | LOCK_NB)) 
     { //failed 
      $key = $this->key; 
      error_log("ExclusiveLock::acquire_lock FAILED to acquire lock [$key]"); 
      return FALSE; 
     } 
     ftruncate($this->file, 0); // truncate file 
     //write something to just help debugging 
     fwrite($this->file, "Locked\n"); 
     fflush($this->file); 

     $this->own = TRUE; 
     return TRUE; // success 
    } 


    function unlock() 
    { 
     $key = $this->key; 
     if($this->own == TRUE) 
     { 
      if(!flock($this->file, LOCK_UN)) 
      { //failed 
       error_log("ExclusiveLock::lock FAILED to release lock [$key]"); 
       return FALSE; 
      } 
      ftruncate($this->file, 0); // truncate file 
      //write something to just help debugging 
      fwrite($this->file, "Unlocked\n"); 
      fflush($this->file); 
      $this->own = FALSE; 
     } 
     else 
     { 
      error_log("ExclusiveLock::unlock called on [$key] but its not acquired by caller"); 
     } 
     return TRUE; // success 
    } 
}; 
+3

__destruct sẽ không bị gọi là lỗi nghiêm trọng? Tôi lo lắng về các tình huống có thể khiến khóa bị kẹt vĩnh viễn và yêu cầu can thiệp thủ công – MeatPopsicle

+1

'flock's được xóa khi tệp bị đóng và tệp được đóng khi quá trình thoát php. Vì vậy, không phải là một vấn đề. – Sam

+0

gọi 'flock' và khóa một tệp trên đĩa là tốn kém hơn nhiều so với hoạt động APC thực tế, do đó một cách rất tốn kém để phối hợp bộ đệm APC. –

7

Bạn có thể sử dụng chức năng apc_add để đạt được điều này mà không cần sử dụng hệ thống tệp hoặc mysql. apc_add chỉ thành công khi biến chưa được lưu trữ; do đó, cung cấp một cơ chế khóa. TTL có thể được sử dụng để đảm bảo rằng lockholders falied sẽ không tiếp tục giữ khóa mãi mãi.

Lý do apc_add là giải pháp chính xác vì nó tránh điều kiện cuộc đua tồn tại giữa việc kiểm tra khóa và đặt nó thành 'bị khóa bởi bạn'. Vì apc_add chỉ đặt giá trị nếu nó không phải là đã đặt ("thêm" nó vào bộ nhớ cache), nó đảm bảo rằng khóa không thể được thu hút bởi hai cuộc gọi cùng một lúc, bất kể khoảng cách của chúng trong thời gian nào. Không có giải pháp nào không kiểm tra đặt khóa cùng một lúc sẽ bị tình trạng cuộc đua này; một hoạt động nguyên tử là cần thiết để khóa thành công mà không có tình trạng đua.

Vì khóa APC sẽ chỉ tồn tại trong ngữ cảnh thực thi php, có lẽ đây không phải là giải pháp tốt nhất cho khóa chung, vì nó không hỗ trợ khóa giữa các máy chủ. Memcache cũng cung cấp chức năng bổ sung nguyên tử và do đó cũng có thể được sử dụng với kỹ thuật này - đó là một phương pháp khóa giữa các máy chủ. Redis cũng hỗ trợ các chức năng 'SETNX' nguyên tử và TTL, và là một phương pháp rất phổ biến về khóa và đồng bộ hóa giữa các máy chủ. Howerver, OP yêu cầu một giải pháp cho APC nói riêng.

+1

Làm thế nào để "khóa" được phát hành nếu quá trình tạo biến ban đầu chết mà không xóa nó? Tôi đoán là không. Tự động giải phóng ổ khóa ngay cả khi thất bại là một tính năng quan trọng của ổ khóa. – Jason

+0

câu hỏi tuyệt vời @ Jason, tôi sẽ mở rộng câu trả lời. –

0

Không thể nói nếu đây là cách tốt nhất để xử lý công việc, nhưng ít nhất nó thuận tiện.

function WhileLocked($pathname, callable $function, $proj = ' ') 
{ 
    // create a semaphore for a given pathname and optional project id 
    $semaphore = sem_get(ftok($pathname, $proj)); // see ftok for details 
    sem_acquire($semaphore); 
    try { 
     // capture result 
     $result = call_user_func($function); 
    } catch (Exception $e) { 
     // release lock and pass on all errors 
     sem_release($semaphore); 
     throw $e; 
    } 

    // also release lock if all is good 
    sem_release($semaphore); 
    return $result; 
} 

Cách sử dụng đơn giản như thế này.

$result = WhileLocked(__FILE__, function() use ($that) { 
    $this->doSomethingNonsimultaneously($that->getFoo()); 
}); 

Đối số tùy chọn thứ ba có thể hữu ích nếu bạn sử dụng hàm này nhiều lần cho mỗi tệp.

Cuối cùng nhưng không kém phần quan trọng là không khó sửa đổi chức năng này (trong khi vẫn giữ chữ ký của nó) để sử dụng bất kỳ loại cơ chế khóa nào khác vào một ngày sau đó, ví dụ: nếu bạn tình cờ thấy mình làm việc với nhiều máy chủ.

0

APC hiện được coi là unmaintained and dead. Đó là người kế thừa APCu cung cấp khóa qua apcu_entry. Nhưng lưu ý rằng nó cũng cấm thực hiện đồng thời các chức năng APCu khác. Tùy thuộc vào trường hợp sử dụng của bạn, điều này có thể được chấp nhận cho bạn.

Từ hướng dẫn:

Lưu ý: Khi kiểm soát vào apcu_entry() khóa cho bộ nhớ cache được mua độc quyền, nó được phát hành khi kiểm soát lá apcu_entry(): Trong thực tế, điều này hóa thân của generator thành một phần quan trọng, không cho phép hai tiến trình thực hiện cùng một đường dẫn mã giống nhau. Ngoài ra, nó cấm thực thi đồng thời bất kỳ chức năng APCu nào khác, vì chúng sẽ có cùng khóa.

Các vấn đề liên quan