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连接池。

  • 相关阅读:
    hive基本操作与应用
    理解MapReduce计算构架
    熟悉HBase基本操作
    熟悉常用的HDFS操作
    爬虫大作业
    数据结构化与保存
    使用正则表达式,取得点击次数,函数抽离
    爬取校园新闻首页的新闻
    网络爬虫基础练习
    Hadoop综合大作业
  • 原文地址:https://www.cnblogs.com/glsy/p/11815778.html
Copyright © 2011-2022 走看看