1.13.始源之地:Vert.x集成

考槃在涧,硕人之宽。独寐寤言,永矢弗谖。——佚名《国风·卫风》

「壹」Vert.x子项目

    Vert.x从3.x开始,就将曾经的工具箱重新进行了模块化设计,您可以在官方文档中看到很多相关子项目(2.x中的模块化完全是灾难级的):

    可能您会困惑,如果使用Zero,是不是就用不了这些相关的子项目了呢?答案是否定的,在Zero中您可以开发连接点 程序,有了它您可以直接和Vert.x提供的官方子项目集成(类似MySQL Client ),也可以和Zero中设计的标准插件集成。Zero中的标准插件主要源起于实战,有些插件是Vert.x的子项目未提供的,而两种插件的引入使用统一的模式,Zero的玩法不一定最炫,但铁定会让您眼前一亮!

ifx全称为infix,翻译为“中缀、插入”。

    Zero的子项目vertx-ifx中包含了目前支持的所有插件,这些插件的开发流程都遵循了本章节的思路,其中还包含类似Neo4jElasticSearch、** Excel**等官方不支持的集成,一旦您学会了本章完整的开发思路,那么您的项目自然如虎添翼。目前的插件如下表:

    表格中的类型标识了该插件是Vert.x集成插件还是Zero的集成插件,本章不去剖析部分复杂的插件用法,后续会有单独的章节讲解,主要告诉读者:

  1. 如何在Zero中开发这种插件项目以及连接点程序(如何扩展)。

  2. 如何在您的Agent/Worker组件中使用这些扩展。

    这些插件的设计以单一职责为原则,项目本身比较小,代码不多,目的也是让您在选择时可直接根据所需按需引入

1.1. MySQL孤岛

1.1.1. 插件分析

    Zero中使用了MySQL,但由于动态建模的影响,并没有使用Vert.x中的MySQL Client,而是将Jooq标准化了。有关MySQL Client 的引入目前存在于zero-ifx-native项目中,它只包含了一个代码文件(读者无需自己开发):

package io.vertx.up.plugin.jdbc;

import io.vertx.core.Vertx;
import io.vertx.ext.asyncsql.MySQLClient;
import io.vertx.ext.sql.SQLClient;
import io.vertx.up.annotations.Plugin;
import io.vertx.up.eon.Plugins;
import io.vertx.up.fn.Fn;
import io.vertx.up.plugin.Infix;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

@Plugin
@SuppressWarnings("unchecked")
public class MySqlInfix implements Infix {

    private static final String NAME = "ZERO_MYSQL_POOL";

    private static final ConcurrentMap<String, SQLClient> CLIENTS
            = new ConcurrentHashMap<>();

    private static void initInternal(final Vertx vertx,
                                     final String name) {
        Fn.pool(CLIENTS, name, () -> 
            Infix.init(
                Plugins.Infix.MYSQL,
                (config) -> MySQLClient.createShared(vertx, config, name),
                MySqlInfix.class
            )
        );
    }

    public static void init(final Vertx vertx) {
        initInternal(vertx, NAME);
    }

    public static SQLClient getClient() {
        return CLIENTS.get(NAME);
    }

    @Override
    public SQLClient get() {
        return getClient();
    }
}

    把它称为孤岛,就是因为整个zero-ifx-native项目中只有这个文件,而这个文件却包含了很多巧思,也包含了Zero插件结构的骨架代码。     接口io.vertx.up.plugin.Infix是Zero中为第三方功能模块设计的插件接口,实现该接口的类必须包含两个基本规范:

  1. 这是一个插件,必须被@Plugin(io.vertx.up.annotations.Plugin)注解。

  2. 这个插件必须包含初始化静态方法(根据Vertx实例初始化插件):

    public static void init(final Vertx vertx)

    Zero中的插件连接点实现了池化客户端,如MySqlInfix构造完成后的完整结构如下:

    默认情况下,您获取的客户端引用直接和ZERO_MYSQL_POOL绑定,Zero中和Vert.x集成的所有Infix目前的版本都没扩展第二客户端 ,是否扩展最终取决于您的实际项目需求,上边代码中的插件您可以直接使用。

