jedis源码分析(六)-扩展哨兵监控多主节点连接池

之前分析了jedis中连接对象jedis,ShardedJedis 和连接池jedisPool,JedisSentinelPool源码,jedis和jedisPool都是对单个实例节点的操作,ShardedJedis是对多个节点的操作,jedisSentinelPool是对多个哨兵监控一个主节点,(从节点不关心)的操作,

目前我们遇到一个情况,我们实际场景中,可能有多个主节点,多个从节点,多个哨兵节点,查看jedis源码好像没有对这种场景支持的连接池,所以需要我们手动自己写一个连接池。根据我们前几章节的源码分析,将jedisSentinelPoll和ShardedJedis的情况做一个结合,可以做到支持我们的多个哨兵,多个主节点的分片存储。
首先我们回顾jedisSentinelPool的源码:
public JedisSentinelPool(String masterName , Set<String> sentinels ,
                      final GenericObjectPoolConfig poolConfig , final int connectionTimeout , final int soTimeout ,
                      final String password , final int database , final String clientName ) {
               this . poolConfig = poolConfig ;
               this . connectionTimeout = connectionTimeout ;
               this . soTimeout = soTimeout ;
               this . password = password ;
               this . database = database ;
               this . clientName = clientName ;
              HostAndPort master = initSentinels( sentinels , masterName );
              initPool( master );
       }
private HostAndPort initSentinels (Set<String> sentinels , final String masterName ) {
              HostAndPort master = null ;
               boolean sentinelAvailable = false ;
               log .info( "Trying to find master from available Sentinels..." );
               for (String sentinel : sentinels ) {
                      final HostAndPort hap = HostAndPort. parseString ( sentinel );
                      log .fine( "Connecting to Sentinel " + hap );
                     Jedis jedis = null ;
                      try {
                            jedis = new Jedis( hap .getHost(), hap .getPort());
                           List<String> masterAddr = jedis .sentinelGetMasterAddrByName( masterName );
                            // connected to sentinel...
                            sentinelAvailable = true ;
                            if ( masterAddr == null || masterAddr .size() != 2) {
                                   log .warning( "Can not get master addr, master name: " + masterName + ". Sentinel: " + hap
                                                + "." );
                                   continue ;
                           }
                            master = toHostAndPort( masterAddr );
                            log .fine( "Found Redis master at " + master );
                            break ;
                     } catch (JedisException e ) {
                            // resolves #1036, it should handle JedisException there's another chance
                            // of raising JedisDataException
                            log .warning( "Cannot get master address from sentinel running @ " + hap + ". Reason: " + e
                                         + ". Trying next one." );
                     } finally {
                            if ( jedis != null ) {
                                   jedis .close();
                           }
                     }
              }
               if ( master == null ) {
                      if ( sentinelAvailable ) {
                            // can connect to sentinel, but master name seems to not monitored
                            throw new JedisException( "Can connect to sentinel, but " + masterName
                                         + " seems to be not monitored..." );
                     } else {
                            throw new JedisConnectionException( "All sentinels down, cannot determine where is "
                                         + masterName + " master is running..." );
                     }
              }
               log .info( "Redis master running at " + master + ", starting Sentinel listeners..." );
               for (String sentinel : sentinels ) {
                      final HostAndPort hap = HostAndPort. parseString ( sentinel );
                     MasterListener masterListener = new MasterListener( masterName , hap .getHost(), hap .getPort());
                      // whether MasterListener threads are alive or not, process can be stopped
                      masterListener .setDaemon( true );
                      masterListeners .add( masterListener );
                      masterListener .start();
              }
               return master ;
       }
private void initPool (HostAndPort master ) {
               if (! master .equals( currentHostMaster )) {
                      currentHostMaster = master ;
                      if ( factory == null ) {
                            factory = new JedisFactory( master .getHost(), master .getPort(), connectionTimeout ,
                                          soTimeout , password , database , clientName , false , null , null , null );
                           initPool( poolConfig , factory );
                     } else {
                            factory .setHostAndPort( currentHostMaster );
                            // although we clear the pool, we still have to check the returned object
                            // in getResource, this call only clears idle instances, not borrowed instances
                            internalPool .clear();
                     }
                      log .info( "Created JedisPool to master at " + master );
              }
       }

