กำลังมองหารูปแบบการล็อคแบบกระจาย


10

ฉันจำเป็นต้องมีกลไกการล็อคออบเจ็กต์ซ้ำแบบกำหนดเอง \ pattern สำหรับระบบแบบกระจายใน C # โดยพื้นฐานแล้วฉันมีระบบหลายโหนด แต่ละโหนดมีสิทธิ์การเขียนพิเศษเหนือชิ้นส่วนของn -number สถานะเดียวกันนี้ยังมีอยู่ในรูปแบบอ่านอย่างเดียวบนโหนดอื่นอย่างน้อยหนึ่งโหนด การเขียน / อัปเดตบางรายการจะต้องเป็นแบบปรมาณูในทุกโหนดในขณะที่การอัปเดตอื่น ๆ ในที่สุดจะกลายเป็นกระบวนการจำลองแบบพื้นหลังที่สอดคล้องกันคิว ฯลฯ ...

สำหรับการอัปเดตอะตอมมิกฉันกำลังมองหารูปแบบหรือตัวอย่างที่อนุญาตให้ฉันทำเครื่องหมายวัตถุอย่างถูกล็อกสำหรับการเขียนซึ่งฉันสามารถกระจายกระทำย้อนกลับ ฯลฯ ... เนื่องจากระบบมีการทำงานพร้อมกันในระดับสูงฉัน ฉันคิดว่าฉันจะต้องสามารถล็อคสแต็กอัพซึ่งจะหมดเวลาหรือไม่ได้เปิดเมื่อล็อคถูกปล่อยออกมา

ส่วนการทำธุรกรรมหรือการส่งข้อความนั้นไม่ได้เป็นจุดสนใจของคำถามนี้ แต่ฉันได้จัดเตรียมไว้สำหรับบริบทเพิ่มเติมบางอย่าง จากที่กล่าวมาคุณสามารถพูดได้อย่างชัดเจนว่าคุณต้องการข้อความใดหากคุณต้องการ

นี่คือตัวอย่างที่คลุมเครือของสิ่งที่ฉันคาดคิดแม้ว่าฉันจะเปิดรับความคิดใหม่ ๆ นอกเหนือจากการใช้ผลิตภัณฑ์ใหม่ทั้งหมด

thing.AquireLock(LockLevel.Write);

//Do work

thing.ReleaseLock();

ฉันคิดว่าจะใช้วิธีการขยายซึ่งอาจมีลักษณะเช่นนี้

public static void AquireLock(this IThing instance, TupleLockLevel lockLevel)
{ 
    //TODO: Add aquisition wait, retry, recursion count, timeout support, etc...  
    //TODO: Disallow read lock requests if the 'thing' is already write locked
    //TODO: Throw exception when aquisition fails
    instance.Lock = lockLevel;
}

public static void ReleaseLock(this IThing instance)
{
    instance.Lock = TupleLockLevel.None;
}

เพื่อชี้แจงรายละเอียดบางอย่าง ...

  • การสื่อสารทั้งหมดเป็น TCP / IP โดยใช้โปรโตคอลคำขอ / ตอบกลับแบบไบนารี
  • ไม่มีเทคโนโลยีตัวกลางเช่นคิวหรือฐานข้อมูล
  • ไม่มีโหนดต้นแบบกลาง ในกรณีนี้การจัดเรียงการล็อคถูกกำหนดโดยผู้ริเริ่มการล็อคและคู่ค้าซึ่งจะเคารพคำขอด้วยรูปแบบการหมดเวลาบางรูปแบบเพื่อควบคุมพฤติกรรมของมัน

ใครมีข้อเสนอแนะ?


โดยทั่วไปการล็อคนั้นเป็นคุณสมบัติมาตรฐานในระบบส่วนใหญ่ ฉันเดาว่ามันมีสำหรับ C # เช่นกัน (ผลการค้นหาของ Google: albahari.com/threading/part2.aspx ) คุณกำลังพยายามทำบางสิ่งที่เหนือกว่า Mutex หรือเซมาฟอร์ขั้นพื้นฐานหรือไม่?
Dipan Mehta

