ฉันจะหลีกเลี่ยงการหยุดชะงักแบบกระจายในระหว่างการเชื่อมต่อซึ่งกันและกันระหว่างสองโหนดได้อย่างไร


11

สมมติว่าเรามีเพียร์สองโหนด: โหนดแรกสามารถส่งการร้องขอการเชื่อมต่อไปยังหนึ่งที่สอง แต่ยังที่สองที่สามารถส่งการร้องขอการเชื่อมต่อไปยังที่หนึ่ง จะหลีกเลี่ยงการเชื่อมต่อสองครั้งระหว่างสองโหนดได้อย่างไร เมื่อต้องการแก้ไขปัญหานี้มันจะเพียงพอที่จะทำให้การดำเนินการตามลำดับสำหรับการสร้างการเชื่อมต่อ TCP ขาเข้าหรือขาออก

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

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

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

ฉันจะแก้ปัญหานี้ได้อย่างไร

อัปเดต:อย่างไรก็ตามฉันยังต้องล็อครายการทุกครั้งที่มีการสร้างการเชื่อมต่อใหม่ (ขาเข้าหรือขาออก) เนื่องจากเธรดอื่นอาจเข้าถึงรายการนี้ดังนั้นปัญหาการหยุดชะงักจะยังคงอยู่

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

ClientSideLoginRoutine() {
    for each (address in cache) {
        lock (neighbors_table) {
            if (neighbors_table.contains(address)) {
                // there is already a neighbor with the same address
                continue;
            }
            neighbors_table.add(address, status: CONNECTING);

        } // end lock

        // ...
        // The node tries to establish a TCP connection with the remote address
        // and perform the login procedure by sending its listening address (IP and port).
        boolean login_result = // ...
        // ...

        if (login_result)
            lock (neighbors_table)
                neighbors_table.add(address, status: CONNECTED);

    } // end for
}

ServerSideLoginRoutine(remoteListeningAddress) {
    // ...
    // initialization of data structures needed for communication (queues, etc)
    // ...

    lock(neighbors_table) {
        if(neighbors_table.contains(remoteAddress) && its status is CONNECTING) {
            // In this case, the client-side on the same node has already
            // initiated the procedure of logging in to the remote node.

            if (myListeningAddress < remoteListeningAddress) {
                refusesLogin();
                return;
            }
        }
        neighbors_table.add(remoteListeningAddress, status: CONNECTED);

    } // end lock
}

ตัวอย่าง: IP: พอร์ตของโหนด A คือ A: 7001 - พอร์ต IP: ของโหนด B คือ B: 8001

สมมติว่าโหนด A ได้ส่งคำขอเข้าสู่ระบบไปที่โหนด B: 8001 ในกรณีนี้โหนด A เรียกรูทีนการล็อกอินโดยส่งโดยการส่งที่อยู่ที่รับฟังของตัวเอง (A: 7001) ด้วยเหตุนี้เพื่อนบ้าน_tableของโหนด A จึงมีที่อยู่ของรีโมตโหนด (B: 8001): ที่อยู่นี้เชื่อมโยงกับสถานะการเชื่อมต่อ โหนด A กำลังรอให้โหนด B ยอมรับหรือปฏิเสธคำขอล็อกอิน

ในขณะเดียวกันโหนด B อาจส่งคำขอเชื่อมต่อไปยังที่อยู่ของโหนด A (A: 7001) จากนั้นโหนด A อาจประมวลผลคำขอของโหนด B ดังนั้นเพื่อนบ้าน _ ตารางของโหนด B จึงมีที่อยู่ของรีโมต node (A: 7001): ที่อยู่นี้เชื่อมโยงกับสถานะ CONNECTING โหนด B กำลังรอโหนด A ยอมรับหรือปฏิเสธคำขอล็อกอิน

หากฝั่งเซิร์ฟเวอร์ของโหนด A ปฏิเสธการร้องขอจาก B: 8001 ดังนั้นฉันต้องแน่ใจว่าฝั่งเซิร์ฟเวอร์ของโหนด B จะยอมรับคำขอจาก A: 7001 ในทำนองเดียวกันถ้าฝั่งเซิร์ฟเวอร์ของโหนด B ปฏิเสธคำขอจาก A: 7001 ดังนั้นฉันต้องแน่ใจว่าฝั่งเซิร์ฟเวอร์ของโหนด A จะยอมรับคำขอจาก B: 8001

ตามกฎ "ที่อยู่ขนาดเล็ก"ในกรณีนี้โหนด A จะปฏิเสธคำขอเข้าสู่ระบบโดยโหนด B ในขณะที่โหนด B จะยอมรับคำขอจากโหนด A

คุณคิดยังไงเกี่ยวกับที่?


