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學架構]公眾號,獲取更多精品原創