2
@DipanMehta ขออภัยฉันควรจะอธิบายเรื่องนี้ให้ชัดเจนยิ่งขึ้น โหนดที่ผมกล่าวถึงเป็นเครื่องบนเครือข่าย ความเข้าใจของฉันเกี่ยวกับ Mutex และ Semaphores คือว่าพวกเขาล็อคทั้งเครื่อง ( เช่นข้ามกระบวนการ ) และไม่ล็อคที่สามารถขยายระหว่างเครื่องในเครือข่าย
JoeGeeky

@JoeGeeky คำถามของคุณอยู่ในหัวข้อที่นี่และจะเป็นไปได้ในทางทฤษฎีเกินไปสำหรับกองมากเกิน หากคุณต้องการถามอีกครั้งคุณสามารถทำได้ แต่คุณต้องการถ้อยคำที่เน้นรหัสมากขึ้น
อดัมเลียร์

คำตอบ:


4

ขอบคุณสำหรับคำชี้แจง

ในกรณีนี้สิ่งที่ฉันแนะนำคือใช้รุ่นเผยแพร่ / สมัครสมาชิก โปรโตคอลการล็อคแบบกระจาย Chubbyของ Google (การนำไปใช้ของPaxos )

ฉันไม่เคยใช้ Paxos (หรืออ้วน) แต่ดูเหมือนว่าจะมีการดำเนินการเปิดแหล่งที่มาที่นี่

หากไม่ได้ทำงานคุณสามารถใช้รุ่นของคุณเอง Paxos ใช้ตัวอย่างเช่นหนึ่งในผู้ต้องสงสัยตามปกติในแง่ของห้องสมุดข้อความที่: ข้อความศูนย์ห้องสมุดคิว , RabbitMQหรือActiveMQ


คำตอบก่อนหน้า:

คำแนะนำส่วนใหญ่เกี่ยวกับ SO ( [A] , [B] ) ไปใช้คิวข้อความเพื่อให้เกิดการล็อคข้ามเครื่อง

AcquireLockวิธีการของคุณจะผลักสิ่งที่ระบุวัตถุล็อคลงในคิวตรวจสอบอินสแตนซ์ของการล็อกก่อนหน้านี้สำเร็จ ReleaseLockวิธีการของคุณจะลบวัตถุล็อคออกจากคิว

เพื่อให้ผู้ใช้แอตแลนติแสดงให้เห็นในโพสต์นี้ , โพสต์เจฟฟ์กุญแจสำหรับบางส่วนของรายละเอียด


ขอบคุณ แต่โซลูชันเหล่านี้จะไม่เหมาะสมเนื่องจากฉันไม่มีต้นแบบหลักฐานข้อมูลหรือคิว ฉันได้อัปเดตคำถามพร้อมรายละเอียดเพิ่มเติมเพื่อชี้แจงรายละเอียดเหล่านี้
JoeGeeky

ฉันจะไม่สามารถใช้ผลิตภัณฑ์เหล่านี้ได้โดยตรงเนื่องจากมีโปรโตคอลที่ดีที่ฉันต้องใช้สำหรับการสื่อสารระหว่างโหนดทั้งหมด แต่ Chubby และ Paxos อาจมีรูปแบบที่กำหนดไว้อย่างดีที่ฉันสามารถเรียนรู้ได้ ฉันจะดู
JoeGeeky

@JoeGeeky ใช่ลิงก์ Paxosมีไดอะแกรมลำดับที่อาจทำให้คุณสามารถใช้งานได้โดยใช้ลิงก์การสื่อสารที่คุณต้องการ
Peter K.

แม้ว่าจะไม่ใช่คำตอบโดยตรงการอ่านทุกสิ่งที่ Chubby และ Paxos ช่วยฉันกำหนดวิธีการแก้ปัญหาของฉันเอง ฉันไม่ได้ใช้เครื่องมือเหล่านั้น แต่สามารถกำหนดรูปแบบที่เหมาะสมตามแนวคิดบางอย่างของพวกเขา ขอบคุณ
JoeGeeky

@ JoeGeeky: ดีใจที่ได้ยินว่ามันช่วยได้อย่างน้อย ขอบคุณสำหรับเห็บ
Peter K.

4