อัลกอริธึมประเภทนี้ค่อนข้างยากที่จะวิเคราะห์และพิสูจน์ อย่างไรก็ตามมีนักวิจัยที่มีความเชี่ยวชาญในด้านการคำนวณแบบกระจาย ลองดูหน้าสิ่งพิมพ์ของ Leslie Lamport ได้ที่: research.microsoft.com/en-us/um/people/lamport/pubs/pubs.html
DeveloperDon

คำตอบ:


3

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


อย่างไรก็ตามฉันยังต้องล็อครายการทุกครั้งที่มีการสร้างการเชื่อมต่อใหม่ (ขาเข้าหรือขาออก) เนื่องจากเธรดอื่นอาจเข้าถึงรายการนี้ดังนั้นปัญหาการหยุดชะงักจะยังคงอยู่
enzom83

@ enzom83 ไม่การหยุดชะงักไม่สามารถทำได้ภายใต้โครงร่างนี้เนื่องจากเพียร์ไม่จำเป็นต้องรอให้คุณดำเนินการตามที่ต้องการให้เสร็จสมบูรณ์ Mutex คือการปกป้อง internals ของรายการของคุณ เมื่อคุณได้รับแล้วคุณจะออกจากจำนวนเวลาที่ทราบเพราะคุณไม่จำเป็นต้องรอทรัพยากรอื่น ๆ ภายในส่วนที่สำคัญ
dasblinkenlight

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

1
@ enzom83 ตราบใดที่คุณไม่ขอการเชื่อมต่อจากในส่วนที่สำคัญคุณจะไม่ได้รับการหยุดชะงักแบบกระจาย นั่นเป็นแนวคิดที่อยู่เบื้องหลังวิธีการมองโลกในแง่ดี - คุณทำการล็อกในรายการเพื่อเพิ่มหรือลบโหนดเท่านั้นและหากคุณพบการเชื่อมต่อซึ่งกันและกันเมื่อเพิ่มโหนดคุณจะแตกหลังจากออกจากส่วนสำคัญโดยใช้ "ที่อยู่ขนาดเล็กลง" กฎ (จากคำตอบ)
dasblinkenlight

4

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

เวลา 1: A พยายามเชื่อมต่อกับ B ส่งหมายเลข 123

เวลา 2: B พยายามเชื่อมต่อกับ A ส่งหมายเลข 234

เวลา 3: B รับคำขอของ A เนื่องจากคำขอของ B มีจำนวนมากกว่าจึงปฏิเสธคำขอของ A

เวลา 4: A รับคำขอของ B เนื่องจากคำขอของ B มีจำนวนที่สูงกว่า A จึงยอมรับและลดคำขอลง

ในการสร้างหมายเลขสุ่มตรวจสอบให้แน่ใจว่าคุณได้สร้างตัวสร้างตัวเลขสุ่มด้วย / dev / urandom แทนที่จะใช้การเริ่มต้นเริ่มต้นของตัวสร้างตัวเลขสุ่มซึ่งมักจะขึ้นอยู่กับเวลาของนาฬิกาแขวน: มีโอกาสไม่น่าสนใจที่สองโหนด จะได้รับเมล็ดเดียวกัน

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


3

หากฉันเข้าใจปัญหาที่คุณพยายามหลีกเลี่ยงจะเป็นดังนี้:

  • Node1 ร้องขอการเชื่อมต่อจากโหนด 2
  • Node1 ล็อครายการเชื่อมต่อ
  • Node2 ร้องขอการเชื่อมต่อจากโหนด 1
  • Node2 ล็อครายการเชื่อมต่อ
  • Node2 ได้รับการร้องขอการเชื่อมต่อจาก node1 ปฏิเสธเนื่องจากรายการถูกล็อค
  • Node1 ได้รับการร้องขอการเชื่อมต่อจาก node2 ปฏิเสธเนื่องจากรายการถูกล็อค
  • ทั้งคู่จบลงที่การเชื่อมต่อซึ่งกันและกัน

ฉันสามารถคิดถึงวิธีที่แตกต่างกันสองสามข้อในการจัดการกับสิ่งนี้

  1. หากคุณพยายามเชื่อมต่อกับโหนดและปฏิเสธคำขอของคุณด้วยข้อความ "รายการถูกล็อค" ให้รอจำนวนมิลลิวินาทีสุ่มก่อนที่จะลองอีกครั้ง (การสุ่มนั้นสำคัญมากทำให้มีโอกาสน้อยกว่าที่ทั้งสองจะรอเวลาเท่ากันและทำซ้ำปัญหาเดียวกันกับinfinitum )
  2. ซิงโครไนซ์นาฬิกาทั้งสองระบบกับเซิร์ฟเวอร์เวลาและส่งการประทับเวลาพร้อมกับการร้องขอการเชื่อมต่อ หากคำขอเชื่อมต่อมาจากโหนดที่คุณพยายามเชื่อมต่ออยู่ในขณะนั้นทั้งสองโหนดยอมรับว่าสิ่งใดที่พยายามเชื่อมต่อชัยชนะครั้งแรกและการเชื่อมต่ออื่นจะถูกปิด

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