由上面源码分析有几个关键点,在jedisSentinelPool的构造方法中,初始化配置参数,之后initSentinels方法通过哨兵列表集合对象和master节点名称获取到包含master节点host,port等信息的HostAndPort对象master。又通过initPool方法最终创建连接池。

initSentinels方法接收哨兵集合和单个master名称,返回单个的HostAndPort对象,fainitPool方法接收单个HostAndPort对象作为参数,
如果我们能让initSentinels方法能接收多个master节点名称,返回HostAndPort对象list集合,initPool接收HostAndPort对象list集合参数,就可以达到我们最终的目的实现哨兵监控多节点的连接池,这里有两个地方需要注意,一个masterListener哨兵列表监听和jedisFactory工厂类的改造。

改造后的支持多个主节点的分片储存连接池
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPubSub;
import redis.clients.jedis.JedisShardInfo;
import redis.clients.jedis.Protocol;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.exceptions.JedisConnectionException;
import redis.clients.jedis.exceptions.JedisException;
import redis.clients.util.Hashing;
import redis.clients.util.Pool;
/**
 * 哨兵监控多节点分片存储连接池
 * @author 01478849
 */
public class MultiShardedJedisSentinelPool extends Pool<ShardedJedis> {
        protected GenericObjectPoolConfig poolConfig ;
        protected int connectionTimeout = Protocol. DEFAULT_TIMEOUT ;
        protected int soTimeout = Protocol. DEFAULT_TIMEOUT ;
        protected String password ;
        protected int database = Protocol. DEFAULT_DATABASE ;
        protected String clientName ;
        protected Set<MasterListener> masterListeners = new HashSet<MasterListener>();
        protected Logger log = Logger. getLogger (getClass().getName());
        private volatile ShardedJedisFactory factory ;
        private volatile List<HostAndPort> currentHostMasters ;
        public MultiShardedJedisSentinelPool(List<String> masterNames , Set<String> sentinels ) {
               this ( masterNames , sentinels , new GenericObjectPoolConfig(), Protocol. DEFAULT_TIMEOUT , null ,
                           Protocol. DEFAULT_DATABASE );
       }
        public MultiShardedJedisSentinelPool(List<String> masterNames , Set<String> sentinels , final GenericObjectPoolConfig poolConfig ) {
               this ( masterNames , sentinels , poolConfig , Protocol. DEFAULT_TIMEOUT , null ,
                           Protocol. DEFAULT_DATABASE );
       }
        public MultiShardedJedisSentinelPool(List<String> masterNames , Set<String> sentinels , String password ) {
               this ( masterNames , sentinels , new GenericObjectPoolConfig(), Protocol. DEFAULT_TIMEOUT , password );
       }
        public MultiShardedJedisSentinelPool(List<String> masterNames , Set<String> sentinels ,
                      final GenericObjectPoolConfig poolConfig , int timeout , final String password ) {
               this ( masterNames , sentinels , poolConfig , timeout , password , Protocol. DEFAULT_DATABASE );
       }
        public MultiShardedJedisSentinelPool(List<String> masterNames , Set<String> sentinels ,
                      final GenericObjectPoolConfig poolConfig , final int timeout ) {
               this ( masterNames , sentinels , poolConfig , timeout , null , Protocol. DEFAULT_DATABASE );
       }
        public MultiShardedJedisSentinelPool(List<String> masterNames , Set<String> sentinels ,
                      final GenericObjectPoolConfig poolConfig , final String password ) {
               this ( masterNames , sentinels , poolConfig , Protocol. DEFAULT_TIMEOUT , password );
       }
        public MultiShardedJedisSentinelPool(List<String> masterNames , Set<String> sentinels ,
                      final GenericObjectPoolConfig poolConfig , int timeout , final String password ,
                      final int database ) {
               this ( masterNames , sentinels , poolConfig , timeout , timeout , password , database );
       }
        public MultiShardedJedisSentinelPool(List<String> masterNames , Set<String> sentinels ,
                      final GenericObjectPoolConfig poolConfig , final int connectionTimeout , final int soTimeout ,
                      final String password , final int database ) {
               this . poolConfig = poolConfig ;
               this . connectionTimeout = connectionTimeout ;
               this . soTimeout = soTimeout ;
               this . password = password ;
               this . database = database ;
              List<HostAndPort> masters = initSentinels( sentinels , masterNames );
              initPool( masters );
       }
        public void destroy() {
               for (MasterListener m : masterListeners ) {
                      m .shutdown();
              }
               super .destroy();
       }
        public List<HostAndPort> getCurrentHostMaster() {
               return currentHostMasters ;
       }
        private void initPool(List<HostAndPort> masters ) {
             
               if (!equals( masters , currentHostMasters )) {
                      currentHostMasters = masters ;
                     List<JedisShardInfo> shards = new ArrayList<JedisShardInfo>();
                     JedisShardInfo info = null ;
                      for (HostAndPort hostAndPort : masters ) {
                            info = new JedisShardInfo( hostAndPort .getHost(), hostAndPort .getPort());
                            info .setPassword( password );
                            shards .add( info );
                     }
                      if ( factory != null ) {
                            internalPool .clear();
                     }
                      factory = new ShardedJedisFactory( shards , Hashing. MURMUR_HASH , null );
                     initPool( poolConfig , factory );
                      log .info( "Created JedisPool to master at " + masters .toString());
              }
       }
        public boolean equals(List<HostAndPort> masters , List<HostAndPort> currentHostMasters ) {
               if ( currentHostMasters != null && masters != null && masters .size() == currentHostMasters .size()) {
                      for (HostAndPort info : masters ) {
                            if (! currentHostMasters .contains( info )) {
                                   return false ;
                           }
                     }
                      return true ;
              }
               return false ;
       }
        private List<HostAndPort> initSentinels(Set<String> sentinels , final List<String> masterNames ) {
              List<HostAndPort> masterList = new ArrayList<HostAndPort>();
              Map<String, HostAndPort> map = new HashMap<String, HostAndPort>();
               log .info( "Trying to find master from available Sentinels..." + sentinels .toString());
               for (String sentinel : sentinels ) {
                      final HostAndPort hap = toHostAndPort(Arrays. asList ( sentinel .split( ":" )));
                      log .info( "Connecting to Sentinel " + hap );
                     Jedis jedis = null ;
                      try {
                            jedis = new Jedis( hap .getHost(), hap .getPort());
                            for (String masterName : masterNames ) {
                                  List<String> masterAddr = jedis .sentinelGetMasterAddrByName( masterName );
                                   if ( masterAddr != null && masterAddr .size() == 2) {
                                         HostAndPort master = toHostAndPort( masterAddr );
                                          if ( master != null ) {
                                                 if (! masterList .contains( master )) {
                                                        masterList .add( master );
                                                }
                                                 map .put( masterName , master );
                                         }
                                  }
                           }
                            log .info( "Found Redis master at " + masterList .toString());
                     } catch (JedisException e ) {
                            log .warning( "Cannot get master address from sentinel running @ " + hap + ". Reason: " + e + ". Trying next one." );
                     } finally {
                            if ( jedis != null ) {
                                   jedis .close();
                           }
                     }
              }
               log .info( "Redis master running at " + masterList .toString() + ", starting Sentinel listeners..." );
               for (String sentinel : sentinels ) {
                      final HostAndPort hap = toHostAndPort(Arrays. asList ( sentinel .split( ":" )));
                     MasterListener masterListener = new MasterListener( hap .getHost(), hap .getPort(), map );
                      masterListener .setDaemon( true );
                      masterListeners .add( masterListener );
                      masterListener .start();
              }
               return masterList ;
       }
        private HostAndPort toHostAndPort(List<String> getMasterAddrByNameResult ) {
              String host = getMasterAddrByNameResult .get(0);
               int port = Integer. parseInt ( getMasterAddrByNameResult .get(1));
               return new HostAndPort( host , port );
       }
        @Override
        public ShardedJedis getResource() {
               while ( true ) {
                     ShardedJedis shardedJedis = super .getResource();
                      shardedJedis .setDataSource( this );
                      return shardedJedis ;
              }
       }
        /**
        * @deprecated starting from Jedis 3.0 this method will not be exposed. Resource
        *             cleanup should be done using @see
        *             {@link redis.clients.jedis.Jedis#close()}
        */
        @Override
        @Deprecated
        public void returnBrokenResource ( final ShardedJedis resource ) {
               if ( resource != null ) {
                     returnBrokenResourceObject( resource );
              }
       }
        /**
        * @deprecated starting from Jedis 3.0 this method will not be exposed. Resource
        *             cleanup should be done using @see
        *             {@link redis.clients.jedis.Jedis#close()}
        */
        @Override
        @Deprecated
        public void returnResource ( final ShardedJedis resource ) {
               if ( resource != null ) {
                      resource .resetState();
                      returnResourceObject ( resource );
              }
       }
        protected class MasterListener extends Thread {
               protected String host ;
               protected int port ;
               protected long subscribeRetryWaitTimeMillis = 5000;
               protected volatile Jedis j ;
               protected AtomicBoolean running = new AtomicBoolean( false );
               protected Map<String, HostAndPort> map ;
               protected MasterListener() {
              }
               public MasterListener(String host , int port , Map<String, HostAndPort> map ) {
                      super (String. format ( "MasterListener-%s-[%s:%d]" , host + ":" + port , host , port ));
                      this . host = host ;
                      this . port = port ;
                      this . map = map ;
              }
               public MasterListener(String masterName , String host , int port , Map<String, HostAndPort> map ,
                            long subscribeRetryWaitTimeMillis ) {
                      this ( host , port , map );
                      this . subscribeRetryWaitTimeMillis = subscribeRetryWaitTimeMillis ;
              }
               @Override
               public void run() {
                      running .set( true );
                      while ( running .get()) {
                            j = new Jedis( host , port );
                            try {
                                   // double check that it is not being shutdown
                                   if (! running .get()) {
                                          break ;
                                  }
                                   j .subscribe( new JedisPubSub() {
                                          @Override
                                          public void onMessage(String channel , String message ) {
                                                 log .info( "Sentinel " + host + ":" + port + " published: " + message + "." );
                                                String[] switchMasterMsg = message .split( " " );
                                                 log .info( "Sentinel masters before change map: " + map .toString() + "." );
                                                 log .info( "Sentinel masters before change: currentHostMasters" + currentHostMasters .toString() + "." );
                                                 if ( switchMasterMsg . length > 3) {
                                                        // 根据变更主节点名称从map中获取变更之前的地址对象
                                                       HostAndPort master = map .get( switchMasterMsg [0]);
                                                       HostAndPort newHostAndPort = toHostAndPort(Arrays. asList ( switchMasterMsg [3], switchMasterMsg [4]));
                                                       List<HostAndPort> shards = new ArrayList<>();
                                                        for (HostAndPort oldHostAndPort : currentHostMasters ) {
                                                               if ( oldHostAndPort .equals( master )) {
                                                                      shards .add( newHostAndPort );
                                                              } else {
                                                                      shards .add( oldHostAndPort );
                                                              }
                                                       }
                                                        log .info( "Sentinel masters after change: " + shards .toString() + "." );
                                                       initPool( shards );
                                                } else {
                                                        log .info( "Invalid message received on Sentinel " + host + ":" + port
                                                                     + " on channel +switch-master: " + message );
                                                }
                                         }
                                  }, "+switch-master" );
                           } catch (JedisConnectionException e ) {
                                   if ( running .get()) {
                                          log .log(Level. SEVERE , "Lost connection to Sentinel at " + host + ":" + port
                                                       + ". Sleeping 5000ms and retrying." , e );
                                          try {
                                                Thread. sleep ( subscribeRetryWaitTimeMillis );
                                         } catch (InterruptedException e1 ) {
                                                 log .log(Level. SEVERE , "Sleep interrupted: " , e1 );
                                         }
                                  } else {
                                          log .info( "Unsubscribing from Sentinel at " + host + ":" + port );
                                  }
                           } finally {
                                   j .close();
                           }
                     }
              }
               public void shutdown() {
                      try {
                            log .info( "Shutting down listener on " + host + ":" + port );
                            running .set( false );
                            // This isn't good, the Jedis object is not thread safe
                            if ( j != null ) {
                                   j .disconnect();
                           }
                     } catch (Exception e ) {
                            log .log(Level. SEVERE , "Caught exception while shutting down: " , e );
                     }
              }
       }
        /**
        * ShardedJedis对象工厂类
        *
        * @author 01478849
        *
        */
        public class ShardedJedisFactory implements PooledObjectFactory<ShardedJedis> {
               private List<JedisShardInfo> shards ;
               private Hashing algo ;
               private Pattern keyTagPattern ;
               public ShardedJedisFactory( final List<JedisShardInfo> shards , final Hashing algo , final Pattern keyTagPattern ) {
                      this . shards = shards ;
                      this . algo = algo ;
                      this . keyTagPattern = keyTagPattern ;
              }
               @Override
               public PooledObject<ShardedJedis> makeObject() throws Exception {
                     ShardedJedis jedis = new ShardedJedis( shards , algo , keyTagPattern );
                      return new DefaultPooledObject<ShardedJedis>( jedis );
              }
               @Override
               public void destroyObject(PooledObject<ShardedJedis> pooledShardedJedis ) throws Exception {
                      final ShardedJedis shardedJedis = pooledShardedJedis .getObject();
                      for (Jedis jedis : shardedJedis .getAllShards()) {
                            try {
                                   try {
                                          jedis .quit();
                                  } catch (Exception e ) {
                                  }
                                   jedis .disconnect();
                           } catch (Exception e ) {
                           }
                     }
              }
               @Override
               public boolean validateObject(PooledObject<ShardedJedis> pooledShardedJedis ) {
                      try {
                           ShardedJedis jedis = pooledShardedJedis .getObject();
                            for (Jedis shard : jedis .getAllShards()) {
                                   if (! shard .ping().equals( "PONG" )) {
                                          return false ;
                                  }
                           }
                            return true ;
                     } catch (Exception ex ) {
                            return false ;
                     }
              }
               @Override
               public void activateObject(PooledObject<ShardedJedis> p ) throws Exception {
              }
               @Override
               public void passivateObject(PooledObject<ShardedJedis> p ) throws Exception {
              }
       }
}
     