ดูเหมือนว่าฉันจะมีเทคโนโลยีผสมอยู่สองอย่างที่นี่:

  • การสื่อสาร (ซึ่งคุณต้องพึ่งพาความน่าเชื่อถือ 100% ... ซึ่งอาจถึงแก่ชีวิตได้)

  • การล็อค / การแยกกัน

  • หมดเวลา (เพื่อวัตถุประสงค์อะไร)?

คำเตือน: หมดเวลาในระบบกระจายอาจเต็มไปด้วยอันตรายและความยากลำบาก หากใช้พวกเขาจะต้องตั้งค่าและใช้อย่างระมัดระวังเพราะการใช้การหมดเวลาไม่ได้แก้ไขปัญหามันเพียงแค่ทำลายภัยพิบัติ (หากคุณต้องการดูว่าควรใช้การหมดเวลาอย่างไรโปรดอ่านและทำความเข้าใจเอกสารประกอบโปรโตคอลการสื่อสาร HDLC นี่เป็นตัวอย่างที่ดีของการใช้งานที่เหมาะสมและชาญฉลาดร่วมกับระบบการเข้ารหัสบิตที่ฉลาดเพื่อให้สามารถตรวจจับสิ่งต่าง ๆ เช่นสาย IDLE) .

ในบางครั้งฉันทำงานในระบบกระจายหลายโปรเซสเซอร์เชื่อมต่อโดยใช้ลิงก์การสื่อสาร (ไม่ใช่ TCP เป็นอย่างอื่น) หนึ่งในสิ่งที่ฉันได้เรียนรู้ก็คือโดยทั่วไปแล้วคร่าว ๆ มีสถานที่หลายโปรแกรมอันตรายที่จะไป:

  • การพึ่งพาคิวมักจะจบลงด้วยน้ำตา (หากคิวเต็มคุณมีปัญหาเว้นแต่คุณสามารถคำนวณขนาดคิวที่ไม่เคยเติมได้ซึ่งในกรณีนี้คุณอาจใช้วิธีแก้ปัญหาแบบไม่มีคิว)

  • ความเชื่อมั่นในการล็อคนั้นเจ็บปวดลองและคิดว่ามีวิธีอื่น (ถ้าคุณต้องใช้การล็อกดูที่วรรณกรรมการล็อกแบบกระจายตัวประมวลผลหลายตัวเป็นหัวข้อของเอกสารเกี่ยวกับโรคโลหิตจางในช่วง 2-3 ทศวรรษที่ผ่านมา)

ฉันต้องทำการล็อคต่อไปแล้ว:

ฉันจะสันนิษฐานว่าคุณจะใช้การหมดเวลาเป็นวิธีการกู้คืนข้อมูลสุดท้ายเท่านั้น - เช่นการตรวจสอบความล้มเหลวของระบบสื่อสารพื้นฐาน ฉันจะสมมติต่อไปว่าระบบการสื่อสาร TCP / IP ของคุณมีแบนด์วิดท์สูงและอาจคิดว่ามีความหน่วงต่ำ (เป็นศูนย์ แต่ไม่เคยเกิดขึ้น)

สิ่งที่ฉันอยากจะแนะนำก็คือทุก ๆ โหนดมีรายการการเชื่อมต่อของโหนดอื่น ๆ ที่มันสามารถเชื่อมต่อได้ (โหนดจะไม่สนใจว่าการเชื่อมต่อมาจากไหน) จำนวนประชากรของตารางที่โหนดที่โหนดสามารถเชื่อมต่อนั้นถูกทิ้งไว้เป็นสิ่งที่แยกออกจากกันคุณยังไม่ได้พูดว่าจะตั้งค่าแบบคงที่หรืออย่างอื่น สิ่งที่ต้องสนใจก็คือการจัดสรรหมายเลขพอร์ต IP ที่การเชื่อมต่อจะเข้าสู่โหนดซึ่งอาจมีเหตุผลที่ดีในการยอมรับการร้องขอบนพอร์ตเดียวเท่านั้นหรือหลายพอร์ต สิ่งนี้จะต้องได้รับการพิจารณาอย่างรอบคอบ ปัจจัยจะรวมถึงการเข้าคิวโดยนัยการสั่งการใช้ทรัพยากรประเภทระบบปฏิบัติการและความสามารถ