1.1.2. 插件使用

    本章讲解如何在Zero中使用Infix插件。

    第一步:先在您的项目pom.xml文件中引入该插件的依赖,由于Zero中所有vertx-ifx子项目都已经定义过版本,所以可省略<version>标签:

    <dependency>
        <groupId>cn.vertxup</groupId>
        <artifactId>zero-ifx-native</artifactId>
        <!-- 版本部分可省略,如果编译通不过,就使用下边这种。-->
        <version>${zero.version}</version>
    </dependency>

    第二步:在项目中书写以下配置文件(以MySQL为例):

src/main/resources/vertx.yml
src/main/resources/vertx-inject.yml
src/main/resources/vertx-mysql.yml

    上述三个文件内容如下:

vertx.yml片段

zero:
    # lime专用扩展,默认会读取vertx-inject.yml,所以不需配置它;
    # 此处的mysql表示Zero会去读取vertx-mysql.yml配置文件
    lime: mysql
    vertx:
        instance:
            # 未显示实例列表

vertx-mysql.yml内容如:

mysql:
  host: localhost
  port: 3306
  username: root
  password: ????
  database: DB_IOP

vertx-inject.yml内容如:

# MySQL插件引入
mysql: io.vertx.up.plugin.jdbc.MySqlInfix

    第三步:完成了上述配置后,您就可以直接在您的Agent或Worker组件中使用SQLClient了,参考下边代码:

Agent代码

package cn.vertxup.mysql;

import io.vertx.core.json.JsonObject;
import io.vertx.ext.sql.SQLClient;
import io.vertx.up.annotations.EndPoint;
import io.vertx.up.annotations.Plugin;

import javax.inject.infix.MySql;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

@EndPoint
@Path("/hi/mysql")
public class MySqlAgent {
    @Plugin
    private transient SQLClient clientA;

    @MySql
    private transient SQLClient clientB;

    @GET
    @Path("/client")
    public JsonObject hiAgent() {
        final JsonObject response = new JsonObject();
        response.put("clientA", this.clientA.toString());
        response.put("clientB", this.clientB.toString());
        return response;
    }
}

    发送请求到地址/hi/mysql/client,您可以得到如下响应:

{
    "data": {
        "clientA": "io.vertx.ext.asyncsql.impl.ClientWrapper@225b8ca6",
        "clientB": "io.vertx.ext.asyncsql.impl.ClientWrapper@225b8ca6"
    }
}

    从响应结果可以知道,实际clientAclientB引用了同一个对象,在整个环境中,都指向了ZERO_MYSQL_POOL绑定的SQLClient 。上边注解部分的代码使用了依赖注入(Worker和Agent组件中代码相同),如果不使用依赖注入,则您可以使用下边代码获取:

// 这种代码在非Agent/Worker环境中尤其有用,比如工具类中。
final SQLClient client = MySqlInfix.getClient();

    值的注意的是在Vert.x的3.9.8中(不知哪个版本开始),原来的vertx-mysql-postgresql-client项目已经被废弃 了,那是不是本章内容无用了呢?其实不然,虽然废弃了原来的SQLClient,但还引入了MySQLClient以及PgSQLClient,只是分得更细了,后续版本中我会更新Zero的Vert.x集成部分的代码,将native 拆成不同的子项目来对待,但整体思路以及玩法和本章节的内容保持高度一致,读者就不用去困惑了。

javax.inject.infix包中的注解为保留注解,后续版本扩展时会根据不同功能执行拓展,如果是您自己定义的插件,直接使用标准的io.vertx.up.annotations.Plugin即可。

1.2. Mongo

    前一个章节讲解了zero-ifx-native项目的内容、用法、以及Zero中插件的开发和使用思路,本章节使用同样的思路来分析zero-ifx-mongo ,这个项目是从最早的手机实战中延生出来的子项目,当初使用了MongoDB作为数据库。回到前文思路中,先看看MongoInfix的写法:

package io.vertx.tp.plugin.mongo;

import io.vertx.core.Vertx;
import io.vertx.ext.mongo.MongoClient;
import io.vertx.up.annotations.Plugin;
import io.vertx.up.eon.Plugins;
import io.vertx.up.fn.Fn;
import io.vertx.up.plugin.Infix;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

@Plugin
@SuppressWarnings("unchecked")
public class MongoInfix implements Infix {

    private static final String NAME = "ZERO_MONGO_POOL";

    private static final ConcurrentMap<String, MongoClient> CLIENTS
            = new ConcurrentHashMap<>();

