zoukankan      html  css  js  c++  java
  • Jedis源码浅析

    1、概述

    Jedis是redis官网推荐的redis java client,代码维护在github https://github.com/xetorthio/jedis

    本质上Jedis帮我们封装了各种redis命令,提供了各种和redis命令相关的方法使用。Jedis的基本结构如下图1.1所示。

    clipboard

    图1.1 Jedis 工作过程

    可以看到Jedis通过socket和redis server通信,通过发送redis命令和参数,接收redis server的结果,从而实现redis client。

     

    2、源码解析

    按照Jedis源码分为两个层次分析,分别是:以Jedis为核心和以JedisPool为核心。

    先看一下Jedis jar包的层次结构,如下图2.1所示,下面都是以 jedis-2.9.0 为例。

    clipboard

    图2.1 Jedis Jar包层次结构

    可以看到,Jedis Jar包结构简单,主要有两个package:redis.clients.jedis 和 redis.clients.util。jedis包下主要是和jedis相关的核心类,util包下是jedis会用到的各种工具类。需要注意的是:jedis依赖apache 的 commons-pool2 包。

    2.1 Jedis

    使用Jedis连接redis server的Java代码如下:

    //连接本地的 Redis 服务
    Jedis jedis = new Jedis("localhost", 6379); 
    jedis.set("key","val");

     

    一行代码就能完成redis的连接,从而开始使用redis。

    先看一下Jedis这个类图,如下图2.2所示。

    clipboard

    图2.2 Jedis 类图

    上图可以看到,Jedis类继承了1个类,实现了7个接口。实现了BinaryJedis类,该类主要用于处理二进制数据,Socket发送给redis server的数据是二进制的。分别实现了JedisCommands、MultiKeyCommands、BasicCommands、ScriptingCommands、SentinelCommands、AdvancedJedisCommands、ClusterCommands接口,从接口名上可以看到,这些接口都声明了不同场景下的redis命令方法。JedisCommands声明了常用的redis命令方法,包含redis五种数据接口的相关操作方法;MultiKeyCommands声明了常用的redis批量操作方法;BasicCommands声明了redis基本系统操作方法;ScriptingCommands声明了redis 相关脚本命令方法;SentinelCommands声明了redis在哨兵模式下的相关命令方法;ClusterCommands声明了redis集群模式下的相关命令方法;AdvancedJedisCommands声明了redis的一些高级命令方法,包含redis配置等。

    下面来分析一下Jedis的代码,其代码如下:

    public class Jedis extends BinaryJedis implements JedisCommands, MultiKeyCommands,
        AdvancedJedisCommands, ScriptingCommands, BasicCommands, ClusterCommands, SentinelCommands {
    
      protected Pool<Jedis> dataSource = null;
      // Jedis提供了很多不同的构造方法,提供了各种接入手段,方便开发
      // Jedis本质上通过调用父类BinaryJedis的构造器来完成初始化操作
      public Jedis() {
        super();
      }
    
      public Jedis(final String host) {
        super(host);
      }
    
      public Jedis(final String host, final int port) {
        super(host, port);
      }
      
      ...
     
      // 下面是redis各种命令的封装方法
      // 本质上都是调用父类 BinaryJedis 中定义的 client 来完成各种操作
      /**
       * Set the string value as value of the key. The string can't be longer than 1073741824 bytes (1
       * GB).
       * <p>
       * Time complexity: O(1)
       * @param key
       * @param value
       * @return Status code reply
       */
      public String set(final String key, String value) {
        checkIsInMultiOrPipeline();
        client.set(key, value);
        return client.getStatusCodeReply();
      }
    
      /**
       * Set the string value as value of the key. The string can't be longer than 1073741824 bytes (1
       * GB).
       * @param key
       * @param value
       * @param nxxx NX|XX, NX -- Only set the key if it does not already exist. XX -- Only set the key
       *          if it already exist.
       * @param expx EX|PX, expire time units: EX = seconds; PX = milliseconds
       * @param time expire time in the units of <code>expx</code>
       * @return Status code reply
       */
      public String set(final String key, final String value, final String nxxx, final String expx,
          final long time) {
        checkIsInMultiOrPipeline();
        client.set(key, value, nxxx, expx, time);
        return client.getStatusCodeReply();
      }
      
      ...
      
    }

     

    从上面可以看到Jedis本质上通过父类BinaryJedis中定义的 client来完成各种redis操作。BinaryJedis的代码如下:

    public class BinaryJedis implements BasicCommands, BinaryJedisCommands, MultiKeyBinaryCommands,
        AdvancedBinaryJedisCommands, BinaryScriptingCommands, Closeable {
      // 用于和redis通信的客户端实例
      protected Client client = null;
      protected Transaction transaction = null;
      protected Pipeline pipeline = null;
      
       // BinaryJedis提供了许多构造方法,用于初始化,
       // Jedis类也是通过调用父类BinaryJedis的构造方法完成初始化
      // BinaryJedis构造方法中本质上是在初始化Clent实例
      public BinaryJedis() {
        client = new Client();
      }
    
      public BinaryJedis(final String host) {
        URI uri = URI.create(host);
        if (uri.getScheme() != null && uri.getScheme().equals("redis")) {
          initializeClientFromURI(uri);
        } else {
          client = new Client(host);
        }
      }
    
      public BinaryJedis(final String host, final int port) {
        client = new Client(host, port);
      }
    
      public BinaryJedis(final String host, final int port, final boolean ssl) {
        client = new Client(host, port, ssl);
      }
    
      public BinaryJedis(final String host, final int port, final boolean ssl,
          final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters,
          final HostnameVerifier hostnameVerifier) {
        client = new Client(host, port, ssl, sslSocketFactory, sslParameters, hostnameVerifier);
      }
    
      public BinaryJedis(final String host, final int port, final int timeout) {
        client = new Client(host, port);
        client.setConnectionTimeout(timeout);
        client.setSoTimeout(timeout);
      }
    
      public BinaryJedis(final String host, final int port, final int timeout, final boolean ssl) {
        client = new Client(host, port, ssl);
        client.setConnectionTimeout(timeout);
        client.setSoTimeout(timeout);
      }
    
      public BinaryJedis(final String host, final int port, final int timeout, final boolean ssl,
          final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters,
          final HostnameVerifier hostnameVerifier) {
        client = new Client(host, port, ssl, sslSocketFactory, sslParameters, hostnameVerifier);
        client.setConnectionTimeout(timeout);
        client.setSoTimeout(timeout);
      }
    
      public BinaryJedis(final String host, final int port, final int connectionTimeout,
          final int soTimeout) {
        client = new Client(host, port);
        client.setConnectionTimeout(connectionTimeout);
        client.setSoTimeout(soTimeout);
      }
    
      public BinaryJedis(final String host, final int port, final int connectionTimeout,
          final int soTimeout, final boolean ssl) {
        client = new Client(host, port, ssl);
        client.setConnectionTimeout(connectionTimeout);
        client.setSoTimeout(soTimeout);
      }
      
      ...
    }

     

    通过BinaryJedis代码可以看到,Jedis主要通过Client来完成具体redis操作,Client的代码如下:

    public class Client extends BinaryClient implements Commands {
     
      // Client类的构造器调用了父类BinaryClient的构造器
      public Client() {
        super();
      }
    
      public Client(final String host) {
        super(host);
      }
    
      public Client(final String host, final int port) {
        super(host, port);
      }
    
      public Client(final String host, final int port, final boolean ssl) {
        super(host, port, ssl);
      }
    
      public Client(final String host, final int port, final boolean ssl,
          final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters,
          final HostnameVerifier hostnameVerifier) {
        super(host, port, ssl, sslSocketFactory, sslParameters, hostnameVerifier);
      }
    
      // 封装了各种redis命令方法,本质上调用了父类BinaryClient中的对应方法
      @Override
      public void set(final String key, final String value) {
        set(SafeEncoder.encode(key), SafeEncoder.encode(value));
      }
    
      public void set(final String key, final String value, final String nxxx, final String expx,
          final long time) {
        set(SafeEncoder.encode(key), SafeEncoder.encode(value), SafeEncoder.encode(nxxx),
          SafeEncoder.encode(expx), time);
      }
    
      public void get(final String key) {
        get(SafeEncoder.encode(key));
      }
    
      public void exists(final String key) {
        exists(SafeEncoder.encode(key));
      }
    
      public void exists(final String... keys) {
        final byte[][] bkeys = SafeEncoder.encodeMany(keys);
        exists(bkeys);
      }
    
      public void del(final String... keys) {
        final byte[][] bkeys = new byte[keys.length][];
        for (int i = 0; i < keys.length; i++) {
          bkeys[i] = SafeEncoder.encode(keys[i]);
        }
        del(bkeys);
      }
      ...
      
    }

     

    从上可以看到,Client类定义了封装redis命令的方法,本质上调用的父类BinaryClient中的方法,BinaryClient代码如下:

    public class BinaryClient extends Connection {
      public enum LIST_POSITION {
        BEFORE, AFTER;
        public final byte[] raw;
    
        private LIST_POSITION() {
          raw = SafeEncoder.encode(name());
        }
      }
    
      private boolean isInMulti;
    
      private String password;
    
      private long db;
    
      private boolean isInWatch;
    
      // Binary提供了各种构造器方法
      public BinaryClient() {
        super();
      }
    
      public BinaryClient(final String host) {
        super(host);
      }
    
      public BinaryClient(final String host, final int port) {
        super(host, port);
      }
    
      public BinaryClient(final String host, final int port, final boolean ssl) {
        super(host, port, ssl);
      }
    
      public BinaryClient(final String host, final int port, final boolean ssl,
          final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters,
          final HostnameVerifier hostnameVerifier) {
        super(host, port, ssl, sslSocketFactory, sslParameters, hostnameVerifier);
      }
    
      public boolean isInMulti() {
        return isInMulti;
      }
    
      public boolean isInWatch() {
        return isInWatch;
      }
      
      private byte[][] joinParameters(byte[] first, byte[][] rest) {
        byte[][] result = new byte[rest.length + 1][];
        result[0] = first;
        System.arraycopy(rest, 0, result, 1, rest.length);
        return result;
      }
    
      public void setPassword(final String password) {
        this.password = password;
      }
    
      public void setDb(long db) {
        this.db = db;
      }
    
      @Override
      public void connect() {
        if (!isConnected()) {
          super.connect();
          if (password != null) {
            auth(password);
            getStatusCodeReply();
          }
          if (db > 0) {
            select(Long.valueOf(db).intValue());
            getStatusCodeReply();
          }
        }
      }
    
      // 所有的redis命令方法本质上调用的父类Connection中的sendCommand方法
      public void ping() {
        sendCommand(Command.PING);
      }
    
      public void set(final byte[] key, final byte[] value) {
        sendCommand(Command.SET, key, value);
      }
    
      public void set(final byte[] key, final byte[] value, final byte[] nxxx, final byte[] expx,
          final long time) {
        sendCommand(Command.SET, key, value, nxxx, expx, toByteArray(time));
      }
    
      public void get(final byte[] key) {
        sendCommand(Command.GET, key);
      }
    
      public void quit() {
        db = 0;
        sendCommand(QUIT);
      }
    
      public void exists(final byte[]... key) {
        sendCommand(EXISTS, key);
      }
      
      ...
      
    }

     

    上面可以看到,BinaryClient中的redis命令方法本质上调用的是父类Connection中的sendCommand方法,下面看一下Connection代码。

    public class Connection implements Closeable {
    
      private static final byte[][] EMPTY_ARGS = new byte[0][];
     
      // redis server host & port
      private String host = Protocol.DEFAULT_HOST;
      private int port = Protocol.DEFAULT_PORT;
      // 用于和redis server通信的socket
      private Socket socket;
      // 用于发送和接收数据的io流对象
      private RedisOutputStream outputStream;
      private RedisInputStream inputStream;
      private int pipelinedCommands = 0;
      private int connectionTimeout = Protocol.DEFAULT_TIMEOUT;
      private int soTimeout = Protocol.DEFAULT_TIMEOUT;
      private boolean broken = false;
      private boolean ssl;
      private SSLSocketFactory sslSocketFactory;
      private SSLParameters sslParameters;
      private HostnameVerifier hostnameVerifier;
    
      // Connection类提供了各种不同的构造方法
      public Connection() {
      }
    
      public Connection(final String host) {
        this.host = host;
      }
    
      public Connection(final String host, final int port) {
        this.host = host;
        this.port = port;
      }
    
      public Connection(final String host, final int port, final boolean ssl) {
        this.host = host;
        this.port = port;
        this.ssl = ssl;
      }
    
      public Connection(final String host, final int port, final boolean ssl,
          SSLSocketFactory sslSocketFactory, SSLParameters sslParameters,
          HostnameVerifier hostnameVerifier) {
        this.host = host;
        this.port = port;
        this.ssl = ssl;
        this.sslSocketFactory = sslSocketFactory;
        this.sslParameters = sslParameters;
        this.hostnameVerifier = hostnameVerifier;
      }
    
      public Socket getSocket() {
        return socket;
      }
    
      public int getConnectionTimeout() {
        return connectionTimeout;
      }
    
      public int getSoTimeout() {
        return soTimeout;
      }
    
      public void setConnectionTimeout(int connectionTimeout) {
        this.connectionTimeout = connectionTimeout;
      }
    
      public void setSoTimeout(int soTimeout) {
        this.soTimeout = soTimeout;
      }
    
      public void setTimeoutInfinite() {
        try {
          if (!isConnected()) {
            connect();
          }
          socket.setSoTimeout(0);
        } catch (SocketException ex) {
          broken = true;
          throw new JedisConnectionException(ex);
        }
      }
    
      public void rollbackTimeout() {
        try {
          socket.setSoTimeout(soTimeout);
        } catch (SocketException ex) {
          broken = true;
          throw new JedisConnectionException(ex);
        }
      }
      
      // redis命令调用方法,本质上调用的都是sendCommand通用方法
      protected Connection sendCommand(final Command cmd, final String... args) {
        final byte[][] bargs = new byte[args.length][];
        for (int i = 0; i < args.length; i++) {
          bargs[i] = SafeEncoder.encode(args[i]);
        }
        return sendCommand(cmd, bargs);
      }
    
      protected Connection sendCommand(final Command cmd) {
        return sendCommand(cmd, EMPTY_ARGS);
      }
      
      ...
    
      // 具体和redis通信,发送命令的方法
      protected Connection sendCommand(final Command cmd, final byte[]... args) {
          try {
            // 1. 建立redis连接  
            connect();
            // 2. 使用Protocol.sendCommand方法发送redis命令和参数
            Protocol.sendCommand(outputStream, cmd, args);
            pipelinedCommands++;
            return this;
          } catch (JedisConnectionException ex) {
            /*
             * When client send request which formed by invalid protocol, Redis send back error message
             * before close connection. We try to read it to provide reason of failure.
             */
            try {
              String errorMessage = Protocol.readErrorLineIfPossible(inputStream);
              if (errorMessage != null && errorMessage.length() > 0) {
                ex = new JedisConnectionException(errorMessage, ex.getCause());
              }
            } catch (Exception e) {
              /*
               * Catch any IOException or JedisConnectionException occurred from InputStream#read and just
               * ignore. This approach is safe because reading error message is optional and connection
               * will eventually be closed.
               */
            }
            // Any other exceptions related to connection?
            broken = true;
            throw ex;
          }
        }
        
        ...
        
        // 和redis server 建立连接,本质上使用socket通信
        public void connect() {
          if (!isConnected()) {
            try {
              socket = new Socket();
              // ->@wjw_add
              socket.setReuseAddress(true);
              socket.setKeepAlive(true); // Will monitor the TCP connection is
              // valid
              socket.setTcpNoDelay(true); // Socket buffer Whetherclosed, to
              // ensure timely delivery of data
              socket.setSoLinger(true, 0); // Control calls close () method,
              // the underlying socket is closed
              // immediately
              // <-@wjw_add
        
              socket.connect(new InetSocketAddress(host, port), connectionTimeout);
              socket.setSoTimeout(soTimeout);
        
              if (ssl) {
                if (null == sslSocketFactory) {
                  sslSocketFactory = (SSLSocketFactory)SSLSocketFactory.getDefault();
                }
                socket = (SSLSocket) sslSocketFactory.createSocket(socket, host, port, true);
                if (null != sslParameters) {
                  ((SSLSocket) socket).setSSLParameters(sslParameters);
                }
                if ((null != hostnameVerifier) &&
                    (!hostnameVerifier.verify(host, ((SSLSocket) socket).getSession()))) {
                  String message = String.format(
                      "The connection to '%s' failed ssl/tls hostname verification.", host);
                  throw new JedisConnectionException(message);
                }
              }
              // 获取socket连接的io流对象
              outputStream = new RedisOutputStream(socket.getOutputStream());
              inputStream = new RedisInputStream(socket.getInputStream());
            } catch (IOException ex) {
              broken = true;
              throw new JedisConnectionException(ex);
            }
          }
        }
        
        ...
        
    }

     

    通过Connection代码可以看到,其调用了 Prototcol 的 sendCommand 方法来发送redis命令和参数。Protocol代码如下。

    public final class Protocol {
    
      private static final String ASK_RESPONSE = "ASK";
      private static final String MOVED_RESPONSE = "MOVED";
      private static final String CLUSTERDOWN_RESPONSE = "CLUSTERDOWN";
      private static final String BUSY_RESPONSE = "BUSY";
      private static final String NOSCRIPT_RESPONSE = "NOSCRIPT";
      
      // 默认主机号和端口号
      public static final String DEFAULT_HOST = "localhost";
      public static final int DEFAULT_PORT = 6379;
      public static final int DEFAULT_SENTINEL_PORT = 26379;
      public static final int DEFAULT_TIMEOUT = 2000;
      public static final int DEFAULT_DATABASE = 0;
      // 支持的编码格式
      public static final String CHARSET = "UTF-8";
    
      // 通用命令常量
      public static final byte DOLLAR_BYTE = '$';
      public static final byte ASTERISK_BYTE = '*';
      public static final byte PLUS_BYTE = '+';
      public static final byte MINUS_BYTE = '-';
      public static final byte COLON_BYTE = ':';
    
      public static final String SENTINEL_MASTERS = "masters";
      public static final String SENTINEL_GET_MASTER_ADDR_BY_NAME = "get-master-addr-by-name";
      public static final String SENTINEL_RESET = "reset";
      public static final String SENTINEL_SLAVES = "slaves";
      public static final String SENTINEL_FAILOVER = "failover";
      public static final String SENTINEL_MONITOR = "monitor";
      public static final String SENTINEL_REMOVE = "remove";
      public static final String SENTINEL_SET = "set";
    
      public static final String CLUSTER_NODES = "nodes";
      public static final String CLUSTER_MEET = "meet";
      public static final String CLUSTER_RESET = "reset";
      public static final String CLUSTER_ADDSLOTS = "addslots";
      public static final String CLUSTER_DELSLOTS = "delslots";
      public static final String CLUSTER_INFO = "info";
      public static final String CLUSTER_GETKEYSINSLOT = "getkeysinslot";
      public static final String CLUSTER_SETSLOT = "setslot";
      public static final String CLUSTER_SETSLOT_NODE = "node";
      public static final String CLUSTER_SETSLOT_MIGRATING = "migrating";
      public static final String CLUSTER_SETSLOT_IMPORTING = "importing";
      public static final String CLUSTER_SETSLOT_STABLE = "stable";
      public static final String CLUSTER_FORGET = "forget";
      public static final String CLUSTER_FLUSHSLOT = "flushslots";
      public static final String CLUSTER_KEYSLOT = "keyslot";
      public static final String CLUSTER_COUNTKEYINSLOT = "countkeysinslot";
      public static final String CLUSTER_SAVECONFIG = "saveconfig";
      public static final String CLUSTER_REPLICATE = "replicate";
      public static final String CLUSTER_SLAVES = "slaves";
      public static final String CLUSTER_FAILOVER = "failover";
      public static final String CLUSTER_SLOTS = "slots";
      public static final String PUBSUB_CHANNELS = "channels";
      public static final String PUBSUB_NUMSUB = "numsub";
      public static final String PUBSUB_NUM_PAT = "numpat";
    
      public static final byte[] BYTES_TRUE = toByteArray(1);
      public static final byte[] BYTES_FALSE = toByteArray(0);
    
      private Protocol() {
        // this prevent the class from instantiation
      }
    
      public static void sendCommand(final RedisOutputStream os, final Command command,
          final byte[]... args) {
        sendCommand(os, command.raw, args);
      }
    
      // 完成redis命令和数据的发送
      private static void sendCommand(final RedisOutputStream os, final byte[] command,
          final byte[]... args) {
        try {
          os.write(ASTERISK_BYTE);
          os.writeIntCrLf(args.length + 1);
          os.write(DOLLAR_BYTE);
          os.writeIntCrLf(command.length);
          os.write(command);
          os.writeCrLf();
    
          for (final byte[] arg : args) {
            os.write(DOLLAR_BYTE);
            os.writeIntCrLf(arg.length);
            os.write(arg);
            os.writeCrLf();
          }
        } catch (IOException e) {
          throw new JedisConnectionException(e);
        }
      }
      
      ...
      
    }

     

    Jedis中通过继承File的IO流程类定义了redis 操作的IO流,包含RedisOutputStream 和 RedisInputStream,用于定义redis的一些定制化操作。

     

    通过上面的分析,举一个例子,比如:

    jedis.set("key", "value") 本质上会给 redis server 发送字节流 *3 $1 nset $3 key $5 value

     

    下面来分析上面过程中的其他细节。

    a. 字符串转字节数组

    字节转数组工具类定义在 redis.clients.util 包下。

    public final class SafeEncoder {
      private SafeEncoder(){
        throw new InstantiationError( "Must not instantiate this class" );
      }
    
      public static byte[][] encodeMany(final String... strs) {
        byte[][] many = new byte[strs.length][];
        for (int i = 0; i < strs.length; i++) {
          many[i] = encode(strs[i]);
        }
        return many;
      }
      
      // 字符串编码为字节数组
      public static byte[] encode(final String str) {
        try {
          if (str == null) {
            throw new JedisDataException("value sent to redis cannot be null");
          }
          return str.getBytes(Protocol.CHARSET);
        } catch (UnsupportedEncodingException e) {
          throw new JedisException(e);
        }
      }
    
      public static String encode(final byte[] data) {
        try {
          return new String(data, Protocol.CHARSET);
        } catch (UnsupportedEncodingException e) {
          throw new JedisException(e);
        }
      }
    }

     

    从上面可以看到,通过调用JDK中的 getBytes 方法获取字节数组,同时指定编码格式UTF-8。

    b. 命令定义

    Jedis用到的所有redis命令和关键字都以枚举形式定义在Protocol类中,代码如下:

    public final class Protocol {
        
        ...
        // redis命令枚举
        public static enum Command {
          PING, SET, GET, QUIT, EXISTS, DEL, TYPE, FLUSHDB, KEYS, RANDOMKEY, RENAME, RENAMENX, RENAMEX, DBSIZE, EXPIRE, EXPIREAT, TTL, SELECT, MOVE, FLUSHALL, GETSET, MGET, SETNX, SETEX, MSET, MSETNX, DECRBY, DECR, INCRBY, INCR, APPEND, SUBSTR, HSET, HGET, HSETNX, HMSET, HMGET, HINCRBY, HEXISTS, HDEL, HLEN, HKEYS, HVALS, HGETALL, RPUSH, LPUSH, LLEN, LRANGE, LTRIM, LINDEX, LSET, LREM, LPOP, RPOP, RPOPLPUSH, SADD, SMEMBERS, SREM, SPOP, SMOVE, SCARD, SISMEMBER, SINTER, SINTERSTORE, SUNION, SUNIONSTORE, SDIFF, SDIFFSTORE, SRANDMEMBER, ZADD, ZRANGE, ZREM, ZINCRBY, ZRANK, ZREVRANK, ZREVRANGE, ZCARD, ZSCORE, MULTI, DISCARD, EXEC, WATCH, UNWATCH, SORT, BLPOP, BRPOP, AUTH, SUBSCRIBE, PUBLISH, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, PUBSUB, ZCOUNT, ZRANGEBYSCORE, ZREVRANGEBYSCORE, ZREMRANGEBYRANK, ZREMRANGEBYSCORE, ZUNIONSTORE, ZINTERSTORE, ZLEXCOUNT, ZRANGEBYLEX, ZREVRANGEBYLEX, ZREMRANGEBYLEX, SAVE, BGSAVE, BGREWRITEAOF, LASTSAVE, SHUTDOWN, INFO, MONITOR, SLAVEOF, CONFIG, STRLEN, SYNC, LPUSHX, PERSIST, RPUSHX, ECHO, LINSERT, DEBUG, BRPOPLPUSH, SETBIT, GETBIT, BITPOS, SETRANGE, GETRANGE, EVAL, EVALSHA, SCRIPT, SLOWLOG, OBJECT, BITCOUNT, BITOP, SENTINEL, DUMP, RESTORE, PEXPIRE, PEXPIREAT, PTTL, INCRBYFLOAT, PSETEX, CLIENT, TIME, MIGRATE, HINCRBYFLOAT, SCAN, HSCAN, SSCAN, ZSCAN, WAIT, CLUSTER, ASKING, PFADD, PFCOUNT, PFMERGE, READONLY, GEOADD, GEODIST, GEOHASH, GEOPOS, GEORADIUS, GEORADIUSBYMEMBER, BITFIELD;
        
          public final byte[] raw;
        
          Command() {
            raw = SafeEncoder.encode(this.name());
          }
    
        }
    
        // redis 关键字
        public static enum Keyword {
          AGGREGATE, ALPHA, ASC, BY, DESC, GET, LIMIT, MESSAGE, NO, NOSORT, PMESSAGE, PSUBSCRIBE, PUNSUBSCRIBE, OK, ONE, QUEUED, SET, STORE, SUBSCRIBE, UNSUBSCRIBE, WEIGHTS, WITHSCORES, RESETSTAT, RESET, FLUSH, EXISTS, LOAD, KILL, LEN, REFCOUNT, ENCODING, IDLETIME, AND, OR, XOR, NOT, GETNAME, SETNAME, LIST, MATCH, COUNT, PING, PONG;
          public final byte[] raw;
        
          Keyword() {
            raw = SafeEncoder.encode(this.name().toLowerCase(Locale.ENGLISH));
          }
        }
    }

    2.2. JedisPool

    在第1部分中已经提到,Jedis依赖apache 的 commons-pool2 的jar包。jedis主要使用commons-pool2 来实现jedis连接池。下面分析一下jedis如何使用 commons-pool2 实现一个jedis连接池。下图3.1为连接池实现类图。

     

    clipboard

    图3.1 JedisPool 实现类图

    JedisPool实现了Pool类,Pool类是jedis中定义的一个工具类,该类中有类型为GenericObjectPool的成员变量,GenericObjectPool是apache commons-pool2中连接池的一种实现。GenericObjectPool构造器有两个入参,分别是连接池配置类 BaseObjectPoolConfig 和PooledObjectFactory 池中对象生成工厂类。Jedis针对apache commons-pool2 中的PooledObjectFactory实现了jedis的池内对象生成工厂类。池内对象生成工厂类生成的对象类型是PooledObject,其有实现类DefaultPooledObject。

    对于GenericObjectPool的实现细节,后续写一篇解析一下。

    再来看一下 GenericObjectPoolConfig 代码中的默认配置。

    public class GenericObjectPoolConfig extends BaseObjectPoolConfig {
    
        ...
    
        /**
         * The default value for the {@code maxTotal} configuration attribute.
         * @see GenericObjectPool#getMaxTotal()
         */
         // 默认最大池大小
        public static final int DEFAULT_MAX_TOTAL = 8;
    
        /**
         * The default value for the {@code maxIdle} configuration attribute.
         * @see GenericObjectPool#getMaxIdle()
         */
         // 默认池中最大空闲对象数
        public static final int DEFAULT_MAX_IDLE = 8;
    
        /**
         * The default value for the {@code minIdle} configuration attribute.
         * @see GenericObjectPool#getMinIdle()
         */
         // 默认池中最小空闲对象数
        public static final int DEFAULT_MIN_IDLE = 0;
    
        private int maxTotal = DEFAULT_MAX_TOTAL;
    
        private int maxIdle = DEFAULT_MAX_IDLE;
    
        private int minIdle = DEFAULT_MIN_IDLE;
        
        ...
        
    }

    3、 总结

    通过上面的源码解析,基本了解了Jedis的工作原理。Jedis作为使用Java语言实现的Redis客户端,原理是通过socket 和 redis server通信,传输redis命令和参数。同时Jedis借助apache commons-pool2 实现了redis连接池。

  • 相关阅读:
    HDU 6143 Killer Names【dp递推】【好题】【思维题】【阅读题】
    HDU 6143 Killer Names【dp递推】【好题】【思维题】【阅读题】
    POJ 3974 Palindrome【manacher】【模板题】【模板】
    POJ 3974 Palindrome【manacher】【模板题】【模板】
    HDU 6127 Hard challenge【计算机几何】【思维题】
    HDU 6127 Hard challenge【计算机几何】【思维题】
    HDU 6129 Just do it【杨辉三角】【思维题】【好题】
    HDU 6129 Just do it【杨辉三角】【思维题】【好题】
    HDU 3037 Saving Beans【Lucas定理】【模板题】【模板】【组合数取余】
    8.Math 对象
  • 原文地址:https://www.cnblogs.com/glsy/p/11815778.html
Copyright © 2011-2022 走看看