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 분포 식 자 물 쇠 를 실현 하 는 데 관 한 자 료 는 저희 의 다른 관련 글 을 주목 하 세 요!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
WebView2를 Visual Studio 2017 Express에서 사용할 수 있을 때까지Evergreen .Net Framework SDK 4.8 VisualStudio2017에서 NuGet을 사용하기 때문에 패키지 관리 방법을 packages.config 대신 PackageReference를 사용해야...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.