    private static void initInternal(final Vertx vertx,
                                     final String name) {
        Fn.pool(CLIENTS, name,() -> 
            Infix.init(
                Plugins.Infix.MONGO,
                (config) -> MongoClient.createShared(vertx, config, name),
                MongoInfix.class
            )
        );
    }

    public static void init(final Vertx vertx) {
        initInternal(vertx, NAME);
    }

    public static MongoClient getClient() {
        return CLIENTS.get(NAME);
    }

    @Override
    public MongoClient get() {
        return getClient();
    }
}

    熟悉的味道、熟悉的配方,有了前一个章节的基础,理解这部分代码轻车熟路。它的基本配置如下:

Maven中引入

    <dependency>
        <groupId>cn.vertxup</groupId>
        <artifactId>zero-ifx-mongo</artifactId>
        <!-- 版本部分可省略,如果编译通不过,就使用下边这种。-->
        <version>${zero.version}</version>
    </dependency>

vertx-mongo.yml

具体配置项可参考官方的MongoOptions。

mongo:
    db_name: xxx
    post: xxx
    host: xxx
    connection_string: xxxx
    username: xxx
    password: xxx

vertx-inject.yml

mongo: io.vertx.tp.plugin.mongo.MongoInfix

    这部分的代码就不详细叙述了,参考下边代码段:

// ..... import 部分
// Mongo客户端引用
import io.vertx.ext.mongo.MongoClient;
// Mongo专用注解
import javax.inject.infix.Mongo;
// ..... 主代码部分
// 插件引用
    @Plugin
    private transient MongoClient clientA;

    @Mongo
    private transient MongoClient clientB;

    如此,您就可以直接在Zero的Agent和Worker组件中直接使用Mongo客户端了,若想要直接在工具类中使用,则可参考:

// 工具类中使用
final MongoClient client = MongoInfix.getClient();

1.3. Redis

    Zero目前版本只支持三个和Vert.x集成项目的子项目:

  • zero-ifx-native:MySQL/PgSQL

  • zero-ifx-mongo:MongoDB

  • zero-ifx-redis:Redis

    本章我们分析一下Redis的基础用法,Redis的插件在Zero中是最复杂的,它支持的功能如下:

  1. 提供了和Mongo类似的RedisInfix实现Redis的异步访问。

  2. 引入Jedis客户端新增同步访问(适用于旧场景)。

  3. 可替换Vert.x Web中的Session存储介质,将web-session存储在Redis中替换原生存储。

    还是回到本章重点Infix的代码:

package io.vertx.tp.plugin.redis;

import io.vertx.core.Vertx;
import io.vertx.core.json.JsonObject;
import io.vertx.redis.client.Redis;
import io.vertx.redis.client.RedisOptions;
import io.vertx.up.annotations.Plugin;
import io.vertx.up.eon.Plugins;
import io.vertx.up.log.Annal;
import io.vertx.up.plugin.Infix;
import io.vertx.up.util.Ut;
import redis.clients.jedis.Jedis;

import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

@Plugin
@SuppressWarnings("unchecked")
public class RedisInfix implements Infix {

    private static final String NAME = "ZERO_REDIS_POOL";
    private static final Annal LOGGER = Annal.get(RedisInfix.class);

    private static final ConcurrentMap<String, Redis> CLIENTS
            = new ConcurrentHashMap<>();
    private static final ConcurrentMap<String, Jedis> CLIENTS_SYNC
            = new ConcurrentHashMap<>();

    private static void initInternal(final Vertx vertx,
                                     final String name) {
        final RedisOptions options = Infix.init(Plugins.Infix.REDIS,
                /*
                 * Two parts for
                 * - Redis reference
                 * - RedisOptions reference ( For Sync usage )
                 */
                (config) -> new RedisOptions(initSync(name, config)),
                RedisInfix.class);
        /*
         * Redis client processing, ping to check whether it's Ok
         */
        final Redis redis = Redis.createClient(vertx, options);
        CLIENTS.put(name, redis);
        redis.connect(handler -> {
            /*
             * If connected, keep
             * If not connected, remove
             * This kind of operation could let your system synced the Redis
             * instead of Null Pointer in Unit Testing
             */
            if (handler.succeeded()) {
                LOGGER.info("[ Redis ] Connected successfully! {0}", 
                    options.toJson().encode());
            } else {
                final Throwable ex = handler.cause();
                if (Objects.nonNull(ex)) {
                    LOGGER.jvm(ex);
                }
                CLIENTS.remove(name);
            }
        });
    }

