Jedis源码浅析
1、概述
Jedis是redis官网推荐的redis java client,代码维护在github https://github.com/xetorthio/jedis。
本质上Jedis帮我们封装了各种redis命令,提供了各种和redis命令相关的方法使用。Jedis的基本结构如下图1.1所示。
图1.1 Jedis 工作过程
可以看到Jedis通过socket和redis server通信,通过发送redis命令和参数,接收redis server的结果,从而实现redis client。
2、源码解析
按照Jedis源码分为两个层次分析,分别是:以Jedis为核心和以JedisPool为核心。
先看一下Jedis jar包的层次结构,如下图2.1所示,下面都是以 jedis-2.9.0 为例。
图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所示。
图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\r\n$1\r\rnset\r\n$3\n\rkey\r\n$5\n\rvalue\r\n
下面来分析上面过程中的其他细节。
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为连接池实现类图。
图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连接池。