几天关键地方,一个是SharedJedisFactory工厂类,可以直接使用SharedJedis中ShardedJedisFactory工厂类,直接拷贝过来,另一个是MasterListener哨兵的监听类,通过redis的消息订阅直接订阅哨兵的消息变更,如果哨兵监控的主节点发生变更,listener监听类中subscribe方法监听变更消息,onMessage方法中,接收到变更后的节点信息将变更的节点地址变更。
变更信息数据格式:master1(变更节点名称) host(变更前地址)  port(变更后端口)  host(变更后地址) port(变更后端口) 
数据样例:master1 127.0.0.1 4102 127.0.0.1 4100.
判断替换变更的逻辑,首先在 initSentinels方法初始化的时候将循环遍历的节点信息放入Map中,其中key是节点名称masterName,value值是封装了host,port信息的HostAntPort对象, 之后在循环创建哨兵监听masterListener的将map传入,在listener的onMessage中判断,
 首先通过空格分割订阅接收的消息, String[]   switchMasterMsg   =   message .split( " " ); 拿到节点名称 switchMasterMsg [0]
通过节点名称取到初始化放入map中的节点变更之前的地址信息,重新创建一个新的HostAndPort对象list,循环变更之前的节点地址列表currentHostMasters,这里储存的都是地址对象HostAndPort,将地址对象都放入新的list中,这里有一个判断如果循环的地址对象与从map中取到的地址对象HostAndPort相等,说明就是这个对象地址发生了变更,那么就将新接收到的地址封装成地址对象放入新的list集合中,最后调用initPool方法重新初始化连接池。
public void onMessage(String channel , String message ) {
   log .fine( "Sentinel " + host + ":" + port + " published: " + message + "." );
  String[] switchMasterMsg = message .split( " " );
   if ( switchMasterMsg . length > 3) {
     HostAndPort master = map .get( switchMasterMsg [0]);
     HostAndPort newHostAndPort = toHostAndPort(Arrays. asList ( switchMasterMsg [3], switchMasterMsg [4]));
     List<HostAndPort> shards = new ArrayList<>();
      for (HostAndPort oldHostAndPort : currentHostMasters ) {
         if ( oldHostAndPort .equals( master )) {
             shards .add( newHostAndPort );
        } else {
            shards .add( oldHostAndPort );
         }
     }
              initPool( shards );
     } else {
        log .severe( "Invalid message received on Sentinel " + host + ":" + port
           + " on channel +switch-master: " + message );
        }
   }

