经过13个月的开发阶段和208张已解决的故障单,我很高兴宣布Lettuce 5.0全面上市。 这是一个主要发行版,带有一些重大更改,新的有趣功能以及Java 9兼容性。

从Maven Central获取发行版

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

或从GitHub下载发行包 。

生菜5引入了动态的Redis命令API。 此编程模型使您可以声明命令方法并根据需要调用命令并支持Redis模块,而无需等待Lettuce支持新命令。

如果本地依赖项可用,则莴苣在Linux各自的macOS系统上默认为本地传输(epoll,kqueue)。 生菜5带来了重大变化。 它删除了过时的接口RedisConnection和RedisAsyncConnection以及它们分离的接口,从而支持StatefulRedisConnection和RedisCommands等。

重大更改:

我们将工件坐标从biz.paluch.redis:lettuce移到io.lettuce:lettuce-core

我们将包从biz.paluch.redis重定位到io.lettuce.core。 通过将导入中的旧软件包名称替换为新软件包名称,可以轻松实现迁移路径。

该文档已从http://redis.paluch.biz移至https://lettuce.io 。

删除了番石榴。

我们删除了一些不推荐使用的方法,有关详细信息,请参见下文。

生菜只需要netty 4.1 (不再支持netty 4.0)和Project Reactor 3.1 ,这使我们有了下一个变化:

通过使用Project Reactor类型Mono和Flux而不是RxJava 1和Observable ,反应式API基于反应式流。 如果您的代码中需要RxJava的Single和Observable ,则在rxjava-reactive-streams使用发布者适配器来适应Mono和Flux 。

此版本引入了随常规工件一起提供的新参考指南。 该参考指南绑定到特定版本,并且不会随着时间的推移而更改,例如Wiki。

  • 参考文档: https : //lettuce.io/core/release/reference/ 。
  • JavaDoc文档: https : //lettuce.io/core/release/api/ 。
<dependency>
  <groupId>io.lettuce</groupId>
  <artifactId>lettuce-core</artifactId>
  <version>5.0.0.RELEASE</version>
</dependency>

您可以找到完整的更改日志,其中包含自第一个5.0里程碑版本以来的所有更改, 在GitHub上 。 当心BREAKING更改。

感谢所有使生菜5成为可能的贡献者。 感谢任何反馈或在GitHub上提出问题 。

动态Redis命令API

Redis命令接口抽象为类型安全的Redis命令调用提供了一种动态方式。 它允许您使用命令方法声明接口,以显着减少调用Redis命令所需的样板代码。

Redis是一个数据存储,支持190多个已记录命令和450多个命令排列。 对于客户端开发人员和Redis用户而言,命令的增长和对即将发布的模块的跟踪是一项挑战,因为单个Redis客户端中的每个模块都没有完整的命令覆盖范围。

用Lettuce调用自定义命令需要几行代码来定义传递参数的命令结构并指定返回类型。

RedisCodec<String, String> codec = StringCodec.UTF8;
RedisCommands<String, String> commands = ...

String response = redis.dispatch(CommandType.SET, new StatusOutput<>(codec),
                new CommandArgs<>(codec)
                       .addKey(key)
                       .addValue(value));

Lettuce Command Interface抽象中的中心接口是Commands 。

该界面主要用作标记界面,可帮助您发现扩展该界面的界面。 您可以声明自己的命令接口和参数序列,其中命令名称是从方法名称派生的,或由@Command提供。 引入新命令不需要您等待新的Lettuce版本,但是它们可以通过自己的声明来调用命令。 该接口还可以根据用例支持不同的键和值类型。

根据方法声明,命令是同步执行,异步执行还是使用反应执行模型执行。

public interface MyRedisCommands extends Commands {

    String get(String key); // Synchronous Execution of GET

    @Command("GET")
    byte[] getAsBytes(String key); // Synchronous Execution of GET returning data as byte array

    @Command("SET") // synchronous execution applying a Timeout
    String setSync(String key, String value, Timeout timeout);

    Future<String> set(String key, String value); // asynchronous SET execution

    @Command("SET")
    Mono<String> setReactive(String key, String value); // reactive SET execution using SetArgs

    @CommandNaming(split = DOT) // support for Redis Module command notation -> NR.RUN
    double nrRun(String key, int... indexes);
}

RedisCommandFactory factory = new RedisCommandFactory(connection);

MyRedisCommands commands = factory.getCommands(MyRedisCommands.class);

String value = commands.get("key");

Redis命令界面给您带来了很多新的可能性。 其中之一是透明的反应式采用。 Lettuce的反应式API基于Reactive Streams,但是通过命令接口,您可以声明RxJava 1或RxJava 2返回类型,Lettuce将为您处理采用。 RxJava 1用户的迁移路径允许使用本机类型而无需 进一步转换。

命令界面批处理

命令接口支持命令批处理,以在批处理队列中收集多个命令,并通过一次写入传输将批处理刷新。 命令批处理以延迟的方式执行命令。 这意味着在调用时没有可用的结果。 批处理只能用于没有返回值(void)的同步方法或返回RedisFuture的异步方法。

