Memcache-Java-Client-Release 원본 읽기(3)

1. 주요 내용 본 장절의 주요 내용은Memcache의 가장 기본적인 set/get 조작 과정을 소개하는 것이다.
2. 준비 작업 1, 서버 시작 192.168.0.106:11211192.168.0.106:11212 두 서버 실례.2. 예제 코드:
MemCachedClient mcc = new MemCachedClient();
boolean success = mcc.set("test1", "Hello!");
System.out.println(success);

String val = mcc.get("test1");
System.out.println(val);

3. 원본 읽기 1. 클라이언트 초기화 과정은 클라이언트를 초기화하는 것이 비교적 간단하다. 기본값은 TCP 프로토콜을 지원하는 AscIIClient 실례화 대상을 만들고 poolName이 default인 연결 탱크의 실례 대상을 가져오는 것이다.편폭을 좀 절약하면 이 부분의 원본 코드는 전시하지 않는다.
2. set 작업은 기본적으로 AscIIClient의 실례 대상을 만들었기 때문에 set 작업의 실현은 자연히 AscIIClient 클래스에 떨어진다. 우리는 몇 가지 관건적인 코드 단편을 본다. 1) 키 값 처리는 빈 검사와 유니버설 인코딩으로 전환되는 것을 포함하고 코드에서 좋은 이름을 지어서 키 값 정화라고 한다.
private boolean set(String cmdname, String key, Object value, Date expiry, Integer hashCode, Long casUnique,
        boolean asString) {

    if (cmdname == null || key == null) {
        log.error("key is null or cmd is null/empty for set()");
        return false;
    }

    try {
        key = sanitizeKey(key);
    } catch (UnsupportedEncodingException e) {

        // if we have an errorHandler, use its hook
        if (errorHandler != null)
            errorHandler.handleErrorOnSet(this, e, key);

        log.error("failed to sanitize your key!", e);
        return false;
    }

//   ...
}

private String sanitizeKey(String key) throws UnsupportedEncodingException {
    return (sanitizeKeys) ? URLEncoder.encode(key, "UTF-8") : key;
}

2) 키 값에 따라 연결 탱크에서 SchoonerSockIO 대상을 가져옵니다. 이것은 Memcache Client 작업의 핵심입니다.
/** * Returns appropriate SockIO object given string cache key and optional * hashcode. * * Trys to get SockIO from pool. Fails over to additional pools in event of * server failure. * * @param key * hashcode for cache key * @param hashCode * if not null, then the int hashcode to use * @return SockIO obj connected to server */
public final SchoonerSockIO getSock(String key, Integer hashCode) {

    if (!this.initialized) {
        log.error("attempting to get SockIO from uninitialized pool!");
        return null;
    }

    // if no servers return null
    int size = 0;
    if ((this.hashingAlg == CONSISTENT_HASH && consistentBuckets.size() == 0)
            || (buckets != null && (size = buckets.size()) == 0))
        return null;
    else if (size == 1) {
        SchoonerSockIO sock = (this.hashingAlg == CONSISTENT_HASH) ? getConnection(consistentBuckets
                .get(consistentBuckets.firstKey())) : getConnection(buckets.get(0));

        return sock;
    }

    // from here on, we are working w/ multiple servers
    // keep trying different servers until we find one
    // making sure we only try each server one time
    Set<String> tryServers = new HashSet<String>(Arrays.asList(servers));
    // get initial bucket
    long bucket = getBucket(key, hashCode);
    String server = (this.hashingAlg == CONSISTENT_HASH) ? consistentBuckets.get(bucket) : buckets
            .get((int) bucket);
    while (!tryServers.isEmpty()) {
        // try to get socket from bucket
        SchoonerSockIO sock = getConnection(server);
        if (sock != null)
            return sock;

        // if we do not want to failover, then bail here
        if (!failover)
            return null;
        // log that we tried
        tryServers.remove(server);
        if (tryServers.isEmpty())
            break;
        // if we failed to get a socket from this server
        // then we try again by adding an incrementer to the
        // current key and then rehashing
        int rehashTries = 0;
        while (!tryServers.contains(server)) {
            String newKey = new StringBuffer().append(rehashTries).append(key).toString();
            // String.format( "%s%s", rehashTries, key );
            bucket = getBucket(newKey, null);
            server = (this.hashingAlg == CONSISTENT_HASH) ? consistentBuckets.get(bucket) : buckets
                    .get((int) bucket);
            rehashTries++;
        }
    }
    return null;
}

