您好,登錄后才能下訂單哦!
小編給大家分享一下Java中Socket如何實(shí)現(xiàn)Redis客戶端,希望大家閱讀完這篇文章之后都有所收獲,下面讓我們一起去探討吧!
Redis是最常見的緩存服務(wù)中間件,在java開發(fā)中,一般使用 jedis 來實(shí)現(xiàn)。
$參數(shù)數(shù)量n
$參數(shù)1的值的字節(jié)數(shù)組長度
$參數(shù)1的值的字符串表示
$參數(shù)2的值的字節(jié)數(shù)組長度
$參數(shù)2的值的字符串表示
...
$參數(shù)n的值的字節(jié)數(shù)組長度
$參數(shù)n的值的字符串表示
1、狀態(tài)回復(fù)(status reply)的第一個(gè)字節(jié)是 "+",單行字符串;
2、錯(cuò)誤回復(fù)(error reply)的第一個(gè)字節(jié)是 "-";
3、整數(shù)回復(fù)(integer reply)的第一個(gè)字節(jié)是 ":";
4、批量回復(fù)(bulk reply)的第一個(gè)字節(jié)是 "$";
5、多條批量回復(fù)(multi bulk reply)的第一個(gè)字節(jié)是 "*";
6、所有的命令都是以 \r\n 結(jié)尾。
針對上述規(guī)則,我們用兩個(gè)類來實(shí)現(xiàn):
1、SimpleRedisClient類,主要用于發(fā)送請求,并讀取響應(yīng)結(jié)果(字符串);
整體比較簡單,稍微復(fù)雜點(diǎn)的地方就是讀取流數(shù)據(jù),遇到兩種情況就該結(jié)束循環(huán),一是返回長度為-1,二是返回字符串以 \r\n 結(jié)尾。
如果處理不當(dāng),可能會導(dǎo)致 read 阻塞,Socket卡住。
2、SimpleRedisData類,用于解析響應(yīng)結(jié)果,把redis統(tǒng)一協(xié)議的字符串,解析為具體的對象。
這部分代碼完全是按照協(xié)議規(guī)則來實(shí)現(xiàn)的,通過一個(gè)游標(biāo) pos 來向前移動(dòng),在移動(dòng)過程中識別不同格式的數(shù)據(jù)。
最復(fù)雜的是 list 類型的數(shù)據(jù),以 * 開頭,后面跟著一個(gè)整數(shù),表示列表中所有元素的數(shù)量,然后就是每一個(gè)列表元素的值,循環(huán)解析即可。
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 List<String> 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");//參數(shù)的數(shù)量 for (int i = 0; i < args.length; i++) { request.append("$" + args[i].getBytes("utf8").length).append("\r\n");//參數(shù)的長度 request.append(args[i]).append("\r\n");//參數(shù)的內(nèi)容 } 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 { //$符號后面的數(shù)值是指內(nèi)容的字節(jié)長度,而不是字符數(shù)量,所以要轉(zhuǎn)換為二進(jìn)制字節(jié)數(shù)組,再取指定長度的數(shù)據(jù) 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 List<String> 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 == '*') { List<String> 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()); List<String> 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(); } } } }
優(yōu)點(diǎn):
1、不依賴任何第三方組件,可以順利編譯通過;
2、代碼極其簡單。
不足之處:
1、未考慮并發(fā)訪問;
2、未提供更多的數(shù)據(jù)類型,以及讀寫方法,大家可以在此基礎(chǔ)上包裝一下。
看完了這篇文章,相信你對“Java中Socket如何實(shí)現(xiàn)Redis客戶端”有了一定的了解,如果想了解更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。