盘点一下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是我们最熟悉和最常用的客户端。轻量,简洁,便于集成和改造。

简单使用方法


    redis.clients
    jedis
    3.6.1

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

简单使用方法


    io.lettuce
    lettuce-core
    5.1.8.RELEASE

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 connection = redisClient.connect();     // <3> 创建线程安全的连接
        RedisCommands 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包


   org.springframework.boot
   spring-boot-starter-data-redis

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包

    
        org.redisson
        redisson
        3.16.0
    
    
  • 时间单节点连接和操作

    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的集成方式。


    org.redisson
    redisson-spring-boot-starter
    3.16.0

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
    

你可能感兴趣的:(盘点一下Redis中常用的Java客户端,或者咱们手写一个?)