C\#Zookeeper 분포 식 잠 금 을 실현 하 는 참고 예시

분산 식 자물쇠
인터넷 초기 에 우리 시스템 은 보통 단일 배치,즉 한 서버 에서 시스템 의 배 치 를 완 성 했 습 니 다.후기 에 사용자 수가 증가 하면 서 서버 의 압력 도 점점 커지 고 응답 속도 가 느 려 지 며 심지어 서버 가 무 너 지 는 상황 이 발생 했 습 니 다.
서버 의 압력 이 너무 크 고 응답 이 느 린 특징 을 해결 하기 위해 분포 식 시스템 배치 가 나 타 났 다.
쉽게 말 하면 우 리 는 시스템 자원 을 여러 대의 서버 에 배치 한 다음 에 한 대의 서버 를 입구 프 록 시 로 하고 일부 결정 에 따라 받 은 요청 을 자원 서버 에 전송 하 는 것 이다.이것 은 바로 우리 가 흔히 말 하 는 역방향 프 록 시(일반적으로 nginx 를 사용 하 는 것)이다.

 분산 식 으로 서버 압력 문 제 를 해 결 했 지만 새로운 문제 도 가 져 왔 다.
예 를 들 어 우 리 는 주문 통 계 를 하 는 기능 이 있 습 니 다.주문 을 완성 한 후에 통계 기능 을 실행 해 야 합 니 다.높 은 방문 상황 에서 두 개의 주문 요청(A 와 B)이 동시에 완성 한 다음 에 통계 기능 을 같이 실 행 했 을 수 있 습 니 다.그러면 발생 할 수 있 는 결 과 는 A 가 B 요청 데 이 터 를 통계 하지 않 았 고 B 요청 도 A 요청 데 이 터 를 통계 하지 않 았 을 수도 있 습 니 다.이렇게 해서 데이터 의 통계 적 오 류 를 초래 했다.이 문제 가 발생 하 는 근본 적 인 원인 은 바로 통계 기능 의 병행 으로 인 한 것 이다.만약 에 단일 배치 시스템 이 라면 우 리 는 하나의 자물쇠 조작 을 간단하게 사용 하면 완성 할 수 있 지만 분포 식 환경 에서 A 와 B 요청 이 두 서버 에서 동시에 실 행 될 수 있 고 일반적인 자 물 쇠 는 효과 가 없 을 것 이다.이 럴 때 는 분포 식 자 물 쇠 를 사용 해 야 한다. 
Zookeeper 분포 식 잠 금 원리
분산 식 잠 금 의 실현 은 여러 가지 가 있 습 니 다.간단 한 것 은 데이터베이스 시트 를 사용 하여 이 를 실현 할 수 있 고 redis 를 사용 하여 이 를 실현 할 수 있 습 니 다.여기 서 사용 할 Zookeeper 는 분포 식 잠 금 을 실현 할 수 있 습 니 다.
Zookeeper 분포 식 잠 금 의 원 리 는 교묘 하 게 znode 임시 노드 의 특징 과 감청(watcher)체 제 를 사용 한 것 이다.감청 체 제 는 매우 간단 하 다.즉,우 리 는 znode 에 감청 기 를 추가 할 수 있다.znode 노드 상태 가 바 뀌 었 을 때(예 를 들 어 데이터 내용 이 바 뀌 고 노드 가 삭제 되 었 을 때)감청 기 에 알 릴 수 있다.
앞의 몇 절 에서 zno de 를 소 개 했 는데 세 가지 유형 이 있어 요.
PERSISTENT:영구적 인 노드 입 니 다.특정한 zno de 를 만 든 클 라 이언 트 가 연결 을 끊 어도 영구적 인 노드 가 존재 합 니 다.기본 적 인 상황 에서 다른 설명 이 없 으 면 모든 znode 가 오래 갑 니 다.
EPHEMERAL:임시 노드,클 라 이언 트 가 연결 상태 일 때 임시 노드 가 유효 합 니 다.클 라 이언 트 와 ZooKeeper 집합 이 연결 을 끊 으 면 임시 노드 가 자동 으로 삭 제 됩 니 다.임시 노드 는 하위 노드 를 허용 하지 않 습 니 다.임시 노드 는 leader 선거 에서 중요 한 역할 을 한다.
SEQUENTIAL:순서 노드 는 지속 적 이거 나 임시 적 일 수 있 습 니 다.새로운 znode 가 하나의 순서 노드 로 만 들 어 졌 을 때 ZooKeeper 는 10 비트 의 시리 얼 번 호 를 원본 이름 에 추가 하여 znode 의 경 로 를 설정 합 니 다.순서 노드 는 잠 금 과 동기 화 에 중요 한 역할 을 합 니 다.
그 중에서 순서 노드 는 오래 지속 되 거나 임시 적 일 수 있 습 니 다.임시 노드 는 그 세 션 을 만 드 는 것 이 특징 입 니 다.세 션 이 끊 어 지면 임시 노드 는 자동 으로 삭 제 됩 니 다.임시 노드 에 감청 기 를 등록 하면 감청 기 는 알림 을 받 습 니 다.임시 노드 에 시간 순서 가 있 으 면그러면 우 리 는 분포 식 자 물 쇠 를 실현 하기 위해 또 하나의 생각 이 있다.
Zookeeper 에 znode 노드/Locker 가 있다 면
1.client 1 이 Zookeeper 에 연결 할 때/Locker 노드 에 하위 노드 가 존재 하 는 지 판단 합 니 다.하위 노드 가 없 으 면/Locker 노드 에서 임시 순서 로 zno de 노드 를 만 듭 니 다./client 1 이 라면 client 1 이 잠 금 상 태 를 가 져 왔 음 을 나타 내 고 client 1 은 계속 실행 할 수 있 습 니 다.
2.client 2 가 Zookeeper 에 연결 할 때 먼저/Locker 노드 에 하위 노드 가 존재 하 는 지 판단 한 다음 에 하위 노드 가 존재 하 는 것 을 발견 한 다음 에/Locker 아래 의 모든 하위 노드 를 가 져 오고 시간 순서에 따라 정렬 합 니 다.마지막 노드,즉/client 1 노드 에 감청 기(watcher 1)를 등록 하 는 동시에/Locker 노드 아래 임시 순서의 znode 노드 를 만 듭 니 다.만약/client 2 라면.동시에 client 2 는 차단 되 고 차단 상태의 방출 은 모니터(watcher 1)에 있 습 니 다.
3.client 3 가 Zookeeper 에 연결 할 때 먼저/Locker 노드 에 하위 노드 가 존재 하 는 지 판단 한 다음 에 하위 노드 가 존재 하 는 것 을 발견 한 다음 에/Locker 아래 의 모든 하위 노드 를 가 져 오고 시간 순서에 따라 정렬 합 니 다.마지막 노드,즉/client 2 노드 에 감청 기(watcher 2)를 등록 하 는 동시에/Locker 노드 아래 임시 순서의 znode 노드 를 만 듭 니 다.만약동시에 client 2 는 차단 되 고 차단 상태의 방출 은 모니터(watcher 2)에 있 습 니 다.
이런 식 으로 유추 하 다.
4.클 라 이언 트 1 이 실행 되면 Zookeeper 의 연결 을 끊 습 니 다./client 1 은 임시 순서 노드 이기 때문에 자동 으로 삭 제 됩 니 다.클 라 이언 트 2 는/client 1 노드 에 모니터(watcher 1)를 등 록 했 기 때문에 watcher 1 은 알림 을 받 고 watcher 1 은 클 라 이언 트 2 의 차단 상 태 를 방출 합 니 다.그래서 client 2 는 잠 금 상 태 를 가 져 와 계속 실행 합 니 다.
5.client 2 가 작업 을 마치 면 Zookeeper 의 연결 을 끊 습 니 다./client 2 는 임시 순서 노드 이기 때문에 자동 으로 삭 제 됩 니 다.client 3 는/client 2 노드 에 모니터(watcher 2)를 등 록 했 기 때문에 watcher 2 는 통 지 를 받 고 watcher 2 는 client 3 의 차단 상 태 를 방출 합 니 다.그래서 client 3 에서 잠 금 상 태 를 가 져 와 계속 실행 합 니 다.
이런 식 으로 유추 하 다.
이렇게 하면 분포 식 환경 에서 몇 대의 서버 가 있 든 지 간 에 프로그램의 줄 을 서 는 것 처럼 실 행 될 수 있다.
C\#Zookeeper 분산 잠 금 실현
이전 절 에 ZookeeperHelper 를 봉 인 했 던 보조 클래스(Zookeeper 기본 튜 토리 얼(4):C\#연결 사용 Zookeeper)가 있 습 니 다.이 보조 클래스 를 사용 하여 ZookeeperLocker 클래스 를 실 현 했 습 니 다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace AspNetCore.ZookeeperConsole
{
    /// <summary>
    ///   Zookeeper     
    /// </summary>
    public class ZookeeperLocker : IDisposable
    {
        /// <summary>
        ///    
        /// </summary>
        static object locker = new object();
        /// <summary>
        /// Zookeeper    
        /// </summary>
        string[] address;
        /// <summary>
        /// Zookeeper     
        /// </summary>
        ZookeeperHelper zookeeperHelper;

        /// <summary>
        ///     
        /// </summary>
        /// <param name="lockerPath">        </param>
        /// <param name="address">    </param>
        public ZookeeperLocker(string lockerPath, params string[] address) : this(lockerPath, 0, address)
        {
        }
        /// <summary>
        ///     
        /// </summary>
        /// <param name="lockerPath">        </param>
        /// <param name="sessionTimeout">      </param>
        /// <param name="address">    </param>
        public ZookeeperLocker(string lockerPath, int sessionTimeout, params string[] address)
        {
            this.address = address.ToArray();

            zookeeperHelper = new ZookeeperHelper(address, lockerPath);
            if (sessionTimeout > 0)
            {
                zookeeperHelper.SessionTimeout = sessionTimeout;
            }
            if (!zookeeperHelper.Connect())
            {
                throw new Exception("connect failed:" + string.Join(",", address));
            }
            lock (locker)
            {
                if (!zookeeperHelper.Exists())//         
                {
                    zookeeperHelper.SetData("", "", true);
                }
            }
        }
        /// <summary>
        ///      
        /// </summary>
        /// <returns>    </returns>
        public string CreateLock()
        {
            var path = Guid.NewGuid().ToString().Replace("-", "");
            while (zookeeperHelper.Exists(path))
            {
                path = Guid.NewGuid().ToString().Replace("-", "");
            }
            return CreateLock(path);
        }
        /// <summary>
        ///             
        /// </summary>
        /// <param name="path">  ,         (/)</param>
        /// <returns>    </returns>
        public string CreateLock(string path)
        {
            if (path.Contains("/"))
            {
                throw new ArgumentException("invalid path");
            }
            return zookeeperHelper.SetData(path, "", false, true);
        }
        /// <summary>
        ///    
        /// </summary>
        /// <param name="path">  </param>
        /// <returns>       true,      </returns>
        public bool Lock(string path)
        {
            return LockAsync(path).GetAwaiter().GetResult();
        }
        /// <summary>
        ///    
        /// </summary>
        /// <param name="path">  </param>
        /// <param name="millisecondsTimeout">    ,  :  </param>
        /// <returns>       true,           false</returns>
        public bool Lock(string path, int millisecondsTimeout)
        {
            return LockAsync(path, millisecondsTimeout).GetAwaiter().GetResult();
        }
        /// <summary>
        ///        
        /// </summary>
        /// <param name="path">  </param>
        /// <returns>       true,      </returns>
        public async Task<bool> LockAsync(string path)
        {
            return await LockAsync(path, System.Threading.Timeout.Infinite);
        }
        /// <summary>
        ///        
        /// </summary>
        /// <param name="path">  </param>
        /// <param name="millisecondsTimeout">    ,  :  </param>
        /// <returns>       true,           false</returns>
        public async Task<bool> LockAsync(string path, int millisecondsTimeout)
        {
            var array = await zookeeperHelper.GetChildrenAsync("", true);
            if (array != null && array.Length > 0)
            {
                var first = array.FirstOrDefault();
                if (first == path)//         ,    
                {
                    return true;
                }

                var index = array.ToList().IndexOf(path);
                if (index > 0)
                {
                    //      
                    var are = new AutoResetEvent(false);
                    var watcher = new NodeWatcher();
                    watcher.NodeDeleted += (ze) =>
                    {
                        are.Set();
                    };
                    if (await zookeeperHelper.WatchAsync(array[index - 1], watcher))//             
                    {
                        if (!are.WaitOne(millisecondsTimeout))
                        {
                            return false;
                        }
                    }

                    are.Dispose();
                }
                else
                {
                    throw new InvalidOperationException($"no locker found in path:{zookeeperHelper.CurrentPath}");
                }
            }
            return true;
        }
        /// <summary>
        ///     
        /// </summary>
        public void Dispose()
        {
            zookeeperHelper.Dispose();
        }
    }
}
지금 프로그램 을 써 서 시 뮬 레이 션 을 할 수 있 습 니 다.

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace AspNetCore.ZookeeperConsole
{
    class Program
    {
        static void Main(string[] args)
        {
            //Zookeeper     ,  host:port  ,          (,)  
            string[] address = new string[] { "192.168.209.133:2181", "192.168.209.133:2181", "192.168.209.133:2181" };
            //      ,    
            int sessionTimeOut = 10000;
            //      
            string lockerPath = "/Locker";

            for (var i = 0; i < 10; i++)
            {
                string client = "client" + i;
                //       
                new Thread(() =>
                {
                    using (ZookeeperLocker zookeeperLocker = new ZookeeperLocker(lockerPath, sessionTimeOut, address))
                    {
                        string path = zookeeperLocker.CreateLock();
                        if (zookeeperLocker.Lock(path))
                        {
                            //      
                            Console.WriteLine($"【{client}】   :{DateTime.Now}");
                            Thread.Sleep(3000);
                            Console.WriteLine($"【{client}】    :{DateTime.Now}");
                        }
                        else
                        {
                            Console.WriteLine($"【{client}】     :{DateTime.Now}");
                        }
                    }
                }).Start();
            }
                        
            Console.ReadKey();
        }
    }
}
실행 결 과 는 다음 과 같 습 니 다.
   
 자물쇠 기능 이 실현 되 었 음 을 발견 할 수 있다.
프로그램 실행 중 로그 인쇄:클 라 이언 트 session timed out,have not heard from server in 8853 ms for sessionid 0x1000000ec5500b 2
또는 직접 이상 던 지기:org.apache.zookeeper.Keeper Exception.Connect LossException:"ExceptionWasThrown”
세 션 타임 아웃 시간 을 적 절 히 조정 하면 됩 니 다.
이상 은 C\#Zookeeper 분포 식 자 물 쇠 를 실현 하 는 참고 예제 의 상세 한 내용 입 니 다.C\#Zookeeper 분포 식 자 물 쇠 를 실현 하 는 데 관 한 자 료 는 저희 의 다른 관련 글 을 주목 하 세 요!

좋은 웹페이지 즐겨찾기