    private static JsonObject initSync(
        final String name, 
        final JsonObject options) {
        if (!CLIENTS_SYNC.containsKey(name)) {
            final String host = options.getString("host");
            final Integer port = options.getInteger("port");
            final Jedis client = new Jedis(host, port);
            // Auth
            final String password = options.getString("password");
            if (Ut.notNil(password)) {
                final String username = options.getString("username");
                if (Ut.isNil(username)) {
                    client.auth(password);
                } else {
                    client.auth(username, password);
                }
            }
            try {
                client.ping();
                CLIENTS_SYNC.put(name, client);
            } catch (final Throwable ex) {
                LOGGER.jvm(ex);
            }
        }
        return options;
    }

    public static void init(final Vertx vertx) {
        initInternal(vertx, NAME);
    }

    public static Redis getClient() {
        return CLIENTS.get(NAME);
    }

    public static Jedis getJClient() {
        return CLIENTS_SYNC.get(NAME);
    }

    public static void disabled() {
        CLIENTS.clear();
        CLIENTS_SYNC.clear();
    }

    @Override
    public Redis get() {
        return getClient();
    }
}

    在Redis部分,除了Vert.x在集成中引入了新版的Redis异步访问部分,还包含了Jedis的同步访问部分(同步不支持JSR330),二者结合使得功能更加完善,基本代码如下:

Maven引入

    <dependency>
        <groupId>cn.vertxup</groupId>
        <artifactId>zero-ifx-redis</artifactId>
        <!-- 版本部分可省略,如果编译通不过,就使用下边这种。-->
        <version>${zero.version}</version>
    </dependency>

vertx-redis.yml

redis:
    post: xxx
    host: xxx

vertx-inject.yml

redis: io.vertx.tp.plugin.redis.RedisInfix

    使用部分参考下边代码段:

// ..... import 部分
// Redis客户端引用
import io.vertx.redis.client.Redis;
// Jedis客户端引用
import redis.clients.jedis.Jedis;
// Redis专用注解
import javax.inject.infix.Redis;
// ..... 主代码部分
// 插件引用(注入模式只支持Redis,不支持Jedis)
    @Plugin
    private transient Redis clientA;
    @Redis
    private transient Redis clientB;
// 工具读取(异步)
final Redis client = RedisInfix.getClient();
// 同步唯一获取引用方式
final Jedis client = RedisInfix.getJClient();

    那么到这里,整个Zero中目前牵涉的三个和Vert.x的集成就告一段落,根据本章节的内容,读者可以很快和MySQL、Mongo、Redis集成,并去详细体会Zero中Infix 插件的详细内容,后续版本中,我会根据实际项目需求将部分客户端内容标准化,当前版本则提供给读者自己发挥。最后强调一点,本章节所有的Infix实现代码都是Zero已经提供的代码,使用时不需额外开发。

挑战:最后留一个挑战,您可以尝试自己去开发一个Infix彻底理解Zero中的插件结构。

「贰」内置插件

    除了第一章节表格中枚举的Zero子项目以外,Zero中还有部分内置的Infix插件,本章主要讲解一个特殊的内置插件MapInfix ;和Redis项目插件一样,Zero为了兼容部分遗留系统或老系统,通常都会开同步异步双通道来做新旧的桥接,如果只支持异步不支持同步,虽然看起来更加Vert.x,但对很多项目的迁移将是灾难。

2.1. MapInfix

    Vert.x中的SharedData数据结构是类似Hash表的一种结构,它不仅仅可以在本地环境使用,也可以在集群环境使用,Zero Extension中用来存储临时授权码 就使用了该结构,为了同时支持同步异步,Zero提供了一个特殊的SharedClient,该客户端架构和MySQLClient以及MongoClient类似(Vert.x没有),而本章的MapInfix 就是用来访问该Client的插件

    MapInfix的完整代码如下:

package io.vertx.tp.plugin.shared;

import io.vertx.core.Vertx;
import io.vertx.up.annotations.Plugin;
import io.vertx.up.eon.Plugins;
import io.vertx.up.fn.Fn;
import io.vertx.up.plugin.Infix;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

@Plugin
@SuppressWarnings("all")
public class MapInfix implements Infix {

    private static final String NAME = "ZERO_MAP_POOL";

