jedis指令

Posted on 2018-03-05 20:03  林浩开发小屋  阅读(804)  评论(0)    收藏  举报

原理:

 

jedis底层主要有两个类: 
redis.clients.jedis.Protocol 
redis.clients.jedis.Connection 
Connection负责client与server之间通信,Protocol是client与server之间通信协议。

  1 public class Connection implements Closeable {
  2 private static final byte[][] EMPTY_ARGS = new byte[0][];
  3     private String host = "localhost"; //redis服务器地址(默认"localhost")
  4     private int port = 6379;//port:服务端号(默认6379)
  5     private Socket socket;
  6     private RedisOutputStream outputStream;//redis-client发送给redis-server的内容
  7     private RedisInputStream inputStream;//redis-server返回给redis-client的内容
  8     private int pipelinedCommands = 0;//管道命令数
  9     private int connectionTimeout = 2000;//连接超时时间(默认2000ms)
 10     private int soTimeout = 2000;//响应超时时间(默认2000ms)
 11     private boolean broken = false;
 12 
 13 ...
 14 
 15 /**主要方法*/
 16 
 17 //连接
 18  public void connect() {
 19         if(!this.isConnected()) {
 20             try {
 21                 this.socket = new Socket();
 22                 this.socket.setReuseAddress(true);
 23                 this.socket.setKeepAlive(true);
 24                 this.socket.setTcpNoDelay(true);
 25                 this.socket.setSoLinger(true, 0);
 26                 this.socket.connect(new InetSocketAddress(this.host, this.port), this.connectionTimeout);
 27                 this.socket.setSoTimeout(this.soTimeout);
 28                 this.outputStream = new RedisOutputStream(this.socket.getOutputStream());
 29                 this.inputStream = new RedisInputStream(this.socket.getInputStream());
 30             } catch (IOException var2) {
 31                 this.broken = true;
 32                 throw new JedisConnectionException(var2);
 33             }
 34         }
 35 
 36     }
 37 
 38 //发送命令内容
 39 protected Connection sendCommand(Command cmd, byte[]... args) {
 40         try {
 41             this.connect();
 42             Protocol.sendCommand(this.outputStream, cmd, args);
 43             ++this.pipelinedCommands;
 44             return this;
 45         } catch (JedisConnectionException var6) {
 46             JedisConnectionException ex = var6;
 47 
 48             try {
 49                 String errorMessage = Protocol.readErrorLineIfPossible(this.inputStream);
 50                 if(errorMessage != null && errorMessage.length() > 0) {
 51                     ex = new JedisConnectionException(errorMessage, ex.getCause());
 52                 }
 53             } catch (Exception var5) {
 54                 ;
 55             }
 56 
 57             this.broken = true;
 58             throw ex;
 59         }
 60     }
 61 }    
 62 //协议
 63 public final class Protocol {
 64 
 65 //命令的发送都是通过redis.clients.jedis.Protocol的sendCommand来完成的,就是对RedisOutputStream写入字节流 
 66 /**
 67 *[*号][消息元素个数]\r\n ( 消息元素个数 = 参数个数 + 1个命令)
 68 *[$号][命令字节个数]\r\n
 69 *[命令内容]\r\n
 70 *[$号][参数字节个数]\r\n
 71 *[参数内容]\r\n
 72 *[$号][参数字节个数]\r\n
 73 *[参数内容]\r\n 
 74 */
 75 private static void sendCommand(RedisOutputStream os, byte[] command, byte[]... args) {
 76         try {
 77             os.write((byte)42);
 78             os.writeIntCrLf(args.length + 1);
 79             os.write((byte)36);
 80             os.writeIntCrLf(command.length);
 81             os.write(command);
 82             os.writeCrLf();
 83             byte[][] e = args;
 84             int var4 = args.length;
 85 
 86             for(int var5 = 0; var5 < var4; ++var5) {
 87                 byte[] arg = e[var5];
 88                 os.write((byte)36);
 89                 os.writeIntCrLf(arg.length);
 90                 os.write(arg);
 91                 os.writeCrLf();
 92             }
 93 
 94         } catch (IOException var7) {
 95             throw new JedisConnectionException(var7);
 96         }
 97     }
 98 }
 99 