可以在两个级别上启用命令批处理:

  • 在类级别,通过使用@BatchSize注释命令界面。 所有方法都参与命令批处理。
  • 在方法级别,通过将CommandBatching添加到参数中。 方法有选择地参与命令批处理。
@BatchSize(50)
interface StringCommands extends Commands {

    void set(String key, String value);

    RedisFuture<String> get(String key);

    RedisFuture<String> get(String key, CommandBatching batching);
}

StringCommands commands = …

commands.set("key", "value"); // queued until 50 command invocations reached.
                              // The 50th invocation flushes the queue.

commands.get("key", CommandBatching.queue()); // invocation-level queueing control
commands.get("key", CommandBatching.flush()); // invocation-level queueing control,
                                              // flushes all queued commands

迁移到反应流

Lettuce 4.0引入了基于RxJava 1和Observable的反应式API。 这是响应式Redis支持的开始。 生菜在各处都使用Observable ,因为其他反应式(如Single和Completable )仍处于测试阶段或正在开发中。

从那时起,反应空间发生了很多变化。 RxJava 2是RxJava 1的后继产品,现已到期。 RxJava 2并不完全基于Java 6的响应流和基线,而其他合成库也可以从Java 8中受益。

这也意味着,没有null值,并且使用专用值类型来表示API上的值多重性( 0|1和0|1|N )。

在Lettuce 5.0中,反应式API使用Project Reactor及其Mono和Flux类型。

生菜4

Observable<Long> del(K... keys);

Observable<K> keys(K pattern);

Observable<V> mget(K... keys);

生菜5

Mono<Long> del(K... keys);

Flux<K> keys(K pattern);

Flux<KeyValue<K, V>> mget(K... keys);

从RxJava 1切换到Project Reactor的使用需要切换库。 大多数运营商使用相似甚至相同的名称。 如果需要坚持使用RxJava 1,请使用rxjava-reactive-streams采用反应类型(RxJava 1 <-> Reactive Streams)。

迁移到反应流需要值包装以指示不存在值。 在命令可以返回null值的情况下,您会发现与以前的API和同步/异步API相比有所不同。 Lettuce 5.0附带了新的Value类型,这些类型是封装值(或不存在)的单子。

值,键值和其他值类型

反应式故事促进了不可变类型的实现,因此此发行版增强了现有的值类型并引入了新的类型以减少null使用并促进函数式编程。

值类型是基于Value和KeyValue / ScoredValue延伸从那里。 值是封装值或不存在的包装器类型。 Value可以通过不同的方式创建:

Value<String> value = Value.from(Optional.of("hello"));

Value<String> value = Value.fromNullable(null);

Value<String> value = Value.just("hello");

KeyValue<Long, String> value = KeyValue.from(1L, Optional.of("hello"));
 
KeyValue<String, String> value = KeyValue.just("key", "hello");

它转换为Optional和Stream以与其他功能用途集成,并允许值映射。

Value.just("hello").stream().filter(…).count();

KeyValue.just("hello").optional().isPresent();

Value.from(Optional.of("hello")).map(s -> s + "-world").getValue();

ScoredValue.just(42, "hello").mapScore(number -> number.doubleValue() * 3.14d).getScore();

您还将发现,所有值类型的公共字段都用getter封装,并且这些字段不再可访问。

退避/延迟策略

当运行具有大量使用Redis的服务的基于云的服务时,一旦分区结束,网络分区将严重影响Redis服务器的连接。 网络分区会同时影响所有断开连接的应用程序,并且所有节点或多或少会同时开始重新连接。

分区结束后,大多数应用程序将同时重新连接。 随着重新连接时间的随机化,抖动回退策略可以充分利用这种影响。

生菜具有各种退避实现:

  • 均等抖动
  • 全抖动
  • 与装饰相关的抖动

这些在ClientResources中配置:

DefaultClientResources.builder()
        .reconnectDelay(Delay.decorrelatedJitter())
        .build();

DefaultClientResources.builder()
        .reconnectDelay(Delay.equalJitter())
        .build();

另请参阅: https : //www.awsarchitectureblog.com/2015/03/backoff.html和 https://lettuce.io/core/5.0.0.RELEASE/reference/#clientresources.advanced-settings

Z…RANGE命令的新API

排序集范围命令附带有关方法重载的简化API。 ZRANGEBYSCORE ,诸如ZRANGEBYSCORE , ZRANGEBYLEX , ZREMRANGEBYLEX等命令声明了接受Range和Limit对象而不是不断增长的参数列表的方法。 新的Range允许分数和值类型应用适当的二进制编码。

4.2及更早版本

commands.zcount(key, 1.0, 3.0)

commands.zrangebyscore(key, "-inf", "+inf")

commands.zrangebyscoreWithScores(key, "[1.0", "(4.0")

commands.zrangebyscoreWithScores(key, "-inf", "+inf", 2, 2)

从5.0开始

commands.zcount(key, Range.create(1.0, 3.0));

commands.zrangebyscore(key, Range.unbounded());