이 코드는 일치성 Hash 알고리즘과 다른 세 개의 Hash 알고리즘 처리 논리의 격리를 뚜렷하게 볼 수 있다. 여기서 우리는 먼저 다른 세 가지 Hash 알고리즘의 처리 상황을 토론한다.앞에서 소개한 바와 같이 버킷 집합에는memcache 서버 연결 정보가 저장되어 있고, socketPool 집합에 저장된 것은 연결 정보와Generic ObjectPool 연결 탱크의 대상이다.getConnection () 방법의 기본적인 사고방식은 이 두 집합에서 Generic ObjectPool 연결 탱크의 대상을 얻고 sockets를 호출하는 것이다.borrowObject();SchoonerSockIO 인스턴스를 가져옵니다.만약에 하나의 서버 실례만 설정했다면 getConnection () 방법을 직접 호출합니다. 만약에 여러 개의 서버 실례를 설정했다면 이것은 어떤 실례를 선택하여 조작했는지와 관련이 있습니다. memcache에서 서버 실례를 선택한 것은 키 값의hashcode 값에 따라 결정됩니다. getBucket (key,hashCode) 방법을 보십시오.
private final long getBucket(String key, Integer hashCode) {
    long hc = getHash(key, hashCode);

    if (this.hashingAlg == CONSISTENT_HASH) {
        return findPointFor(hc);
    } else {
        long bucket = hc % buckets.size();
        if (bucket < 0)
            bucket *= -1;
        return bucket;
    }
}


/** * Returns a bucket to check for a given key. * * @param key * String key cache is stored under * @return int bucket */
private final long getHash(String key, Integer hashCode) {

    if (hashCode != null) {
        if (hashingAlg == CONSISTENT_HASH)
            return hashCode.longValue() & 0xffffffffL;
        else
            return hashCode.longValue();
    } else {
        switch (hashingAlg) {
        case NATIVE_HASH:
            return (long) key.hashCode();
        case OLD_COMPAT_HASH:
            return origCompatHashingAlg(key);
        case NEW_COMPAT_HASH:
            return newCompatHashingAlg(key);
        case CONSISTENT_HASH:
            return md5HashingAlg(key);
        default:
            // use the native hash as a default
            hashingAlg = NATIVE_HASH;
            return (long) key.hashCode();
        }
    }
}

우선 getHash 방법을 호출하여 키의hash 값을 계산합니다. 그 안에 사용된hash 알고리즘은 모두 안에 있습니다. (기본적으로 Native_Hash 알고리즘을 사용합니다.) 만약hashCode가 이미 값을 지정했다면 키는 Hash 연산에 참여하지 않습니다. 이것은 특정한 대상을 편리하게 하기 위해 고정된 Hash 값을 사용합니다.키의 Hash 값을 얻은 후에 비일치성 Hash 알고리즘의 처리 논리는 Hash 값을 직접 버킷의 크기에 대해 구모 연산을 하고 사용하는 버킷 인덱스를 얻어 연결 정보 대상을 얻는 것이다. 마지막으로 이 연결 정보에 따라 SocketPool에서Generic ObjectPool 대상을 끌어당겨서 Schooner SockIO의 실례화를 완성한다.
3)value의 유형을 판단하고 서열화된 조작을 하는 NativeHandle 클래스는 주로 자바 기본 데이터 클래스의 변환과 디코딩에 사용되며,memcache 클라이언트가 기초 데이터 형식을 지원하는 보증이다. 예를 들어boolean/Boolean 유형의true를 set 조작 시 1,false를 0으로 변환하고 get 조작 시 다시 변환한다.모두 14가지가 지원되고 각각 하나의 표지값이 있으며 다른 대상(예를 들어 JavaBean 대상)은 모두byte수조로 바뀌며 지원되는 14가지 유형의 인코딩은 다음과 같다.
/** * values for cache flags */
public static final int MARKER_BYTE = 1;
public static final int MARKER_BOOLEAN = 8192;
public static final int MARKER_INTEGER = 4;
public static final int MARKER_LONG = 16384;
public static final int MARKER_CHARACTER = 16;
public static final int MARKER_STRING = 32;
public static final int MARKER_STRINGBUFFER = 64;
public static final int MARKER_FLOAT = 128;
public static final int MARKER_SHORT = 256;
public static final int MARKER_DOUBLE = 512;
public static final int MARKER_DATE = 1024;
public static final int MARKER_STRINGBUILDER = 2048;
public static final int MARKER_BYTEARR = 4096;
public static final int MARKER_OTHERS = 0x00;

