Springboot集成Tile38客户端

314 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第22天,点击查看活动详情

原理

为了能够在springboot项目中使用Tile38空间数据库,我们需要把Tile38客户端集成到springboot项目中。因为Tile38使用了redis协议,所以我们可以使用现有的redis客户端工具类集成Tile38的命令。

依赖

redis客户端工具中,我们首选lettuce,我们先把相关的依赖引进来:

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

实现自定义命令

因为Tile38有些命令和Redis命令不同,所以我们无法直接使用现有的redis命令来操作Tile38;因此我们需要在redis客户端的基础上实现自己的自定义命令。

  • 创建Tile38客户端链接

    @Data
    public class Client implements AutoCloseable {
      private static final StringCodec STRING_CODEC = new StringCodec(StandardCharsets.UTF_8);
      // redis客户端
      private RedisClient redisClient;
      // tile38链接
      private StatefulRedisConnection<String, String> conn;
      // 命令工具,主要用来发送命令,基于上面的链接来发送命令
      private RedisCommands<String, String> commands;
      // 构造一个客户端工具
      public Client(final String host, final int port) {
        this(host, port, StringUtils.EMPTY);
      }
    ​
      // 构造客户端工具
      public Client(final String host, final int port, final String password) {
        // 先构造redis客户端工具
        this.redisClient = RedisClient.create(RedisURI.create(host, port));
        // 获取链接
        this.conn = this.redisClient.connect();
        // 创建命令工具
        this.commands = this.conn.sync();
        if (StringUtils.isNotBlank(password)) {
          // 校验密码
          auth(password);
        }
        // 修改当前通道返回的数据格式
        output(OutPutType.JSON);
      }
    }
    

    在上面的代码中,总共做了三步操作:

    1. 通过host和端口号建立redis客户端,其实底层是基于Netty client实现的,此时还没有与Tile38服务器建立链接;
    2. 调用connect()方法,这个时候依然没有建立链接,只是把需要建立链接的参数设置好,直到sync()后才真正地与Tile38建立了链接;
    3. 在建立链接后,做了校验密码的操作和设置返回数据格式的操作;其实这两步操作就已经是在执行相应的Tile38命令了;
  • 执行Tile38命令

    下面这种执行命令的方式是最简单的一种方式,适用于执行不带任何参数的命令,比如PING

    // 执行Tile38命令
    private String execute(Tile38Command command) {
        // 执行指定命令并返回字符串
        return commands.dispatch(command, new ValueOutput<>(STRING_CODEC));
    }
    

    为了满足需要执行带参数的命令,我们还要重载execute方法,给它补上一个args参数;

    同样的,我们还可以做其他的重载,下面针对数组和列表各重载了一次。

    比如创建链接成功后,需要校验密码的时候,就可以使用这个重载后的方法执行AUTH password命令;

    private String execute(Tile38Command command, String... args) {
        CommandArgs<String, String> commandArgs = new CommandArgs<>(STRING_CODEC);
        if (ArrayUtils.isNotEmpty(args)) {
            for (String arg : args) {
                commandArgs.add(arg);
            }
        }
        return commands.dispatch(command, new ValueOutput<>(STRING_CODEC), commandArgs);
    }
    ​
    private String execute(Tile38Command command, List<String> args) {
        CommandArgs<String, String> commandArgs = new CommandArgs<>(STRING_CODEC);
        if (!CollectionUtils.isEmpty(args)) {
            for (String arg : args) {
                commandArgs.add(arg);
            }
        }
        return commands.dispatch(command, new ValueOutput<>(STRING_CODEC), commandArgs);
    }
    

    从上面的代码中,可以看出,通过redis客户端调用Tile38命令就跟拼接参数一样,只要把对应的参数按顺序放到对应的数组或者列表中去,就可以通过redis客户端发送给Tile38服务器去执行。那么根据上面得到的规律,我们再次重载execute方法,以便让我们再更复杂的命令中使用起来更加方便:

    private String execute(Tile38Command command, List<String>... args) {
        CommandArgs<String, String> commandArgs = new CommandArgs<>(STRING_CODEC);
        if (ArrayUtils.isNotEmpty(args)) {
          for (List<String> argList : args) {
            for (String arg : argList) {
              commandArgs.add(arg);
            }
          }
        }
        return commands.dispatch(command, new ValueOutput<>(STRING_CODEC), commandArgs);
      }
    ​
      private String execute(Tile38Command command, String[]... args) {
        CommandArgs<String, String> commandArgs = new CommandArgs<>(STRING_CODEC);
        if (ArrayUtils.isNotEmpty(args)) {
          for (String[] argList : args) {
            for (String arg : argList) {
              commandArgs.add(arg);
            }
          }
        }
        return commands.dispatch(command, new ValueOutput<>(STRING_CODEC), commandArgs);
      }
    
  • 关闭链接

    我们为了在客户端使用完毕后能够及时地释放掉,需要实现AutoCloseable接口,以便我们能够通过close方法释放资源:

    @Override
    public void close() {
        // 先把commands置为null,以便解除对conn的依赖
        if (Objects.nonNull(this.commands)) {
            this.commands = null;
        }
        // 关闭链接
        if (Objects.nonNull(this.conn)) {
            this.conn.close();
        }
        // 关闭Netty的线程池
        if (Objects.nonNull(redisClient)) {
            redisClient.shutdown();
        }
    }
    

至此,我们就完成了一个最简单的Tile38客户端的集成。为了让开发人员能够更好地使用我们集成的客户端工具,我们还需要在此基础上最一些封装、优化的工作。后续我将逐步补充如何封装Tile38命令,让这个工具更好使用。