之前在github上有另一种MasterListener监听判断方法,代码:
protected class MasterListener extends Thread {
               protected List<String> masters ;
               protected String host ;
               protected int port ;
               protected long subscribeRetryWaitTimeMillis = 5000;
               protected Jedis jedis ;
               protected AtomicBoolean running = new AtomicBoolean( false );
               protected MasterListener() {
              }
               public MasterListener(List<String> masters , String host , int port ) {
                      this . masters = masters ;
                      this . host = host ;
                      this . port = port ;
              }
               public MasterListener(List<String> masters , String host , int port , long subscribeRetryWaitTimeMillis ) {
                      this ( masters , host , port );
                      this . subscribeRetryWaitTimeMillis = subscribeRetryWaitTimeMillis ;
              }
               public void run() {
                      running .set( true );
                      while ( running .get()) {
                            jedis = new Jedis( host , port );
                            try {
                                   jedis .subscribe( new JedisPubSubAdapter() {
                                          @Override
                                          public void onMessage(String channel , String message ) {
                                                 log .fine( "Sentinel " + host + ":" + port + " published: " + message + "." );
                                                String[] switchMasterMsg = message .split( " " );
                                                 if ( switchMasterMsg . length > 3) {
                                                        int index = masters .indexOf( switchMasterMsg [0]);
                                                        if ( index >= 0) {
                                                              HostAndPort newHostMaster = toHostAndPort(
                                                                            Arrays. asList ( switchMasterMsg [3], switchMasterMsg [4]));
                                                              List<HostAndPort> newHostMasters = new ArrayList<HostAndPort>();
                                                               for ( int i = 0; i < masters .size(); i ++) {
                                                                      newHostMasters .add( null );
                                                              }
                                                              Collections. copy ( newHostMasters , currentHostMasters );
                                                               newHostMasters .set( index , newHostMaster );
                                                              initPool( newHostMasters );
                                                       } else {
                                                              StringBuffer sb = new StringBuffer();
                                                               for (String masterName : masters ) {
                                                                      sb .append( masterName );
                                                                      sb .append( "," );
                                                              }
                                                               log .fine( "Ignoring message on +switch-master for master name " + switchMasterMsg [0]
                                                                           + ", our monitor master name are [" + sb + "]" );
                                                       }
                                                } else {
                                                        log .severe( "Invalid message received on Sentinel " + host + ":" + port
                                                                     + " on channel +switch-master: " + message );
                                                }
                                         }
                                  }, "+switch-master" );
                           } catch (JedisConnectionException e ) {
                                   if ( running .get()) {
                                          log .severe( "Lost connection to Sentinel at " + host + ":" + port
                                                       + ". Sleeping 5000ms and retrying." );
                                          try {
                                                Thread. sleep ( subscribeRetryWaitTimeMillis );
                                         } catch (InterruptedException e1 ) {
                                                 e1 .printStackTrace();
                                         }
                                  } else {
                                          log .fine( "Unsubscribing from Sentinel at " + host + ":" + port );
                                  }
                           }
                     }
              }
               public void shutdown() {
                      try {
                            log .fine( "Shutting down listener on " + host + ":" + port );
                            running .set( false );
                            // This isn't good, the Jedis object is not thread safe
                            jedis .disconnect();
                     } catch (Exception e ) {
                            log .severe( "Caught exception while shutting down: " + e .getMessage());
                     }
              }
       }
