All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.rubyeye.xmemcached.impl.KetamaMemcachedSessionLocator Maven / Gradle / Ivy

There is a newer version: 2.4.8
Show newest version
/**
 *Copyright [2009-2010] [dennis zhuang([email protected])]
 *Licensed under the Apache License, Version 2.0 (the "License");
 *you may not use this file except in compliance with the License.
 *You may obtain a copy of the License at
 *             http://www.apache.org/licenses/LICENSE-2.0
 *Unless required by applicable law or agreed to in writing,
 *software distributed under the License is distributed on an "AS IS" BASIS,
 *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 *either express or implied. See the License for the specific language governing permissions and limitations under the License
 */
package net.rubyeye.xmemcached.impl;

import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Random;
import java.util.SortedMap;
import java.util.TreeMap;

import net.rubyeye.xmemcached.HashAlgorithm;
import net.rubyeye.xmemcached.networking.MemcachedSession;

import com.google.code.yanf4j.core.Session;

/**
 * ConnectionFactory instance that sets up a ketama compatible connection.
 *
 * 

* This implementation piggy-backs on the functionality of the * DefaultConnectionFactory in terms of connections and queue * handling. Where it differs is that it uses both the * KetamaNodeLocator * and the HashAlgorithm.KETAMA_HASH to provide consistent node * hashing. * * @see http://www.last.fm/user/RJ/journal/2007/04/10/392555/ * *

*/ /** * Consistent Hash Algorithm implementation,based on TreeMap.tailMap(hash) * method. * * @author dennis * */ public class KetamaMemcachedSessionLocator extends AbstractMemcachedSessionLocator { static final int NUM_REPS = 160; private transient volatile TreeMap> ketamaSessions = new TreeMap>(); private final HashAlgorithm hashAlg; private volatile int maxTries; private final Random random = new Random(); /** * compatible with nginx-upstream-consistent,patched by wolfg1969 */ static final int DEFAULT_PORT = 11211; private final boolean cwNginxUpstreamConsistent; public KetamaMemcachedSessionLocator() { this.hashAlg = HashAlgorithm.KETAMA_HASH; this.cwNginxUpstreamConsistent = false; } public KetamaMemcachedSessionLocator(boolean cwNginxUpstreamConsistent) { this.hashAlg = HashAlgorithm.KETAMA_HASH; this.cwNginxUpstreamConsistent = cwNginxUpstreamConsistent; } public KetamaMemcachedSessionLocator(HashAlgorithm alg) { this.hashAlg = alg; this.cwNginxUpstreamConsistent = false; } public KetamaMemcachedSessionLocator(HashAlgorithm alg, boolean cwNginxUpstreamConsistent) { this.hashAlg = alg; this.cwNginxUpstreamConsistent = cwNginxUpstreamConsistent; } public KetamaMemcachedSessionLocator(List list, HashAlgorithm alg) { super(); this.hashAlg = alg; this.cwNginxUpstreamConsistent = false; this.buildMap(list, alg); } private final void buildMap(Collection list, HashAlgorithm alg) { TreeMap> sessionMap = new TreeMap>(); String sockStr; for (Session session : list) { if (this.cwNginxUpstreamConsistent) { InetSocketAddress serverAddress = session .getRemoteSocketAddress(); sockStr = serverAddress.getAddress().getHostAddress(); if (serverAddress.getPort() != DEFAULT_PORT) { sockStr = sockStr + ":" + serverAddress.getPort(); } } else { sockStr = String.valueOf(session.getRemoteSocketAddress()); } /** * Duplicate 160 X weight references */ int numReps = NUM_REPS; if (session instanceof MemcachedTCPSession) { numReps *= ((MemcachedSession) session).getWeight(); } if (alg == HashAlgorithm.KETAMA_HASH) { for (int i = 0; i < numReps / 4; i++) { byte[] digest = HashAlgorithm.computeMd5(sockStr + "-" + i); for (int h = 0; h < 4; h++) { long k = (long) (digest[3 + h * 4] & 0xFF) << 24 | (long) (digest[2 + h * 4] & 0xFF) << 16 | (long) (digest[1 + h * 4] & 0xFF) << 8 | digest[h * 4] & 0xFF; this.getSessionList(sessionMap, k).add(session); } } } else { for (int i = 0; i < numReps; i++) { long key = alg.hash(sockStr + "-" + i); this.getSessionList(sessionMap, key).add(session); } } } this.ketamaSessions = sessionMap; this.maxTries = list.size(); } private List getSessionList( TreeMap> sessionMap, long k) { List sessionList = sessionMap.get(k); if (sessionList == null) { sessionList = new ArrayList(); sessionMap.put(k, sessionList); } return sessionList; } public final Session getSessionByKey(final String key) { if (this.ketamaSessions == null || this.ketamaSessions.size() == 0) { return null; } long hash = this.hashAlg.hash(key); Session rv = this.getSessionByHash(hash); int tries = 0; while (!this.failureMode && (rv == null || rv.isClosed()) && tries++ < this.maxTries) { hash = this.nextHash(hash, key, tries); rv = this.getSessionByHash(hash); } return rv; } public final Session getSessionByHash(final long hash) { TreeMap> sessionMap = this.ketamaSessions; if (sessionMap.size() == 0) { return null; } Long resultHash = hash; if (!sessionMap.containsKey(hash)) { // Java 1.6 adds a ceilingKey method, but xmemcached is compatible // with jdk5,So use tailMap method to do this. SortedMap> tailMap = sessionMap.tailMap(hash); if (tailMap.isEmpty()) { resultHash = sessionMap.firstKey(); } else { resultHash = tailMap.firstKey(); } } // // if (!sessionMap.containsKey(resultHash)) { // resultHash = sessionMap.ceilingKey(resultHash); // if (resultHash == null && sessionMap.size() > 0) { // resultHash = sessionMap.firstKey(); // } // } List sessionList = sessionMap.get(resultHash); if (sessionList == null || sessionList.size() == 0) { return null; } int size = sessionList.size(); return sessionList.get(this.random.nextInt(size)); } public final long nextHash(long hashVal, String key, int tries) { long tmpKey = this.hashAlg.hash(tries + key); hashVal += (int) (tmpKey ^ tmpKey >>> 32); hashVal &= 0xffffffffL; /* truncate to 32-bits */ return hashVal; } public final void updateSessions(final Collection list) { this.buildMap(list, this.hashAlg); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy