http://donald-draper.iteye.com/blog/2347121
Redis Protocol specification:https://redis.io/topics/protocol
redis协议:http://doc.redisfans.com/topic/protocol.html
上一篇中,我们探究了一下Jedis获取Redis连接过程,具体如下:
JedisPoolConfig的功能主要是配置连接最大空闲时间,存活数量,及等待时间;
JedisPoolConfig的父类Config为GenericObjectPool的静态内部类,与连接池有关的属性在Config中,而属性的设置在JedisPoolConfig中;JedisPool的初始化主要是GenericObjectPool初始化,主要是初始化连接池,连接数,空闲时间,等待时间,连接池,候选连接池,初始化候选连接初始化执行器,JedisFactory。JedisFactory工厂为JedisPool的内部类,JedisFactory的属性有host,port,timeout,password和database;JedisFactory的主要功能为管理(创建,关闭,验证)redis连接jedis。从连接池获取jedis连接资源,实际上看是从JedisPool的父类pool中获取,而pool又委托给JedisFactory,最后由JedisFactory创建redis连接jedis。
今天我们来看一下,jedis客户端如何操作redis服务器:
public class Jedis extends BinaryJedis
implements JedisCommands
{
//这个在前面JedisFactory创建jedis客户端时,所用的构造方法
public Jedis(String host, int port, int timeout)
{
super(host, port, timeout);
}
}
来看其父类BinaryJedis
//BinaryJedis
public class BinaryJedis
implements BinaryJedisCommands
{
protected Client client;//redis连接客户端
public BinaryJedis(String host, int port, int timeout)
{
client = null;
client = new Client(host, port);
client.setTimeout(timeout);
}
}
//再来看Client的构造
public class Client extends BinaryClient
implements Commands
{
public Client(String host, int port)
{
super(host, port);
}
}
再看BinaryClient
public class BinaryClient extends Connection
{
private boolean isInMulti;//是否是事务
private String password;//密码
private long db;//数据库
public BinaryClient(String host, int port)
{
super(host, port);
}
}
再来看Connection
public class Connection
{
private String host;//ip
private int port;//端口
private Socket socket;//与redis连接socket
private RedisOutputStream outputStream;//输出流
private RedisInputStream inputStream;//输入流
private int pipelinedCommands;//管道命令数
private int timeout;//超时时间
public Connection(String host, int port)
{
this.port = 6379;
pipelinedCommands = 0;
timeout = 2000;
this.host = host;
this.port = port;
}
}
从JedisFactory创建redis连接jedis的构造方法,来看Jedis构造所做的事情为,初始化
BinaryJedis,即初始化Client的host和port,BinaryJedis有个Client,;Client初始化,其实
是初始化BinaryClient,即初始化Connection,Connection为实际与redis通信的连接,
BinaryClient有连三个属性分别为 isInMulti(是否是事务)password,db(数据库),
Connection有几个内部变量分别为host,port,socket,outputStream,inputStream,pipelinedCommands,timeout
下面再来看Jedis的另一种构造方式
public Jedis(JedisShardInfo shardInfo)
{
super(shardInfo);
}
//JedisShardInfo
public class JedisShardInfo extends ShardInfo
{
private int timeout;
private String host;
private int port;
private String password;
private String name;
public JedisShardInfo(String host, int port, int timeout)
{
this(host, port, timeout, 1);
}
public Jedis createResource()
{
return new Jedis(this);
}
//创建jedis客户端
public volatile Object createResource()
{
return createResource();
}
}
//ShardInfo
public abstract class ShardInfo
{
public ShardInfo()
{
}
public ShardInfo(int weight)
{
this.weight = weight;
}
public int getWeight()
{
return weight;
}
protected abstract Object createResource();
public abstract String getName();
private int weight;
}
从JedisShardInfo可以看出,JedisShardInfo创建jedis客户端,实际上为Jedis,及JedisShardInfo依托于Jedis。
下面看一jedis的验证
jedis.auth("redis");
//Jedis
public String auth(String password)
{
//检查是否是事务
checkIsInMulti();
//验证密码
client.auth(password);
//返回redis恢复字符串
return client.getStatusCodeReply();
}
分3步来看:
1.检查是否是事务
checkIsInMulti();
//Jedis
protected void checkIsInMulti()
{
if(client.isInMulti())
throw new JedisDataException("Cannot use Jedis when in Multi. Please use JedisTransaction instead.");
else
return;
}
//Client
public boolean isInMulti()
{
return isInMulti;
}
2.验证密码
client.auth(password);
//Client
public void auth(String password)
{
//设置密码
setPassword(password);
//发送命令
sendCommand(Protocol.Command.AUTH, new String[] {
password
});
}
//设置密码
public void setPassword(String password)
{
this.password = password;
}
//发送命令
protected transient Connection sendCommand(Protocol.Command cmd, String args[])
{
//将发送内容,转换编码字节
byte bargs[][] = new byte[args.length][];
for(int i = 0; i < args.length; i++)
bargs[i] = SafeEncoder.encode(args[i]);
//发送字节流
return sendCommand(cmd, bargs);
}
编码字符
//SafeEncoder
public static byte[] encode(String str)
{
if(str == null)
throw new JedisDataException("value sent to redis cannot be null");
return str.getBytes("UTF-8");
}
发送字节流
protected transient Connection sendCommand(Protocol.Command cmd, byte args[][])
{
//连接redis
connect();
//协议发送命令
Protocol.sendCommand(outputStream, cmd, args);
pipelinedCommands++;
return this;
}
创建连接redis Socket
connect();
//Connection
public void connect()
{
if(!isConnected())
try
{
//如果没有redis建立连接则,则创建socket,并初始化RedisOutputStream,RedisInputStream
socket = new Socket();
socket.setReuseAddress(true);
socket.setKeepAlive(true);
socket.setTcpNoDelay(true);