原理: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必須在處理完全部命令前先緩存起全部命令的處理結果。打包的命令越多,緩存消耗內存也越多。因此並是否是打包的命令越多越好。具體多少合適須要根據具體狀況測試。服務器