原理:redis
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必须在处理完全部命令前先缓存起全部命令的处理结果。打包的命令越多,缓存消耗内存也越多。因此并是否是打包的命令越多越好。具体多少合适须要根据具体状况测试。服务器