100 //返回的数据是通过读取RedisInputStream  进行解析处理后得到的
101   /** 
102    * public static final byte PLUS_BYTE = 43;
103    * public static final byte DOLLAR_BYTE = 36;
104    * public static final byte ASTERISK_BYTE = 42;
105    * public static final byte COLON_BYTE = 58;
106 
107    * "+": 状态回复(status reply)         PLUS_BYTE 
108    *    * <pre> 
109    * 状态回复通常由那些不需要返回数据的命令返回,这种回复不能包含新行。 
110    * eg: 
111    *    cli: set name zhangsan 
112    *    server: +OK 
113    * </pre> 
114    * 
115    * "$": 批量回复(bulk reply)           DOLLAR_BYTE 
116    *   服务器使用批量回复来返回二进制安全的字符串,字符串的最大长度为 512 MB。 
117    *  eg: 
118    *      cli: get name 
119    *      server: $8\r\nzhangsan\r\n 
120    *  空批量回复: 
121    *  如果被请求的值不存在, 那么批量回复会将特殊值 -1 用作回复的长度值。当请求对象不存在时,客户端应该返回空对象,而不是空字符串。 
122    * 
123    * "*": 多条批量回复(multi bulk reply)  ASTERISK_BYTE  
124    *    * 多条批量回复是由多个回复组成的数组, 数组中的每个元素都可以是任意类型的回复, 包括多条批量回复本身。 
125    * eg: 
126    *    cli: lrange mylist 0 3 
127    *    server: *4\r\n 
128    *            :1\r\n 
129    *            :2\r\n 
130    *            :3\r\n 
131    *            $3\r\n 
132    *            foo\r\n 
133    * 多条批量回复也可以是空白的, 
134    * eg: 
135    *    cli: lrange mylist 7 8 
136    *    server: *0\r\n 
137    * 无内容的多条批量回复(null multi bulk reply)也是存在的, 比如当 BLPOP 命令的阻塞时间超过最大时限时, 它就返回一个无内容的多条批量回复, 这个回复的计数值为 -1 : 
138    * eg: 
139    *    cli: blpop key 1 
140    *    server: *-1\r\n 
141    * 多条批量回复中的元素可以将自身的长度设置为 -1 , 从而表示该元素不存在, 并且也不是一个空白字符串(empty string)。 
142    * 
143    * ":": 整数回复(integer reply)        COLON_BYTE
144    *    *  整数回复就是一个以 ":" 开头, CRLF 结尾的字符串表示的整数。 
145    *    eg: 
146    *    cli: exists name 
147    *    server: :1 
148    * 
149    * "-": 错误回复(error reply)          MINUS_BYTE
150    */ 
151 private static Object process(RedisInputStream is) {
152         byte b = is.readByte();
153         if(b == 43) {
154             return processStatusCodeReply(is);
155         } else if(b == 36) {
156             return processBulkReply(is);
157         } else if(b == 42) {
158             return processMultiBulkReply(is);
159         } else if(b == 58) {
160             return processInteger(is);
161         } else if(b == 45) {
162             processError(is);
163             return null;
164         } else {
165             throw new JedisConnectionException("Unknown reply: " + (char)b);
166         }
167     }
168 
169 }

以Jedis的get方法为例:

 

  /**
   * Get the value of the specified key. If the key does not exist null is returned. If the value
   * stored at key is not a string an error is returned because GET can only handle string values.
   * <p>
   * Time complexity: O(1)
   * @param key
   * @return Bulk reply
   */
  public String get(final String key) {
    checkIsInMultiOrPipeline();
    client.sendCommand(Protocol.Command.GET, key);
    return client.getBulkReply();
  }

1:checkIsInMultiOrPipeline(); 
进行无事务检查 Jedis不能进行有事务的操作 带事务的连接要用redis.clients.jedis.Transaction类。 
2:client.sendCommand(Protocol.Command.GET, key);
2.1:redis.clients.jedis.Connection connect()方法建立连接 
2.2:public final class Protocol sendCommand()方法向RedisOutputStream写入命令 
2.3:在命令写入成功之后,会将Connection的pipelinedCommands属性自增一,表示在管道中已经有一个命令了 
3:return this.client.getBulkReply(); 
get方法使用getBulkReply()获取返回结果,其他见上文redis.clients.jedis.Protocol process()方法

 

  • pipeline

redis是一个cs模式的tcp server,使用和http类似的请求响应协议。一个client可以通过一个socket连接发起多个请求命令。每个请求命令发出后client通常会阻塞并等待redis服务处理,redis处理完后请求命令后会将结果通过响应报文返回给client。 
所以在多条命令需要处理时,使用pipeline效率会快得多。 
通过pipeline方式当有大批量的操作时候。我们可以节省很多原来浪费在网络延迟的时间。pipeline方式将client端命令一起发出,redis server会处理完多条命令后,将结果一起打包返回client,从而节省大量的网络延迟开销。需要注意到是用 pipeline方式打包命令发送,redis必须在处理完所有命令前先缓存起所有命令的处理结果。打包的命令越多,缓存消耗内存也越多。所以并是不是打包的命令越多越好。具体多少合适需要根据具体情况测试。

博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3