Memcached集羣,客戶端自動hash到不同服務器的實現

最近項目需要,要求做一個遠程服務中心,爲各客戶端提供遠程存取接口,壓力基本上是每個客戶端平均每秒300次請求,估計大約有10個客戶端。 我考慮實現的方案是: 客戶端寫數據時直接寫到本地ehcache緩存,然後同時發送數據到memcached緩存,最後通過遠程服務接口定時同步數據到後臺數據庫。 客戶端讀數據首先從本地ehcache查找,然後再從memcached獲取,最後通過遠程數據接口查找數據 那麼,此方案的一些需要考慮的問題就是: 1.memcached是否可以支撐這麼大的壓力 2.memcahced繁忙或down掉是否會影響client端線程阻塞或堆積到下一秒,造成壓力 之前也通過測過Java client的效率,開10個線程,基本可以達到 2000~3000每秒。 首先分析一下Java client 啓動時的部分代碼 Memcached 支持直接設置多個servers屬性 來實現多個memcahced均衡,對應還有一個屬性是weights,字面意思就是權重,分析了一下代碼,和我想的是一樣的 啓動memcached的代碼通常是這樣的
Java代碼
SockIOPool pool = SockIOPool.getInstance(poolname);   
pool.setServers(servers);   
pool.setWeights(weights);   
pool.setInitConn(initConn);   
pool.setMinConn(minConn);   
pool.setMaxConn(maxConn);   
pool.setMaxIdle(maxIdle);   
pool.setMaxBusyTime(maxBusyTime);   
pool.setMaintSleep(maintSleep);   
pool.setSocketTO(socketTO);   
pool.setSocketConnectTO(socketConnectTO);   
pool.setNagle(nagle);   
pool.setHashingAlg(SockIOPool.NEW_COMPAT_HASH);   
pool.initialize();   
MemCachedClient client = new MemCachedClient(poolname);  
SockIOPool pool = SockIOPool.getInstance(poolname);
pool.setServers(servers);
pool.setWeights(weights);
pool.setInitConn(initConn);
pool.setMinConn(minConn);
pool.setMaxConn(maxConn);
pool.setMaxIdle(maxIdle);
pool.setMaxBusyTime(maxBusyTime);
pool.setMaintSleep(maintSleep);
pool.setSocketTO(socketTO);
pool.setSocketConnectTO(socketConnectTO);
pool.setNagle(nagle);
pool.setHashingAlg(SockIOPool.NEW_COMPAT_HASH);
pool.initialize();
MemCachedClient client = new MemCachedClient(poolname);servers 和 weights 都是一個數組,就是說可以同時設置多個server 然後看一下 pool.initialize() 做了什麼
Java代碼
availPool   = new HashMap<String,Map<SockIO,Long>>( servers.length * initConn );   
busyPool    = new HashMap<String,Map<SockIO,Long>>( servers.length * initConn );   
deadPool    = new IdentityHashMap<SockIO,Integer>();   
hostDeadDur = new HashMap<String,Long>();   
hostDead    = new HashMap<String,Date>();   
maxCreate   = (poolMultiplier > minConn) ? minConn : minConn / poolMultiplier;   // only create up to maxCreate connections at once   
if ( log.isDebugEnabled() ) {   
    log.debug( "++++ initializing pool with following settings:" );   
    log.debug( "++++ initial size: " + initConn );   
    log.debug( "++++ min spare   : " + minConn );   
    log.debug( "++++ max spare   : " + maxConn );   
}   
// if servers is not set, or it empty, then   
// throw a runtime exception   
if ( servers == null || servers.length <= 0 ) {   
    log.error( "++++ trying to initialize with no servers" );   
    throw new IllegalStateException( "++++ trying to initialize with no servers" );   
}   
    // initalize our internal hashing structures   
        if ( this.hashingAlg == CONSISTENT_HASH )   
        populateConsistentBuckets();   
    else  
            populateBuckets();  
availPool   = new HashMap<String,Map<SockIO,Long>>( servers.length * initConn );
busyPool    = new HashMap<String,Map<SockIO,Long>>( servers.length * initConn );
deadPool    = new IdentityHashMap<SockIO,Integer>();
hostDeadDur = new HashMap<String,Long>();
hostDead    = new HashMap<String,Date>();
maxCreate   = (poolMultiplier > minConn) ? minConn : minConn / poolMultiplier; // only create up to maxCreate connections at once
if ( log.isDebugEnabled() ) {
 log.debug( "++++ initializing pool with following settings:" );
 log.debug( "++++ initial size: " + initConn );
 log.debug( "++++ min spare   : " + minConn );
 log.debug( "++++ max spare   : " + maxConn );
}
// if servers is not set, or it empty, then
// throw a runtime exception
if ( servers == null || servers.length <= 0 ) {
 log.error( "++++ trying to initialize with no servers" );
 throw new IllegalStateException( "++++ trying to initialize with no servers" );
}
 // initalize our internal hashing structures
        if ( this.hashingAlg == CONSISTENT_HASH )
  populateConsistentBuckets();
 else
         populateBuckets();
