原理
为了能够在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); } } 复制代码
- 在上面的代码中,总共做了三步操作:
- 通过host和端口号建立redis客户端,其实底层是基于Netty client实现的,此时还没有与Tile38服务器建立链接;
- 调用connect()方法,这个时候依然没有建立链接,只是把需要建立链接的参数设置好,直到sync()后才真正地与Tile38建立了链接;
- 在建立链接后,做了校验密码的操作和设置返回数据格式的操作;其实这两步操作就已经是在执行相应的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命令,让这个工具更好使用。