เมื่อโหนดรู้ว่าพวกเขาเชื่อมต่อกับใครพวกเขาสามารถส่งไปยังโหนดนั้นเพื่อขอล็อคและจะต้องได้รับกลับมาจากการตอบกลับล็อคจากโหนดระยะไกลที่ คุณสามารถแพ็คการดำเนินการทั้งสองนี้ไว้ใน wrapper เพื่อทำให้ดูเป็นอะตอม ผลของสิ่งนี้คือโหนดที่ต้องการรับการล็อคจะทำการโทรออกดังนี้:

if (get_lock(remote_node) == timeout) then
  {
    take some failure action - the comms network is down
  }

/* Lock is now acquired - do work here */

if (release_lock(remote_node) == timeout) then
  {
    take some failure action - the comms network is down
  }

การเรียกใช้ get_lock และ release_lock ควรเป็นดังนี้ (โดยหลักการ):

send_to_remote_node(lock_request)
get_from_remote_node_or_timeout(lock_reply, time)
if (result was timeout) then
  return timeout
else
  return ok

คุณจะต้องระมัดระวังเป็นอย่างมากด้วยระบบล็อคแบบกระจายที่หน่วยของงานที่ดำเนินการในขณะที่การล็อคจะมีขนาดเล็กและรวดเร็วเพราะคุณจะมีโหนดระยะไกลจำนวนมากที่อาจหยุดรอการล็อค นี่เป็นระบบมัลติโพรเซสเซอร์ / การสื่อสารแบบหยุดและรอที่มีประสิทธิภาพซึ่งมีประสิทธิภาพ แต่ไม่มีประสิทธิภาพสูงสุดที่เป็นไปได้

ข้อเสนอแนะคือใช้แนวทางที่แตกต่างอย่างสิ้นเชิง คุณสามารถใช้การเรียกโพรซีเดอร์แบบรีโมตที่การเรียกใช้ RPC แต่ละครั้งมีแพ็กเกจข้อมูลซึ่งผู้รับสามารถจัดการได้และสิ่งใดที่ไม่ต้องการการล็อค?


เมื่ออ่านคำถามอีกครั้งดูเหมือนว่าคุณไม่ต้องการกังวลกับเรื่องการสื่อสารของสิ่งต่าง ๆ คุณเพียงต้องการแก้ปัญหาการล็อคของคุณ

คำตอบของฉันอาจดูเล็กน้อยนอกหัวข้ออย่างไรก็ตามฉันเชื่อว่าคุณไม่สามารถแก้ปัญหาการล็อคของคุณได้หากไม่ได้รับชิ้นส่วนด้านล่างเช่นกัน การเปรียบเทียบ: การสร้างบ้านบนรากฐานที่ไม่ดีทำให้ล้มลง ... ในที่สุด


1
ซีแมนทิกส์หมดเวลาส่วนใหญ่จะมีการจัดการกับโหนดที่หายไปจากเครือข่ายหรือเพื่อจัดการกับ backlogs ขนาดใหญ่ในการล็อคสแต็ค ... ซึ่งจะ จำกัด เวลาที่ใช้ในการบล็อกในขณะที่รอการล็อคและจะช่วยให้ผู้ร้องขอโอกาส เพื่อเริ่มกระบวนการอื่น ๆ ท่ามกลางความล่าช้าที่ไม่คาดคิดความล้มเหลว ฯลฯ ... นอกจากนี้สิ่งนี้จะป้องกันบางสิ่งบางอย่างจากการถูกล็อคตลอดไปในกรณีที่บางสิ่งบางอย่างล้มเหลว ฉันขอขอบคุณข้อกังวลของคุณแม้ว่า ณ จุดนี้ฉันไม่เห็นทางเลือกใด ๆ เนื่องจากในที่สุดบางสิ่งบางอย่างจะล้มเหลว
JoeGeeky

