Java中那些Redis的客戶端
前面我們的所有操作都是基於redis-cli來完成的,那麼我們要在Java中操作Redis,怎麼做呢?首先我們先來了解一下Redis Serialization Protocol(Redis序列化協議),這個是Redis提供的一種,客戶端和Redis服務端通訊傳輸的編碼協議,服務端收到罅隙ihou,會基於這個約定編碼進行解碼。
-
開啟Wireshark工具,對VMnet8這個網路進行抓包
-
增加過濾條件
ip.dst_host==192.168.221.128 and tcp.port in {6379}
-
使用RDM工具連線到Redis Server進行key-value操作,比如執行 set name mic
-
通過Wireshark工具監控資料包內容,如圖3-3所示,可以看到實際發出的資料包是:
*3\r\n$3\r\nSET\r\n$4\r\nname\r\n$3\r\nmic
-
其中
*3*
代表引數個數,set name mic, 表示三個引數。 -
$3
表示屬性長度,$
表示包含3個字元。
客戶端和伺服器傳送的命令或資料一律以 \r\n (CRLF回車+換行)結尾。
圖3-3 -
基於這樣一個特性,我們可以自己實現一個Java客戶端。
自定義Redis客戶端
下面我們通過抓包相關的命令,瞭解Redis客戶端的工作機制。
定義常量池。
public class CommandConstant {
/**
* 開始符
*/
public static final String START = "*";
/**
* 指令長度符
*/
public static final String LENGTH = "$";
/**
* 換行符
*/
public static final String LINE = "\r\n";
public enum CommandEnum {
SET,
GET,
INCR
}
}
CustomClientSocket
CustomClientSocket用來建立網路通訊連線,並且傳送資料指定到RedisServer。
public class CustomClientSocket {
private Socket socket;
private InputStream inputStream;
private OutputStream outputStream;
public CustomClientSocket(String ip,int port) {
try {
socket=new Socket(ip,port);
inputStream=socket.getInputStream();
outputStream=socket.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
}
public void send(String cmd){
try {
outputStream.write(cmd.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
public String read(){
byte[] bytes = new byte[1024];
int count = 0;
try {
count = inputStream.read(bytes);
} catch (IOException e) {
e.printStackTrace();
}
return new String(bytes, 0, count);
}
}
封裝客戶端
public class CustomRedisClient {
private CustomClientSocket customClientSocket;
public CustomRedisClient(String host,int port) {
customClientSocket=new CustomClientSocket(host,port);
}
public String set(String key, String value) {
customClientSocket.send(convertToCommand(CommandConstant.CommandEnum.SET, key.getBytes(), value.getBytes()));
return customClientSocket.read();
}
public String get(String key) {
customClientSocket.send(convertToCommand(CommandConstant.CommandEnum.GET, key.getBytes()));
return customClientSocket.read();
}
public static String convertToCommand(CommandConstant.CommandEnum command, byte[]... bytes) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(CommandConstant.START).append(bytes.length + 1).append(CommandConstant.LINE);
stringBuilder.append(CommandConstant.LENGTH).append(command.toString().length()).append(CommandConstant.LINE);
stringBuilder.append(command.toString()).append(CommandConstant.LINE);
for (byte[] aByte : bytes) {
stringBuilder.append(CommandConstant.LENGTH).append(aByte.length).append(CommandConstant.LINE);
stringBuilder.append(new String(aByte)).append(CommandConstant.LINE);
}
return stringBuilder.toString();
}
}
測試方法
public static void main(String[] args) {
CustomRedisClient redisClient=new CustomRedisClient("192.168.221.128",6379);
System.out.println(redisClient.set("name","mic"));
System.out.println(redisClient.get("name"));
}
總結
你看,理解了原理之後,自己去實現起來發現並不難。
但是實際開發過程中,我們難倒也需要開發自己開發客戶端嗎?當然不用,官方推薦了以下三種客戶端
配置 | 作用 |
---|---|
Jedis | A blazingly small and sane redis java client |
lettuce | Advanced Redis client for thread-safe sync, async, and reactive usage. Supports Cluster, Sentinel, Pipelining, and codecs. |
Redisson | distributed and scalable Java data structures on top of Redis server |
Jedis
Jedis是我們最熟悉和最常用的客戶端。輕量,簡潔,便於整合和改造。
簡單使用方法
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.6.1</version>
</dependency>
public static void main(String[] args) {
Jedis jedis = new Jedis("127.0.0.1", 6379);
jedis.set("qingshan", "2673");
System.out.println(jedis.get("qingshan"));
jedis.close();
}
一般來說,我們不會使用單個Jedis連線,而是會使用連線池,Jedis提供了連線池的功能。
public static void main(String[] args) {
JedisPool pool = new JedisPool(ip, port);
Jedis jedis = jedisPool.getResource();
}
Luttece
Lettuce
是一個Redis
的Java
驅動包,大家常用的spring-boot-starter-data-redis中預設就採用的Lettuce。Lettuce
是一個高效能基於Java
編寫的Redis
驅動框架,底層整合了Project Reactor
提供天然的反應式程式設計,通訊框架整合了Netty
使用了非阻塞IO
,5.x
版本之後融合了JDK1.8
的非同步程式設計特性,在保證高效能的同時提供了十分豐富易用的API
。
簡單使用方法
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<version>5.1.8.RELEASE</version>
</dependency>
Lettuce
使用的時候依賴於四個主要元件:
RedisURI
:連線資訊。RedisClient
:Redis
客戶端,特殊地,叢集連線有一個定製的RedisClusterClient
。Connection
:Redis
連線,主要是StatefulConnection
或者StatefulRedisConnection
的子類,連線的型別主要由連線的具體方式(單機、哨兵、叢集、訂閱釋出等等)選定,比較重要。RedisCommands
:Redis
命令API
介面,基本上覆蓋了Redis
發行版本的所有命令,提供了同步(sync
)、非同步(async
)、反應式(reative
)的呼叫方式,對於使用者而言,會經常跟RedisCommands
系列介面打交道。
public static void main(String[] args) {
RedisURI redisUri = RedisURI.builder() // <1> 建立單機連線的連線資訊
.withHost("192.168.221.128")
.withPort(6379)
.withTimeout(Duration.of(10, ChronoUnit.SECONDS))
.build();
RedisClient redisClient = RedisClient.create(redisUri); // <2> 建立客戶端
StatefulRedisConnection<String, String> connection = redisClient.connect(); // <3> 建立執行緒安全的連線
RedisCommands<String, String> redisCommands = connection.sync(); // <4> 建立同步命令
SetArgs setArgs = SetArgs.Builder.nx().ex(5);
String result = redisCommands.set("name", "throwable", setArgs);
System.out.println(result);
result = redisCommands.get("name");
System.out.println(result);
// ... 其他操作
connection.close(); // <5> 關閉連線
redisClient.shutdown(); // <6> 關閉客戶端
}
和Spring Boot整合使用
Lettuce是Spring Boot 2.x 預設的客戶端,替換了Jedis。整合之後我們不需要單獨使用它,直接呼叫Spring的RedisTemplate操作,連線和建立和關閉也不需要我們操心。
引入依賴jar包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
application.yml配置檔案如下
redis:
port: 6379
host: 192.168.221.128
lettuce:
pool:
max-active: -1
max-idle: 2000
max-wait: -1
min-idle: 1
time-between-eviction-runs: 5000
使用方法
@RestController
@RequestMapping("/")
public class LutteceController {
@Autowired
RedisTemplate redisTemplate;
@GetMapping
public ResponseEntity get(){
String name=(String)redisTemplate.opsForValue().get("name");
return ResponseEntity.ok(name);
}
}
Redisson
Redisson是一個在Redis的基礎上實現的Java駐記憶體資料網格(In-Memory Data Grid)。它不僅提供了一系列的分散式的Java常用物件,還提供了許多分散式服務。其中包括(BitSet
, Set
, Multimap
, SortedSet
, Map
, List
, Queue
, BlockingQueue
, Deque
, BlockingDeque
, Semaphore
, Lock
, AtomicLong
, CountDownLatch
, Publish / Subscribe
, Bloom filter
, Remote service
, Spring cache
, Executor service
, Live Object service
, Scheduler service
) Redisson提供了使用Redis的最簡單和最便捷的方法。Redisson的宗旨是促進使用者對Redis的關注分離(Separation of Concern),從而讓使用者能夠將精力更集中地放在處理業務邏輯上。
簡單使用方法
-
引入依賴Jar包
<dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.16.0</version> </dependency>
-
時間單節點連線和操作
public static void main(String[] args) { Config config=new Config(); config.useSingleServer().setAddress("redis://192.168.221.128:6379"); RedissonClient redissonClient= Redisson.create(config); redissonClient.getBucket("test").set("mic"); System.out.println(redissonClient.getBucket("test").get()); }
和Spring Boot整合
Spring Boot的整合方式。
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.16.0</version>
</dependency>
application.yml中的配置。
spring:
redis:
timeout: 2000
host: 192.168.221.128
port: 6379
使用方法。
@RestController
public class RedissonController {
@Autowired
RedissonClient redissonClient;
@GetMapping("/")
public String get(){
return redissonClient.getBucket("test").get().toString();
}
}
另外一種配置方式如下
-
修改application.yml
spring: redis: redisson: file: classpath:redisson.yml
-
建立一個redisson.yml檔案,內容如下
singleServerConfig: address: redis://192.168.221.128:6379 #--------------------------------------------- # 連線空閒超時,單位:毫秒 idleConnectionTimeout: 10000 # 連線超時,單位:毫秒 connectTimeout: 10000 # 命令等待超時,單位:毫秒 timeout: 3000 # 命令失敗重試次數,如果嘗試達到 retryAttempts(命令失敗重試次數) 仍然不能將命令傳送至某個指定的節點時,將丟擲錯誤。 # 如果嘗試在此限制之內傳送成功,則開始啟用 timeout(命令等待超時) 計時。 retryAttempts: 3 # 命令重試傳送時間間隔,單位:毫秒 retryInterval: 1500
關注[跟著Mic學架構]公眾號,獲取更多精品原創