zookeeper 분포 식 자물쇠 의 실현


 본 고 는 주로 ZooKeeper 를 사용 하여 분포 식 자 물 쇠 를 실현 하 는 과정 에서 '양 떼 효과 (herdeffect)' 의 등장 을 어떻게 효과적으로 피 하 는 지 설명 한다.
마지막 으로 실현 코드 가 있 으 니 참고 하 시기 바 랍 니 다.
본 고 는 과 다음 과 같은 홈 페이지 내용 을 참고 하여 실현 했다.
http://aliapp.blog.51cto.com/8192229/1328018
 
실현 과정 은 다음 과 같다.
일반적인 분포 식 잠 금 실현
일반적인 분포 식 자물쇠 가 어떻게 실현 되 는 지 간단하게 말씀 드 리 겠 습 니 다.구체 적 인 코드 실현 은 여기 서 볼 수 있다. https://svn.apache.org/repos/asf/zookeeper/trunk/src/recipes/lock/
앞에서 언급 한 바 와 같이 zookeeper 에서 노드 의 생 성 유형 은 4 가지 가 있 는데 여기 서 우 리 는 임시 순서 노드 에 중심 을 두 었 다.이런 유형의 노드 는 몇 가지 특성 이 있다.
노드 의 생명주기 와 클 라 이언 트 세 션 이 연결 되 어 있 습 니 다. 즉, 노드 를 만 드 는 클 라 이언 트 세 션 이 효력 을 잃 으 면 이 노드 도 삭 제 됩 니 다.
모든 부모 노드 는 하위 노드 가 만 든 선후 순 서 를 유지 하고 순서 노드 (SEQUENTIAL) 를 만 들 면 부모 노드 는 자동 으로 이 노드 에 성형 수 치 를 분배 하고 접미사 형식 으로 노드 이름 에 자동 으로 추가 하여 이 노드 의 최종 노드 이름 으로 한다.
위의 두 가지 특성 을 이용 하여 분포 식 잠 금 을 실현 하 는 기본 논 리 를 살 펴 보 자.
클 라 이언 트 가 create () 방법 을 호출 하여 " locknode / guid - lock -" 라 는 노드 를 만 듭 니 다. 주의해 야 할 것 은 이 노드 의 생 성 유형 을 EPHEMERAL 로 설정 해 야 합 니 다.SEQUENTIAL。
클 라 이언 트 는 getChildren (" locknode") 방법 을 호출 하여 만 든 모든 하위 노드 를 가 져 오고 이 노드 에 하위 노드 변경 알림 을 등록 하 는 Watcher 입 니 다.
클 라 이언 트 가 모든 하위 노드 path 를 가 져 온 후에 자신 이 단계 1 에서 만 든 노드 가 모든 노드 에서 번호 가 가장 작은 것 을 발견 하면 이 클 라 이언 트 가 자 물 쇠 를 얻 었 다 고 생각 합 니 다.
3 단계 에서 자신 이 모든 하위 노드 에서 가장 작은 것 이 아니 라 는 것 을 발견 하면 자신 이 자 물 쇠 를 얻 지 못 했다 는 것 을 설명 하고 다음 하위 노드 가 알림 을 변경 할 때 까지 기 다 렸 다가 하위 노드 를 가 져 와 자 물 쇠 를 가 져 올 지 여 부 를 판단 한다.
자 물 쇠 를 풀 어 주 는 과정 은 상대 적 으로 간단 하 다. 바로 자신 이 만 든 하위 노드 를 삭제 하면 된다.
문제 의 소재
위의 이 분포 식 자물쇠 의 실현 에서 대체적으로 일반적인 분포 식 클 러 스 터 경쟁 자물쇠 의 수 요 를 만족 시 킬 수 있다.여기 서 말 하 는 일반적인 장면 은 군집 규모 가 크 지 않 고 보통 10 대의 기계 안에 있다 는 것 을 말한다.
그러나 위의 실현 논 리 를 곰 곰 이 생각해 보면 우 리 는 쉽게 문 제 를 발견 할 수 있다. 절차 4. '즉, 모든 하위 점 을 얻 고 자신 이 만 든 노드 가 번호 가 가장 작은 노드 인지 판단 하 는 것' 이다. 이 과정 은 전체 분포 식 자물쇠 의 경쟁 과정 에서 대량의 재 운행 을 하고 대부분 운행 결 과 는 자신 이 번호 가 가장 작은 노드 가 아니 라 는 것 을 판단 한다.다음 통 지 를 계속 기다 리 는 것 은 분명 과학적 으로 보이 지 않 는 다.클 라 이언 트 는 자신 과 관련 이 없 는 사건 통 지 를 이유 없 이 많이 받 습 니 다. 이것 은 클 라 이언 트 규모 가 클 때 서버 에 큰 성능 영향 을 줄 수 있 습 니 다. 그리고 같은 시간 에 여러 노드 의 클 라 이언 트 가 연결 을 끊 으 면 서버 는 다른 클 라 이언 트 처럼 대량의 사건 통 지 를 보 냅 니 다. 이것 이 바로 양 떼 효과 입 니 다.이 문제 의 근원 은 클 라 이언 트 의 진정한 관심 사 를 제대로 찾 지 못 한 데 있다.
우 리 는 위의 분포 식 자물쇠 경쟁 과정 을 다시 한 번 살 펴 보 자. 그의 핵심 논 리 는 자신 이 모든 노드 에서 번호 가 가장 작은 지 판단 하 는 것 이다.그래서 쉽게 연상 할 수 있 는 것 은 모든 노드 의 창조 자 는 자신의 번호 보다 작은 노드 에 만 관심 을 가 져 야 한 다 는 것 이다.
개 선 된 분포 식 잠 금 실현
다음은 개 선 된 분포 식 자물쇠 실현 입 니 다. 이전의 실현 방식 과 유일 하 게 다른 점 은 모든 자물쇠 경쟁자 로 설계 되 었 습 니 다. 관심 만 가 져 야 합 니 다. "locknode_”노드 아래 번호 가 자신 보다 작은 노드 가 존재 하 는 지 확인 하면 됩 니 다.다음 과 같이 구현:
클 라 이언 트 가 create () 방법 을 호출 하여 " locknode / guid - lock -" 라 는 노드 를 만 듭 니 다. 주의해 야 할 것 은 이 노드 의 생 성 유형 을 EPHEMERAL 로 설정 해 야 합 니 다.SEQUENTIAL。
클 라 이언 트 는 getChildren (" locknode") 방법 으로 만 든 모든 하위 노드 를 가 져 옵 니 다. 여 기 는 Watcher 를 등록 하지 않 습 니 다.
클 라 이언 트 가 모든 하위 노드 path 를 가 져 온 후에 자신 이 단계 1 에서 만 든 노드 번호 가 가장 작은 것 을 발견 하면 이 클 라 이언 트 가 자 물 쇠 를 얻 었 다 고 생각 합 니 다.
3 단계 에서 자신 이 모든 하위 노드 에서 가장 작은 것 이 아니 라 는 것 을 발견 하면 자신 이 아직 자 물 쇠 를 얻 지 못 했다 는 것 을 의미한다.이 때 클 라 이언 트 는 자신 보다 작은 노드 를 찾 은 다음 에 exist () 방법 을 호출 하고 이벤트 감청 을 등록 해 야 합 니 다.
그 후에 이 주 목 받 는 노드 가 제거 되면 클 라 이언 트 는 해당 하 는 통 지 를 받 을 것 이다.이 럴 때 클 라 이언 트 는 getChildren (" locknode") 방법 을 다시 호출 하여 만 든 모든 하위 노드 를 가 져 와 서 자신 이 가장 작은 노드 임 을 확인 한 다음 에 3 단계 에 들 어가 야 합 니 다.
 