4) 연결 조작 명령은 사실 텔넷 명령을 연결하는 것이다. 예를 들어 set1 320
5) 네트워크 전송 부분의 나머지 내용은 네트워크 프로그래밍 관련 부분에 속하기 때문에 대상의 서열화 조작을 유의할 수 있다. 여기서nio를 사용하여 클라이언트의 통신 효율을 향상시켰다.마지막으로 명령의 반환 값을 가져옵니다. STORED이면 작업이 성공했고 set 작업을 끝냅니다.
여기가 바로 set 조작의 간단한 설명입니다. 이 코드에memcache 클라이언트의 다른 특성의 실현, 예를 들어 실효 이동, 자동 복구 등이 삽입되어 있기 때문에 여기서 우리는 가장 기본적인 실현 사고방식을 주목하면 됩니다.
3. get 조작은 사실 get/set을 한 쌍의 조작으로 한다. 연결 정보의 위치, 연결 탱크의 획득, 키의hash값 계산 등 이런 부분은 모두 유사하다. 이렇게 해야만 set의 캐시 대상이 get 조작에 의해 정확하게 얻을 수 있고 마지막 네트워크 통신 부분과 달리 데이터를 얻었을 때byte[]수조를 자바 대상으로 반서열화할 수 있다.반환값 예시, 올바른 응답: VALUE 1 32 6 Hello!실패 시 응답 가져오기: END 올바른 응답의 특징은 공백 3개에 "\r"이므로 관련 Response 확인 코드는 다음과 같습니다.
while (!stop) {
    /* * Critical block to parse the response header. */
    b = input.read();
    if (b == ' ' || b == '\r') {
        switch (index) {
        case 0:
            if (END.startsWith(sb.toString()))
                return null;
        case 1:
            break;
        case 2:
            flag = Integer.parseInt(sb.toString());
            break;
        case 3:
            // get the data size
            dataSize = Integer.parseInt(sb.toString());
            break;
        }
        index++;
        sb = new StringBuffer();
        if (b == '\r') {
            input.read();
            stop = true;
        }
        continue;
    }
    sb.append((char) b);
}

4. FAQQ1: 가장 간단한 몇 마디로 set 등 기본 조작을 묘사한다면 어떻게 묘사해야 합니까?A1: 세 마디면 됩니다. 첫째, 키의 Hash 값에 따라 이번에 조작할 서버 실례를 찾습니다.둘째: 관련telnet 조작 명령 연결;셋째: 네트워크 통신.
Q2: 다른 조작은 set와 유사합니까?A2: 네, 가장 관건적인 문제는 똑같습니다. 단지 연결된 명령과 처리 명령의 응답이 약간 다를 뿐입니다.
Q3:set 작업은 키에 고정된hashcode 값을 지정할 수 있습니다. 어떤 효과가 있습니까?A3:getHash () 방법에서 이hashcode의 값을 직접 되돌려주면 getBuckets의 결과에 영향을 줄 수 있습니다. 목적은 고정된 서버 노드를 지정할 수 있기 때문입니다. 매번hash 값이 같기 때문에 매핑 알고리즘이 나온 서버 노드는 모두 같고delete 작업도 마찬가지입니다.

좋은 웹페이지 즐겨찾기