เพื่อพูดคุยกับความคิดเห็นอื่น ๆ ของคุณฉันไม่ได้ใช้คิวต่อ se (ในแง่ของการสื่อสารแบบ async) แม้ว่าฉันจะคาดหวังว่าล็อคจะถูกสแต็กและวางจำหน่ายโดยใช้รูปแบบ FIFO ฉันยังไม่ได้รับการคืนดีว่ามันจะทำงานอย่างไรในแง่ของรูปแบบคำขอ / การตอบสนองที่ต้องการนอกเหนือจากนี้จะต้องปิดกั้นในบางวิธีและเป็นส่วนหนึ่งของการจับมือกัน ในขณะนี้ฉันกำลังทำงานผ่านกลไกการล็อคแบบซ้อนภายในโหนดเดียวและวิธีการทำงานผ่านสถานการณ์แบบกระจาย ฉันจะอ่านเพิ่มอีกเล็กน้อยตามที่คุณแนะนำ ขอบคุณ
JoeGeeky

@JoeGeeky - FIFO เป็นคิว ระวังคิว คิดด้านนั้นให้รอบคอบ ฟังดูเหมือนว่าคุณจะไม่ได้อะไรจาก "ชั้นวาง" แต่จะต้องคิดถึงปัญหาและทางออกของคุณอย่างรอบคอบ
quick_now

ฉันเข้าใจ ... ฉันพยายามชี้แจงความแตกต่าง btwn คิว FIFO ที่ใช้ในกระบวนการ async ( เช่นกระบวนการหนึ่ง enqueues และจากนั้น dequeues อื่น ) ในกรณีนี้สิ่งต่างๆจะต้องได้รับการจัดการตามลำดับ แต่กระบวนการที่เข้าสู่คิวจะไม่ออกจนกว่า (a) พวกเขาได้รับการล็อค (b) ถูกปฏิเสธการล็อคหรือ (c) หมดเวลาและออกจากสาย เหมือนยืนอยู่แถวตู้เอทีเอ็ม สิ่งนี้ทำหน้าที่เหมือนรูปแบบ FIFO ในกรณีที่ประสบความสำเร็จ แต่กระบวนการสามารถละทิ้งได้ก่อนที่จะถึงด้านหน้าของบรรทัด ส่วนนอกชั้นวาง? ไม่ แต่นี่ไม่ใช่ปัญหาใหม่
JoeGeeky

0

คำถามของคุณสามารถใช้งานได้ง่ายโดยใช้แคชแบบกระจายเช่น NCache สิ่งที่คุณต้องการคือกลไกการล็อคในแง่ร้ายซึ่งคุณสามารถรับล็อคโดยใช้วัตถุ จากนั้นทำงานและการดำเนินการของคุณและปลดล็อคเพื่อให้แอปพลิเคชันอื่น ๆ สามารถใช้งานได้ในภายหลัง

ลองดูรหัสต่อไปนี้

ที่นี่คุณจะได้รับการล็อคกุญแจที่เฉพาะเจาะจงแล้วดำเนินการงาน (ตั้งแต่การดำเนินการอย่างน้อยหนึ่งครั้ง) จากนั้นก็ปลดล็อคเมื่อคุณทำเสร็จแล้ว

// Instance of the object used to lock and unlock cache items in NCache
LockHandle lockHandle = new LockHandle();

// Specify time span of 10 sec for which the item remains locked
// NCache will auto release the lock after 10 seconds.
TimeSpan lockSpan = new TimeSpan(0, 0, 10); 

try
{
    // If item fetch is successful, lockHandle object will be populated
    // The lockHandle object will be used to unlock the cache item
    // acquireLock should be true if you want to acquire to the lock.
    // If item does not exists, account will be null
    BankAccount account = cache.Get(key, lockSpan, 
    ref lockHandle, acquireLock) as BankAccount;
    // Lock acquired otherwise it will throw LockingException exception

    if(account != null && account.IsActive)
    {
        // Withdraw money or Deposit
        account.Balance += withdrawAmount;
        // account.Balance -= depositAmount;

        // Insert the data in the cache and release the lock simultaneously 
        // LockHandle initially used to lock the item must be provided
        // releaseLock should be true to release the lock, otherwise false
        cache.Insert("Key", account, lockHandle, releaseLock); 
        //For your case you should use cache.Unlock("Key", lockHandle);
    }
    else
    {
        // Either does not exist or unable to cast
        // Explicitly release the lock in case of errors
        cache.Unlock("Key", lockHandle);
    } 
}
catch(LockingException lockException)
{
    // Lock couldn't be acquired
    // Wait and try again
}

นำมาจากลิงค์: http://blogs.alachisoft.com/ncache/distributed-locking/

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.