    private static final ConcurrentMap<String, SharedClient<String, Object>> 
        CLIENTS = new ConcurrentHashMap<>();

    private static void initInternal(final Vertx vertx,
                                     final String name) {
        Fn.pool(CLIENTS, name,() -> 
            Infix.init(
                Plugins.Infix.SHARED,
                (config) -> SharedClient.createShared(vertx, config, name),
                MapInfix.class
            )
        );
    }

    public static void init(final Vertx vertx) {
        initInternal(vertx, NAME);
    }

    public static SharedClient<String, Object> getClient() {
        return CLIENTS.get(NAME);
    }

    public static String getDefaultName() {
        return NAME;
    }

    @Override
    public SharedClient<String, Object> get() {
        return getClient();
    }
}

    由于该插件是内置,所以基本使用代码中不需Maven的额外引入,直接可用。

vertx-shared.yml

shared:
  # 默认模式是同步模式,如果要使用异步模式,则需打开该开关
  async: true

vertx-inject.yml

shared: io.vertx.tp.plugin.shared.MapInfix

    使用代码段如:

// ...import 导入部分
import io.vertx.tp.plugin.shared.SharedClient;
import io.vertx.up.annotations.Plugin;
// JSR330注入
    @Plugin
    private transient SharedClient client;
// 工具类中使用方法
final SharedClient client = SharedInfix.getClient();

2.2. SharedClient

2.2.1. 结构综述

    由于当前插件的特殊性,SharedClient是Zero提供的接口,本小节简单讲解该接口中常用的API函数,该客户端的完整图示如下:

    每个SharedClient内部都包含了一个对应的同步引用(LocalMap)和异步引用 (AsyncMap)。由于Vert.x中的LocalMap不能在集群环境中跨环境使用,vertx-shared.yml 中的配置会让SharedClient选择内部实现模式,如果是异步则使用AsyncMap,如果是同步则使用LocalMap,且只能二选一。

    如果您要使用多个Map如何操作,您可以使用下边的API函数(图中的切换)。

SharedClient<K, V> switchClient(final String name)

    上述API可以让您直接切换客户端引用,一旦切换后您就可以操作第二个SharedClient,Zero中保证每个名字下只有一个SharedClient实例,实现池化单件结构。

// name = ZERO_MAP_POOL
SharedClient client1 = MapInfix.getClient(); 
// name = test
SharedClient client2 = client1.switchClient("test");

2.2.2. 最小单元

    SharedClient最小操作单元为io.vertx.up.atom.Kv——该数据结构存储了一个key = value的完整信息,包括键名、键值等,该类的完整实现代码如下:

package io.vertx.up.atom;

import java.util.Map;
import java.util.Objects;

/*
 * [Data Structure]
 * Definition for `key = value` pair here
 * It stored `key = value` and act as Pair here for some spec usage
 */
public final class Kv<K, V> {
    private K key;
    private V value;

    private Kv(final K key, final V value) {
        this.key = key;
        this.value = value;
    }

    public static <K, V> Kv<K, V> create() {
        return new Kv<>(null, null);
    }

    public static <K, V> Kv<K, V> create(final K key,
                                         final V value) {
        return new Kv<>(key, value);
    }

    public final K getKey() {
        return this.key;
    }

    public final V getValue() {
        return this.value;
    }

    public void set(final K key, final V value) {
        this.key = key;
        this.value = value;
    }

    @Override
    public final int hashCode() {
        return Objects.hashCode(this.key) ^ Objects.hashCode(this.value);
    }

    @Override
    public final boolean equals(final Object o) {
        if (o == this) {
            return true;
        }
        if (o instanceof Map.Entry) {
            final Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
            return Objects.equals(this.key, e.getKey()) &&
                    Objects.equals(this.value, e.getValue());
        }
        return false;
    }

    @Override
    public String toString() {
        return "Kv{" +
                "key=" + this.key +
                ", value=" + this.value +
                '}';
    }
}

    该结构和Map.Entry类似,但比Map.Entry更简化,Zero内部很多地方都使用了该数据结构。

