盤點一下Redis中常用的Java客戶端,或者咱們手寫一個?


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回車+換行)結尾。

    image-20210703173900083

    圖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是一個RedisJava驅動包,大家常用的spring-boot-starter-data-redis中默認就采用的Lettuce。Lettuce是一個高性能基於Java編寫的Redis驅動框架,底層集成了Project Reactor提供天然的反應式編程,通信框架集成了Netty使用了非阻塞IO5.x版本之后融合了JDK1.8的異步編程特性,在保證高性能的同時提供了十分豐富易用的API

簡單使用方法

<dependency>
    <groupId>io.lettuce</groupId>
    <artifactId>lettuce-core</artifactId>
    <version>5.1.8.RELEASE</version>
</dependency>

Lettuce使用的時候依賴於四個主要組件:

  • RedisURI:連接信息。
  • RedisClientRedis客戶端,特殊地,集群連接有一個定制的RedisClusterClient
  • ConnectionRedis連接,主要是StatefulConnection或者StatefulRedisConnection的子類,連接的類型主要由連接的具體方式(單機、哨兵、集群、訂閱發布等等)選定,比較重要。
  • RedisCommandsRedis命令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

https://redisson.org/

https://github.com/redisson/redisson/wiki/目錄

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM