Redis是最常見的緩存服務中間件,在java開發中,一般使用 jedis 來實現。
如果不想依賴第三方組件,自己實現一個簡單的redis客戶端工具,該如何實現呢?本文就是介紹這樣一種方法。
Redis的協議非常簡單,而且輸入數據和輸出數據都遵循統一的協議,具體規則參考這里:
http://redisdoc.com/topic/protocol.html
Redis的命令協議:
$參數數量n
$參數1的值的字節數組長度
$參數1的值的字符串表示
$參數2的值的字節數組長度
$參數2的值的字符串表示
...
$參數n的值的字節數組長度
$參數n的值的字符串表示
Redis的返回協議:
1、狀態回復(status reply)的第一個字節是 "+",單行字符串;
2、錯誤回復(error reply)的第一個字節是 "-";
3、整數回復(integer reply)的第一個字節是 ":";
4、批量回復(bulk reply)的第一個字節是 "$";
5、多條批量回復(multi bulk reply)的第一個字節是 "*";
6、所有的命令都是以 \r\n 結尾。
Java代碼說明
針對上述規則,我們用兩個類來實現:
1、SimpleRedisClient類,主要用于發送請求,并讀取響應結果(字符串);
整體比較簡單,稍微復雜點的地方就是讀取流數據,遇到兩種情況就該結束循環,一是返回長度為-1,二是返回字符串以 \r\n 結尾。
如果處理不當,可能會導致 read 阻塞,Socket卡住。
2、SimpleRedisData類,用于解析響應結果,把redis統一協議的字符串,解析為具體的對象。
這部分代碼完全是按照協議規則來實現的,通過一個游標 pos 來向前移動,在移動過程中識別不同格式的數據。
最復雜的是 list 類型的數據,以 * 開頭,后面跟著一個整數,表示列表中所有元素的數量,然后就是每一個列表元素的值,循環解析即可。
package demo;
import java.io.Closeable;
import java.io.IOException;
import java.net.Socket;
import java.util.List;
public class SimpleRedisClient implements Closeable {
private String host;
private int port;
private String auth;
private Socket socket = null;
public SimpleRedisClient(String host, int port, String auth) {
this.host = host;
this.port = port;
this.auth = auth;
try {
socket = new Socket(this.host, this.port);
socket.setSoTimeout(8 * 1000);//8秒
} catch (Exception ex) {
socket = null;
ex.printStackTrace();
}
}
public boolean connect() throws IOException {
if (socket == null || auth == null || auth.length() = 0) {
return false;
}
String response = execute("AUTH", auth);
if (response == null || response.length() = 0) {
return false;
}
String res = new SimpleRedisData(response).getString();
return "OK".compareTo(res) == 0;
}
@Override
public void close() {
try {
if (socket != null) {
socket.shutdownOutput();
socket.close();
}
//System.out.println("closed");
} catch (Exception ex) {
ex.printStackTrace();
}
}
public String getString(String key) {
if (socket == null || key == null || key.isEmpty()) {
return null;
}
try {
String response = execute("GET", key);
return new SimpleRedisData(response).getString();
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
public String setString(String key, String value) {
if (socket == null || key == null || key.isEmpty()) {
return null;
}
try {
String response = execute("SET", key, value);
return new SimpleRedisData(response).getString();
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
public String deleteKey(String key) throws IOException {
if (socket == null || key == null || key.isEmpty()) {
return null;
}
String response = execute("DEL", key);
return new SimpleRedisData(response).getString();
}
public ListString> getKeys(String pattern) throws IOException {
if (socket == null || pattern == null || pattern.isEmpty()) {
return null;
}
String response = execute("KEYS", pattern);
return new SimpleRedisData(response).getStringList();
}
public String execute(String... args) throws IOException {
if (socket == null || args == null || args.length = 0) {
return null;
}
//System.out.println(StringUtil.join(args, " "));
StringBuilder request = new StringBuilder();
request.append("*" + args.length).append("\r\n");//參數的數量
for (int i = 0; i args.length; i++) {
request.append("$" + args[i].getBytes("utf8").length).append("\r\n");//參數的長度
request.append(args[i]).append("\r\n");//參數的內容
}
socket.getOutputStream().write(request.toString().getBytes());
socket.getOutputStream().flush();
StringBuilder reply = new StringBuilder();
int bufSize = 1024;
while (true) {
byte[] buf = new byte[bufSize];
int len = socket.getInputStream().read(buf);
if (len 0) {
break;
}
String str = new String(buf, 0, len);
reply.append(str);
if (str.endsWith("\r\n")) {
break;
}
}
String response = reply.toString();
//System.out.println("response: " + response);
return response;
}
}
package demo;
import java.util.ArrayList;
import java.util.List;
public class SimpleRedisData {
public SimpleRedisData(String rawData) {
this.rawData = rawData;
//System.out.println(rawData);
}
private int pos;
private String rawData;
public String getString() {
if (rawData == null || rawData.length() = 0) {
return null;
}
int i = rawData.indexOf("\r\n", pos);
if (i = 0) {
return null;
}
char c = rawData.charAt(pos);
if (c == '+') {
int from = pos + 1;
int to = i;
String v = rawData.substring(from, to);
pos = to + 2;
return v;
} else if (c == '-') {
int from = pos + 1;
int to = i;
String v = rawData.substring(from, to);
pos = to + 2;
return v;
} else if (c == ':') {
int from = pos + 1;
int to = i;
String v = rawData.substring(from, to);
pos = to + 2;
return v;
} else if (c == '$') {
int from = pos + 1;
int to = i;
int bulkSize = Integer.parseInt(rawData.substring(from, to));
pos = to + 2;
from = pos;
to = pos + bulkSize;
try {
//$符號后面的數值是指內容的字節長度,而不是字符數量,所以要轉換為二進制字節數組,再取指定長度的數據
byte[] buf = rawData.substring(from).getBytes("utf-8");
String v = new String(buf, 0, bulkSize);
pos = to + 2;
return v;
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
} else {
return null;
}
}
public ListString> getStringList() {
if (rawData == null || rawData.length() = 0) {
return null;
}
int i = rawData.indexOf("\r\n", pos);
if (i = 0) {
return null;
}
char c = rawData.charAt(pos);
if (c == '*') {
ListString> values = new ArrayList>();
int from = pos + 1;
int to = i;
int multSize = Integer.parseInt(rawData.substring(from, to));
pos = to + 2;
for (int index = 0; index multSize; index++) {
values.add(getString());
}
return values;
} else {
return null;
}
}
}
package demo;
import org.junit.jupiter.api.Test;
import java.util.List;
public class RedisTest {
@Test
public void test() {
SimpleRedisClient client = null;
try {
client = new SimpleRedisClient("127.0.0.1", 6379, "123456");
System.out.println("connected: " + client.connect());
ListString> keyList = client.getKeys("api_*");
for (int i = 0; i keyList.size(); i++) {
System.out.println((i + 1) + "\t" + keyList.get(i));
}
System.out.println("keys: " + keyList != null ? keyList.size() : "null");
System.out.println(client.getString("api_getCustomerName"));
} catch (Exception ex) {
ex.printStackTrace();
} finally {
if (client != null) {
client.close();
}
}
}
}
優點:
1、不依賴任何第三方組件,可以順利編譯通過;
2、代碼極其簡單。
不足之處:
1、未考慮并發訪問;
2、未提供更多的數據類型,以及讀寫方法,大家可以在此基礎上包裝一下。
以上就是如何用Java Socket實現一個簡單的Redis客戶端的詳細內容,更多關于Java Socket Redis客戶端的資料請關注腳本之家其它相關文章!
您可能感興趣的文章:- Java Socket+多線程實現多人聊天室功能
- Java Socket實現多人聊天系統
- Java Socket模擬實現聊天室
- java Nio使用NioSocket客戶端與服務端交互實現方式
- 淺談java socket的正確關閉姿勢
- 解決java.net.SocketTimeoutException: Read timed out的問題
- 詳解Java Socket通信封裝MIna框架
- Java使用Socket簡單通訊詳解