看到這裏就是開闢一些連接池的空間,然後調用了根據我們選擇的hash 算法 執行populateBuckets();或者populateConsistentBuckets(); hash算法共有4種
Java代碼
// native String.hashCode();   
public static final int NATIVE_HASH     = 0;       
// original compatibility hashing algorithm (works with other clients)   
public static final int OLD_COMPAT_HASH = 1;   
// new CRC32 based compatibility hashing algorithm (works with other clients)      
public static final int NEW_COMPAT_HASH = 2;   
// MD5 Based -- Stops thrashing when a server added or removed   
public static final int CONSISTENT_HASH = 3;          
// native String.hashCode();
public static final int NATIVE_HASH     = 0; 
// original compatibility hashing algorithm (works with other clients)
public static final int OLD_COMPAT_HASH = 1;
// new CRC32 based compatibility hashing algorithm (works with other clients) 
public static final int NEW_COMPAT_HASH = 2;
// MD5 Based -- Stops thrashing when a server added or removed
public static final int CONSISTENT_HASH = 3;  我們通常用的是 NEW_COMPAT_HASH,這個保證可以wokrs with other clients 所以看一下populateBuckets()做了什麼
Java代碼
this.buckets = new ArrayList<String>();   
for ( int i = 0; i < servers.length; i++ ) {   
  if ( this.weights != null && this.weights.length > i ) {   
     for ( int k = 0; k < this.weights[i].intValue(); k++ ) {   
     this.buckets.add( servers[i] );   
     if ( log.isDebugEnabled() )   
        log.debug( "++++ added " + servers[i] + " to server bucket" );   
     }   
  }   
  else {   
     this.buckets.add( servers[i] );   
  }   
     // create initial connections   
  for ( int j = 0; j < initConn; j++ ) {   
          SockIO socket = createSocket( servers[i] );   
      if ( socket == null ) {   
        break;   
          }   
          addSocketToPool( availPool, servers[i], socket );   
  }   
}  
this.buckets = new ArrayList<String>();
for ( int i = 0; i < servers.length; i++ ) {
  if ( this.weights != null && this.weights.length > i ) {
     for ( int k = 0; k < this.weights[i].intValue(); k++ ) {
  this.buckets.add( servers[i] );
  if ( log.isDebugEnabled() )
     log.debug( "++++ added " + servers[i] + " to server bucket" );
     }
  }
  else {
     this.buckets.add( servers[i] );
  }
     // create initial connections
  for ( int j = 0; j < initConn; j++ ) {
          SockIO socket = createSocket( servers[i] );
   if ( socket == null ) {
  break;
          }
          addSocketToPool( availPool, servers[i], socket );
  }
}假如我們設置的servers是 192.168.0.1:44444和192.168.0.2:22222 然後我們設置了weights是 5和3 那麼 buckets list的值最終會是 [ 192.168.0.1:44444, 192.168.0.1:44444, 192.168.0.1:44444, 192.168.0.1:44444, 192.168.0.1:44444, 192.168.0.2:22222, 192.168.0.2:22222. 192.168.0.2:22222. ] 然後就開始根據initCon初始連接數按servers分別創建socket 那麼究竟這個buckets做什麼用呢? 在我們使用set存放對象時會調用
Java代碼
SockIOPool.SockIO sock = pool.getSock( key, hashCode );  
SockIOPool.SockIO sock = pool.getSock( key, hashCode );看一看pool.getSock的代碼
Java代碼
// get initial bucket   
long bucket = getBucket( key, hashCode );   
String server = ( this.hashingAlg == CONSISTENT_HASH )   
        ? consistentBuckets.get( bucket )   
        : buckets.get( (int)bucket );  
// get initial bucket
long bucket = getBucket( key, hashCode );
String server = ( this.hashingAlg == CONSISTENT_HASH )
  ? consistentBuckets.get( bucket )
  : buckets.get( (int)bucket );其中有段代碼是這樣的,看看getBucket
Java代碼
private 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;   
    }   
}  
private 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;
 }
}先不管key和hashCode,我們看到首先算出一個hc值後會直接做hc%buckets.size()實際上就是根據buckets的數量散列,最終值一定是buckets.size()範圍裏的一個值 然後最終server值就根據buckets.get( (int)bucket )得到,那麼假如我們得到bucket是3,則參照上面buckets 裏的值,得到 list.get(3)=192.168.0.1:44444 所以會根據weight設置的值的不同得到不同的server ,如果 weights設置10:1 那buckets裏就是10個相同的server和另一個不同的,將來散列得到的server很大可能性是servers裏設置的第一個server。

本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/sanshiqiduer/archive/2008/07/06/2616852.aspx

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章