코드 구현:
package cn.liu.zookeeper.ch14;


import java.io.IOException;
import java.util.concurrent.CountDownLatch;

import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.Watcher.Event.KeeperState;


public class ConnectionWatcher implements Watcher {

	private static final int SESSION_TIMEOUT = 5000;

	protected ZooKeeper zk;
	private CountDownLatch connectedSignal = new CountDownLatch(1);

	public void connect(String hosts) throws IOException, InterruptedException {
		zk = new ZooKeeper(hosts, SESSION_TIMEOUT, this);
		connectedSignal.await();
	}

	@Override
	public void process(WatchedEvent event) {
		if (event.getState() == KeeperState.SyncConnected) {
			connectedSignal.countDown();
		}
	}

	public void close() throws InterruptedException {
		zk.close();
	}
}

 
package cn.liu.zookeeper.project;



import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.data.Stat;

import cn.liu.zookeeper.ch14.ConnectionWatcher;


/**
 * 
 *               
 * 
 *     zookeeper     ;        ,          
 *        ,               ,            
 * 
 * 
 *                (Herd Effect),       
 * 
 * 
 * 
 * 
 * @author Liu Dengtao
 *
 * 2014-2-28
 */
public class DistributedLock extends ConnectionWatcher {

	public String join(String groupPath)
			throws KeeperException, InterruptedException {
	
		String path = groupPath + "/lock-" + zk.getSessionId() + "-";
		
		//          
		String createdPath = zk.create(path, null/* data */,
				Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
		System.out.println("Created " + createdPath);
		
		return createdPath;
	}

	
	/**
	 *                
	 * @param groupPath
	 * @param myName
	 * @return
	 * @throws KeeperException
	 * @throws InterruptedException
	 */
	public boolean checkState(String groupPath,String myName) throws KeeperException, InterruptedException{
		
		List<String> childList =  zk.getChildren(groupPath, false);
		
		String[] myStr = myName.split("-");
		long myId = Long.parseLong(myStr[2]);
		
		boolean minId = true;
		for (String childName : childList) {
			String[] str = childName.split("-");
			long id = Long.parseLong(str[2]);
			if (id < myId) {
				minId = false;
				break;
			}
		}
		
		if (minId) {
			System.out.println(new Date() + "       ,  ! myId:" + myId);
			
			return true;
		}else {
			System.out.println(new Date() + "     ,  myId:" + myId);
			
			return false;
		}		
	}
	
	/**
	 *              ,             (      )
	 * @param groupPath
	 * @param myName
	 * @throws KeeperException
	 * @throws InterruptedException
	 */
	public void listenNode(final String groupPath, final String myName) throws KeeperException, InterruptedException{
		
		List<String> childList =  zk.getChildren(groupPath, false);
		
		String[] myStr = myName.split("-");
		long myId = Long.parseLong(myStr[2]);
		
		List<Long> idList = new ArrayList<Long>();
		Map<Long, String> sessionMap = new HashMap<Long, String>();
		
		for (String childName : childList) {
			String[] str = childName.split("-");
			long id = Long.parseLong(str[2]);
			idList.add(id);
			sessionMap.put(id, str[1]+"-"+str[2]);
		}
		
		Collections.sort(idList);
		
		int i = idList.indexOf(myId);
		if (i <=0) {
			throw new IllegalArgumentException("    !");
		}
		
		//         
		long headId = idList.get(i-1);
		
		String headPath = groupPath + "/lock-" + sessionMap.get(headId);
		System.out.println("    :" + headPath);
		
		Stat stat = zk.exists(headPath, new Watcher(){

			@Override
			public void process(WatchedEvent event) {
				System.out.println("     " + event.getType() + "  !");
				
				try {
					while(true){
						if (checkState(groupPath,myName)) {
							Thread.sleep(3000);
							System.out.println(new Date() + "     !");
							System.exit(0);
						}
						
						Thread.sleep(3000);

					}
				} catch (KeeperException e) {
					e.printStackTrace();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				
			}
			
		});

		System.out.println(stat);
		
	}
	
	
	public static void main(String[] args) throws Exception {
		DistributedLock joinGroup = new DistributedLock();
		joinGroup.connect("localhost:" + "2181");
	
		//zookeeper    ;      ,      
		String groupName = "zkRoot";
		String memberName = "_locknode_";
		String path = "/" + groupName + "/" + memberName;
		
		String myName = joinGroup.join(path);
		if (!joinGroup.checkState(path, myName)) {
			joinGroup.listenNode(path, myName);
		}
		
		Thread.sleep(Integer.MAX_VALUE);
		
		joinGroup.close();

		

	}
}
// ^^ JoinGroup

 
 
 

좋은 웹페이지 즐겨찾기