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

Jedis源码浅析的更多相关文章

  1. 【深入浅出jQuery】源码浅析--整体架构

    最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...

  2. 【深入浅出jQuery】源码浅析2--奇技淫巧

    最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...

  3. Struts2源码浅析-ConfigurationProvider

    ConfigurationProvider接口 主要完成struts配置文件 加载 注册过程 ConfigurationProvider接口定义 public interface Configurat ...

  4. (转)【深入浅出jQuery】源码浅析2--奇技淫巧

    [深入浅出jQuery]源码浅析2--奇技淫巧 http://www.cnblogs.com/coco1s/p/5303041.html

  5. HashSet其实就那么一回事儿之源码浅析

    上篇文章<HashMap其实就那么一回事儿之源码浅析>介绍了hashMap,  本次将带大家看看HashSet, HashSet其实就是基于HashMap实现, 因此,熟悉了HashMap ...

  6. Android 手势识别类 ( 三 ) GestureDetector 源码浅析

    前言:上 篇介绍了提供手势绘制的视图平台GestureOverlayView,但是在视图平台上绘制出的手势,是需要存储以及在必要的利用时加载取出手势.所 以,用户绘制出的一个完整的手势是需要一定的代码 ...

  7. Android开发之Theme、Style探索及源码浅析

    1 背景 前段时间群里有伙伴问到了关于Android开发中Theme与Style的问题,当然,这类东西在网上随便一搜一大把模板,所以关于怎么用的问题我想这里也就不做太多的说明了,我们这里把重点放在理解 ...

  8. 【深入浅出jQuery】源码浅析2--使用技巧

    最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...

  9. Android手势源码浅析-----手势绘制(GestureOverlayView)

    Android手势源码浅析-----手势绘制(GestureOverlayView)

随机推荐

  1. Xshell6,亲测可用~破解版简单解压免安装~已更新官方版本安装方法

    下面的内容别看了,使用这个最新的安装官方版本 https://www.cnblogs.com/taopanfeng/p/11671727.html 下面的内容别看了,使用这个最新的安装官方版本 htt ...

  2. Ubuntu分区方案(菜鸟方案、常用方案和进阶方案)

    菜鸟方案 “/”与swap两个分区就可以应付绝大多数的应用 常用方案 分为3个区 1. 挂载点/:主分区:安装系统和软件:大小为30G:分区格式为ext4: 2. 挂载点/home:逻辑分区:相当于“ ...

  3. 数据库备份及SQL脚本导入

    数据库备份及SQL脚本导入 数据导出 su - oracle exp 数据库用户名/数据库密码@ORCL file=20190905.dmp full=y SQL脚本导入 首先导入前查看Oracle用 ...

  4. flex布局总结回顾

    1.背景 传统css盒式模型,依赖 display属性 + position属性 + float属性实现页面的布局,而随着互联网的迅猛发展,带动了无数的互联网创业者和互联网产品,因而样式布局的美化成为 ...

  5. 洛谷P2401 不等数列 题解

    可食用的题目链接 题解: 有题目得:这个题有巧做法而不是暴力模拟.废话 这个题看着像一道dp,因为可以由前一种(数据更小的推出数据更大的)推出后一种. 我们设已经得到了n-1个数的总方法(1~n-1) ...

  6. java高并发核心要点|系列4|CPU内存指令重排序(Memory Reordering)

    今天,我们来学习另一个重要的概念. CPU内存指令重排序(Memory Reordering) 什么叫重排序? 重排序的背景 我们知道现代CPU的主频越来越高,与cache的交互次数也越来越多.当CP ...

  7. 解读sam格式文件

    1,SAM文件格式介绍 SAM(The Sequence Alignment / Map format)格式,即序列比对文件的格式,详细介绍文档:http://samtools.github.io/h ...

  8. 动态列表+动态样式(vue双向绑定)

    先上效果图 注:下面的几个值可以从其他地方获取,这边演示我是写死的 在上逻辑图 接着上代码template部分 <template> <div > <div> &l ...

  9. JVM垃圾回收之CMS收集器

    从前文JVM垃圾回收几种常见算法和常见收集器我们知道,CMS是老年代垃圾收集器.CMS 收集器主要关注系统停顿时间.CMS 是 Concurrent Mark Sweep 的缩写,意为并发标记清除,从 ...

  10. python 后台 安装 富文本编辑

    前言 当然需要安装一些后台只能输入一些文本编辑器,不然这样多少不美观呀 当然python 有 safe 可以把后台的标签转换 , 还有 striptags   这个是换成html 格式的,但不会加粗或 ...