commands.zrangebyscoreWithScores(key, Range.from(Boundary.including(1.0), Boundary.excluding(4.0));

commands.zrangebyscoreWithScores(key, Range.unbounded(), Limit.create(2, 2));

再见了番石榴

Lettuce 5.0不再使用Google的Guava库。 Guava是Java 6兼容时代的好朋友, Future同步和回调很无聊。 随Java 8和CompletableFuture改变了。

HostAndPort或LoadingCache等其他用途可以被内联或替换为Java 8的Collection框架。

删除不推荐使用的接口和方法

此发行版删除了不推荐使用的接口RedisConnection和RedisAsyncConnection及其分离的接口,而支持StatefulRedisConnection和RedisCommands 。

使用该API时,您会发现细微的差别。 事务命令和数据库选择不再通过Redis Cluster API可用,因为旧API是从独立API派生的。 RedisCommands和RedisAsyncCommands不再是Closeable 。 请使用commands.getStatefulConnection().close()关闭连接。 此更改消除了关闭命令界面和关闭连接的歧义。

连接池更换

花了相当长的时间,但4.3不赞成使用Lettuce的现有连接池支持。 特别是RedisClient.pool(…)和RedisClient.asyncPool(…) 。 这些方法在Lettuce 5.0中已删除。

连接池的支持非常有限,并且将需要额外的重载,从而使API混乱以暴露所有支持的连接的池。 此版本带来了一个可定制且不会污染API的替代品。 ConnectionPoolSupport提供了一些方法来创建接受工厂方法和池配置的连接池。

返回的连接对象是在调用close()时将连接返回到其池的代理。 StatefulConnection实施Closeable允许使用try-with-resources。

GenericObjectPool<StatefulRedisConnection<String, String>> pool = ConnectionPoolSupport
        .createGenericObjectPool(() -> client.connect(), new GenericObjectPoolConfig());


try(StatefulRedisConnection<String, String> connection = pool.borrowObject()) {
    // Work
}


pool.close();

Redis集群拓扑刷新共识

群集拓扑刷新在某些情况下(动态拓扑源)可能导致孤立。 如果从群集中删除了群集节点,而生菜决定接受该已删除节点的拓扑视图,则可能会发生这种情况。 生菜卡在该节点上,无法使用剩余的群集。

此版本引入了PartitionsConsensus策略,以便在获取多个视图时确定最合适的拓扑视图。 可以通过重写RedisClusterClient.determinePartitions(Partitions, Map<RedisURI, Partitions>)定义策略。

生菜默认选择具有大多数先前已知群集节点的拓扑视图。 这有助于生菜坚持由最多节点组成的群集。

另请参阅: https : //github.com/lettuce-io/lettuce-core/issues/355

Redis集群中的异步连接

RedisClusterClient现在异步连接,而没有中间阻塞到集群节点。 连接进度在之间共享 多个线程首次请求群集节点连接。 以前,连接是顺序同步的。 每次连接尝试都会阻止其他线程的后续尝试。 如果群集节点连接超时,则线程将受到等待时间增加的惩罚。 如果说有10个线程等待连接,则最后一个线程必须等待多达10倍的连接超时。

异步连接一旦在内部使用Future即可启动连接,因此多个并发连接尝试会共享结果Future 。 错误现在可以更快地失败,并且群集节点的使用完全异步,无需同步,也没有陷入线程死锁的危险。

Redis Cluster Pub / Sub关于节点选择

RedisClusterClient.connectPubSub()现在返回一个StatefulRedisClusterPubSubConnection ,该StatefulRedisClusterPubSubConnection允许RedisClusterPubSubListener的注册以及在特定集群节点上的预订。

特定于群集节点的订阅允许使用键空间通知。 密钥空间通知不同于用户空间Pub / Sub,因为密钥空间通知不会广播到整个集群,而是仅在发生通知的节点上发布。 一个常见的用例是密钥在集群中过期。

StatefulRedisClusterPubSubConnection connection = client.connectPubSub();

connection.addListener(…);

connection.setNodeMessagePropagation(true);

RedisClusterPubSubCommands<String, String> sync = connection.sync();
sync.slaves().commands().psubscribe("__key*__:expire");

本地运输

如果操作系统是合格的并且依赖项可用,则Lettuce现在默认情况下使用本机传输。 Lettuce从4.0版本开始支持epoll(在基于Linux的系统上),从此版本开始支持kqueue(基于BSD的系统,如macOS)。

可以通过设置io.lettuce.core.epoll=false来禁用Epoll的使用和系统属性。 以类似的方式,可以禁用kqueue 与io.lettuce.core.kqueue=false 。

Epoll依赖性:

<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-transport-native-epoll</artifactId>
    <version>${netty-version}</version>
    <classifier>linux-x86_64</classifier>
</dependency>

Kqueue依赖项:

<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-transport-native-kqueue</artifactId>
    <version>${netty-version}</version>
    <classifier>osx-x86_64</classifier>
</dependency>

翻译自: https://www.javacodegeeks.com/2017/09/redis-client-lettuce-5-ga-released.html