2.2.3. 操作类API

    同步所有API定义如下:

    // 将 key = value 存储到SharedData中
    Kv<K, V> put(K key, V value);
    // 将 key = value 存储到SharedData中,expiredSecs秒后自动移除
    Kv<K, V> put(K key, V value, int expiredSecs);
    // 移除 key 对应的 key = value 键值对,返回刚移除的项
    Kv<K, V> remove(K key);
    // 读取 key 对应的 value 值
    V get(K key);
    // 读取 key 对应的 value,如果 once = true,则读取后移除
    V get(K key, boolean once);
    // 清除所有
    boolean clear();
    // 返回当前Map尺寸
    int size();
    // 返回当前Map所有 key 名集合
    Set<K> keys();

    异步所有API定义如下:


    @Fluent
    SharedClient<K, V> put(K key, V value, 
        Handler<AsyncResult<Kv<K, V>>> handler);

    @Fluent
    SharedClient<K, V> put(K key, V value, int expiredSecs, 
        Handler<AsyncResult<Kv<K, V>>> handler);

    @Fluent
    SharedClient<K, V> remove(K key, 
        Handler<AsyncResult<Kv<K, V>>> handler);

    @Fluent
    SharedClient<K, V> get(K key, 
        Handler<AsyncResult<V>> handler);

    @Fluent
    SharedClient<K, V> get(K key, boolean once, 
        Handler<AsyncResult<V>> handler);

    @Fluent
    SharedClient<K, V> clear(
        Handler<AsyncResult<Boolean>> handler);

    @Fluent
    SharedClient<K, V> size(
        Handler<AsyncResult<Integer>> handler);

    @Fluent
    SharedClient<K, V> keys(
        Handler<AsyncResult<Set<K>>> handler);

    简单汇总:

  1. 异步类型的API全部参考Vert.x子项目中的Client架构量身打造,且每个API都是@Fluent的,由于参考了Vert.x子项目,所以并不包含Future模式。

  2. 这些API比Map常规API唯一多出来的两个功能是:

    1. 存储到Map中的**项(key = value)**可按秒计时,如果超时则Zero会自动移除该项。

    2. 可使用一次读取的函数,该项被消费后就直接从数据结构中移除。

2.3. Ux.Pool

    Ux.Pool是Zero提供的另外一个工具函数,它近似于Ux.JwtUx.Jooq ,提供了Zero中常用的函数(可以理解成SharedClient的Future模式的封装)。使用它的初始化代码如:

// 使用默认的SharedData,name = ZERO_MAP_POOL
Ux.Pool.on();         // UxPool 类型
// 使用带名称的SharedData,name = test
Ux.Pool.on("test");    // UxPool类型

    实际上边代码片段最终返回了UxPool类型,看看它开放的所有API定义:

public <K, V> Future<Kv<K, V>> put(final K key, final V value)
public <K, V> Future<Kv<K, V>> put(final K key, final V value, int expiredSecs)
public <K, V> Future<Kv<K, V>> remove(final K key)
public <K, V> Future<V> get(final K key)
public <K, V> Future<V> get(final K key, final boolean once)
public Future<Boolean> clear()
public Future<Integer> size()
public Future<Set<String>> keys()

    该工具和SharedClient中的API维持了高度统一,所以只是转换了异步函数,转换成了Future 的返回结果以方便使用Zero的开发人员,最后提供一份内部使用临时授权码部分的调用代码给您参考:

    // 读取授权码
    @SuppressWarnings("all")
    static <V> Future<V> code(final String key) {
        final String codePool = CONFIG.getCodePool();
        return Ux.Pool.on(codePool).remove(key)
                .compose(value -> Ux.future((V) value.getValue()));
    }
    // 写入授权码,加入30秒的过期时间
    static <V> Future<V> code(final String key, final V value) {
        final String codePool = CONFIG.getCodePool();
        final Integer codeExpired = CONFIG.getCodeExpired();
        return Ux.Pool.on(codePool).put(key, value, codeExpired)
                .compose(item -> Ux.future(item.getValue()));
    }

「叁」小结

    本文主要给您讲解了Zero如何和Vert.x中的子项目做集成,并且通过集成部分给您讲解了Zero中的插件Infix架构,可能有部分读者会困惑,为什么不直接在SharedClient 中提供Future模式?这和Vert.x中的子项目有关,为了和Vert.x中的大部分子项目高度统一,所以才提供了原生风格类似的异步API(全@Fluent模式结构),而扩展部分的API才会提供Future模式,简单说SharedClient几乎是参考Vert.x中子项目的各种Client仿造的。

    目前Zero提供的工具类中,主要包含如下:

Last updated