这里在初始化MasterListener的时候传入了List<MasterNames>,通过接收的节点名称确定在list中的位置index, int index = masters .indexOf( switchMasterMsg [0]);同样创建一个跟list同样大小的List<HostAndPort>集合newHostMasters,将变更前的地址对象集合currentHostMasters通过Collections的copy方法复制到新的newHostMasters中,然后根据之前确定的变更节点的位置index,重新将新值赋值,newHostMasters.set(index, newHostMaster);最后调用initPool重新初始化连接池。

多节点测试连接池示例:本示例两个主节点,一个哨兵节点
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.logging.Logger;
import com.redis.demo.MultiShardedJedisSentinelPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.ShardedJedis;
public class ShardedJedisSentinelPoolUtil {
        private static Logger logger = Logger. getLogger ( "ShardedJedisSentinelPoolUtil" );
        // private static ShardedJedisSentinelPool pool = null;
        // private static MyJedisSentinelPool pool = null;
        private static MultiShardedJedisSentinelPool pool = null ;
        /**
        * 创建连接池
        *
        */
        static {
              JedisPoolConfig config = new JedisPoolConfig();
               config .setMaxIdle(10);
               config .setMaxTotal(200);
               config .setMaxWaitMillis(1000 * 10);
               config .setTestOnBorrow( true );
              Set<String> sentinels = new HashSet<String>();
               sentinels .add( "127.0.0.1:4200" );
              sentinels.add("127.0.0.1:4201");
              List<String> masters = new ArrayList<String>();
               masters .add( "master1" );
               masters .add( "master2" );
                pool = new MultiShardedJedisSentinelPool( masters , sentinels , config );
               logger .info( "多节点连接池创建成功" );
       }
     
