Giả sử chúng ta có hai nút ngang hàng: nút đầu tiên có thể gửi yêu cầu kết nối đến nút thứ hai, nhưng nút thứ hai có thể gửi yêu cầu kết nối đến nút đầu tiên. Làm thế nào để tránh kết nối kép giữa hai nút? Để giải quyết vấn đề này, sẽ đủ để thực hiện tuần tự các hoạt động được thực hiện để tạo kết nối TCP trong hoặc ngoài nước.
Điều này có nghĩa là mỗi nút nên xử lý tuần tự từng thao tác tạo kết nối mới, cho cả kết nối đến và kết nối đi. Theo cách này, việc duy trì danh sách các nút được kết nối, trước khi chấp nhận kết nối mới đến từ một nút hoặc trước khi gửi yêu cầu kết nối đến một nút, sẽ đủ để kiểm tra xem nút này đã có trong danh sách chưa.
Để thực hiện tuần tự các hoạt động tạo kết nối, việc thực hiện khóa trong danh sách các nút được kết nối là đủ : trên thực tế, đối với mỗi kết nối mới, mã định danh của nút được kết nối mới được thêm vào danh sách này. Tuy nhiên, tôi tự hỏi liệu cách tiếp cận này có thể gây ra bế tắc phân tán :
- nút đầu tiên có thể gửi yêu cầu kết nối đến nút thứ hai;
- nút thứ hai có thể gửi yêu cầu kết nối đến nút đầu tiên;
- giả sử rằng hai yêu cầu kết nối không đồng bộ, cả hai nút sẽ khóa mọi yêu cầu kết nối đến.
Làm thế nào tôi có thể giải quyết vấn đề này?
CẬP NHẬT: Tuy nhiên, tôi vẫn phải khóa danh sách mỗi khi kết nối mới (đến hoặc đi) được tạo, vì các luồng khác có thể truy cập danh sách này, sau đó vấn đề bế tắc sẽ vẫn còn.
CẬP NHẬT 2: Dựa trên lời khuyên của bạn, tôi đã viết một thuật toán để ngăn chặn sự chấp nhận lẫn nhau của một yêu cầu đăng nhập. Vì mỗi nút là một mạng ngang hàng, nên nó có thể có một thói quen máy khách để gửi các yêu cầu kết nối mới và một thói quen máy chủ để chấp nhận các kết nối đến.
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
}
Ví dụ: Cổng IP: của nút A là A: 7001 - Cổng IP: của nút B là B: 8001.
Giả sử rằng nút A đã gửi yêu cầu đăng nhập đến nút B: 8001. Trong trường hợp này, nút A gọi thủ tục đăng nhập bằng cách gửi bằng cách gửi địa chỉ nghe của chính nó (A: 7001). Kết quả là, hàng xóm_table của nút A chứa địa chỉ của nút từ xa (B: 8001): địa chỉ này được liên kết với trạng thái KẾT NỐI. Nút A đang chờ nút B chấp nhận hoặc từ chối yêu cầu đăng nhập.
Trong khi đó, nút B cũng có thể đã gửi yêu cầu kết nối đến địa chỉ của nút A (A: 7001), sau đó nút A có thể đang xử lý yêu cầu của nút B. Vì vậy, hàng xóm_table của nút B chứa địa chỉ của điều khiển từ xa nút (A: 7001): địa chỉ này được liên kết với trạng thái KẾT NỐI. Nút B đang chờ nút A chấp nhận hoặc từ chối yêu cầu đăng nhập.
Nếu phía máy chủ của nút A từ chối yêu cầu từ B: 8001, thì tôi phải chắc chắn rằng phía máy chủ của nút B sẽ chấp nhận yêu cầu từ A: 7001. Tương tự, nếu phía máy chủ của nút B từ chối yêu cầu từ A: 7001, thì tôi phải chắc chắn rằng phía máy chủ của nút A sẽ chấp nhận yêu cầu từ B: 8001.
Theo quy tắc "địa chỉ nhỏ" , trong trường hợp này, nút A sẽ từ chối yêu cầu đăng nhập của nút B, trong khi nút B sẽ chấp nhận yêu cầu từ nút A.
Bạn nghĩ gì về điều này?