        public static  synchronized  String set(String key , String value ) {
              ShardedJedis jedis = null ;
              String result = null ;
               try {
                      if ( jedis == null ) {
                            jedis = pool .getResource();
                     }
                      result = jedis .set( key , value );
              } catch (Exception e ) {
                      logger .info( e .getMessage());
              } finally {
                      jedis .close();
              }
               return result ;
       }
        public static String get(String key ) {
              ShardedJedis jedis = null ;
              String result = null ;
               try {
                      if ( jedis == null ) {
                            jedis = pool .getResource();
                     }
                      result = jedis .get( key );
              } catch (Exception e ) {
                      logger .info( e .getMessage());
              } finally {
                      jedis .close();
              }
               return result ;
       }
        public static void main(String[] args ) {
               Thread1 thread1 = new Thread1();
               thread1 .start();
              Thread1 thread2 = new Thread1();
               thread2 .start();
              Thread1 thread3 = new Thread1();
               thread3 .start();
              Thread1 thread4 = new Thread1();
               thread4 .start();
              Thread1 thread5 = new Thread1();
               thread5 .start();
              Thread1 thread6 = new Thread1();
               thread6 .start();
              Thread1 thread7 = new Thread1();
               thread7 .start();
              Thread1 thread8 = new Thread1();
               thread8 .start();
              Thread1 thread9 = new Thread1();
               thread9 .start();
              Thread1 thread10 = new Thread1();
               thread10 .start();
       }
}
class Thread1 extends Thread {
        public void run() {
               while ( true ) {
                     String ts = new SimpleDateFormat( "hh:mm:ss" ).format( new Date());
                     ShardedJedis jedis = ShardedJedisSentinelPoolUtil. getJedis ();
                      try {
                           String key = "hello" + String. valueOf ( new Random().nextInt());
                           String value = String. valueOf ( new Random().nextInt());
                           System. out .println( "线程id::" + Thread. currentThread ().getId() + ": " + ts + ": hello=" + jedis .set( key , value ));
                     } catch (Exception e ) {
                           System. out .println( "线程id::" + Thread. currentThread ().getId() + ": " + ts + ": Cannot connect to Redis  " + e .getMessage());
                     } finally {
                            jedis .close();
                     }
                      try {
                           Thread. sleep (500L);
                     } catch (InterruptedException e ) {
                            e .printStackTrace();
                     }
              }
       }
}

注:使用完的资源一定要close关闭,否则会导致无法获取链接资源。多线程并发测试需要在操作方法上加上 synchronized锁,否则会获取不到资源异常。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值