All Downloads are FREE. Search and download functionalities are using the official Maven repository.

rebue.wheel.vertx.verticle.AbstractMainVerticle Maven / Gradle / Ivy

There is a newer version: 3.5.45
Show newest version
package rebue.wheel.vertx.verticle;

import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
import io.vertx.config.ConfigChange;
import io.vertx.config.ConfigRetriever;
import io.vertx.config.ConfigRetrieverOptions;
import io.vertx.config.ConfigStoreOptions;
import io.vertx.core.*;
import io.vertx.core.eventbus.Message;
import io.vertx.core.eventbus.MessageConsumer;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.core.json.jackson.DatabindCodec;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import rebue.wheel.core.file.FileUtils;
import rebue.wheel.vertx.guice.GuiceVerticleFactory;
import rebue.wheel.vertx.guice.VertxGuiceModule;

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.Map.Entry;

//@SuppressWarnings("deprecation")
@Slf4j
public abstract class AbstractMainVerticle extends AbstractVerticle {

    /**
     * 部署成功事件
     */
    public static final String EVENT_BUS_DEPLOY_SUCCESS = "rebue.wheel.vertx.verticle.main-verticle.deploy-success";
    /**
     * 配置改变事件
     */
    public static final String EVENT_BUS_CONFIG_CHANGED = "rebue.wheel.vertx.verticle.main-verticle.config-changed";

    static {
        // 初始化jackson的功能
        DatabindCodec.mapper()
                .disable(
                        DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES   // 忽略没有的字段
                )
                .disable(
                        SerializationFeature.WRITE_DATES_AS_TIMESTAMPS      // 按默认的时间格式 yyyy-MM-dd'T'HH:mm:ss.SSS 转换有时会报错
                )
                .enable(
                        MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES    // 忽略字段和属性的大小写
                )
                .enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)  // 浮点型用BigDecimal处理
                // .enable(DeserializationFeature.USE_BIG_INTEGER_FOR_INTS) // 整型用BigInteger处理
                .setSerializationInclusion(Include.NON_NULL)                // 不序列化值为null的字段
                .setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"))
                .registerModules(new JavaTimeModule(),                      // 支持Java8的LocalDate/LocalDateTime类型
                        new Jdk8Module(),
                        new ParameterNamesModule());
    }

    @Inject
    @Named("mainId")
    private String mainId;

    private final List deploymentIds = new LinkedList<>();

    private MessageConsumer configChangedConsumer;


    @Override
    public void start(Promise startPromise) {
        log.info("MainVerticle start");

        ConfigRetrieverOptions defaultConfigRetrieverOptions = new ConfigRetrieverOptions()
                .setIncludeDefaultStores(true);

        String classpath                 = FileUtils.getClassesPath(this.getClass());
        Path   defaultConfigYamlFilePath = Path.of(classpath, "conf", "config.yml");
        if (Files.exists(defaultConfigYamlFilePath)) {
            log.debug("加载conf/config.yml文件的配置");
            ConfigStoreOptions defaultConfigStoreOptions = new ConfigStoreOptions()
                    .setType("file")
                    .setFormat("yaml")
                    .setOptional(true)
                    .setConfig(new JsonObject().put("path", defaultConfigYamlFilePath));
            defaultConfigRetrieverOptions.addStore(defaultConfigStoreOptions);
        }

        final ConfigRetriever defaultConfigRetriever = ConfigRetriever.create(this.vertx, defaultConfigRetrieverOptions);
        defaultConfigRetriever.getConfig(defaultConfigRes -> {
            if (defaultConfigRes.failed()) {
                log.warn("Get config failed", defaultConfigRes.cause());
                startPromise.fail(defaultConfigRes.cause());
                return;
            }

            final JsonObject defaultConfigJsonObject = defaultConfigRes.result();
            if (defaultConfigJsonObject == null || defaultConfigJsonObject.isEmpty()) {
                startPromise.fail("Get config is empty");
                return;
            }

            Long scanPeriod = defaultConfigJsonObject.getLong("scanPeriod");
            if (scanPeriod == null) scanPeriod = 15000L;    // 默认15秒检查一下是否有更新
            JsonArray stores = defaultConfigJsonObject.getJsonArray("stores");
            if (stores == null) {
                startWithConfig(startPromise, defaultConfigJsonObject);
            } else {
                log.info("配置仓库数量: {}", stores.size());
                final ConfigRetrieverOptions configRetrieverOptions = new ConfigRetrieverOptions();
                configRetrieverOptions.setScanPeriod(scanPeriod);
                stores.forEach(store -> configRetrieverOptions.addStore(new ConfigStoreOptions((JsonObject) store)));

                ConfigRetriever storeConfigRetriever = ConfigRetriever.create(this.vertx, configRetrieverOptions);
                storeConfigRetriever.getConfig(storeConfigRes -> {
                    if (storeConfigRes.failed()) {
                        log.warn("Get store config failed", storeConfigRes.cause());
                        startPromise.fail(storeConfigRes.cause());
                        return;
                    }

                    final JsonObject storeConfigJsonObject = storeConfigRes.result();
                    if (storeConfigJsonObject == null || storeConfigJsonObject.isEmpty()) {
                        startPromise.fail("Get store config is empty");
                        return;
                    }

                    startWithConfig(startPromise, storeConfigJsonObject);
                });

                storeConfigRetriever.listen(this::listenConfigChange);
            }

            defaultConfigRetriever.listen(this::listenConfigChange);
        });
    }

    /**
     * 监听配置改变事件
     *
     * @param configChange 配置改变对象
     */
    private void listenConfigChange(ConfigChange configChange) {
        log.info("配置有变动");
        JsonObject previousConfiguration = configChange.getPreviousConfiguration();
        JsonObject newConfiguration      = configChange.getNewConfiguration();
        log.info("上一次的配置: \n{}", previousConfiguration.encode());
        log.info("新配置: \n{}", newConfiguration.encode());
        log.info("发布配置改变的消息");
        this.vertx.eventBus().publish(EVENT_BUS_CONFIG_CHANGED + "::" + this.mainId, newConfiguration);
    }

    /**
     * 处理配置改变
     */
    @SneakyThrows
    private void handleConfigChange(Message message) {
        log.info("处理配置改变");
        this.configChangedConsumer.unregister(res -> {
            log.info("undeploy verticles");
            final List> undeployFutures = new LinkedList<>();
            for (String deploymentId : deploymentIds) {
                undeployFutures.add(this.vertx.undeploy(deploymentId));
            }

            Future.all(undeployFutures)
                    .onSuccess(compositeFuture -> {
                        log.info("取消部署verticle完成");
                        this.vertx.verticleFactories().forEach(verticleFactory -> {
                            log.info("unregisterVerticleFactory: {}", verticleFactory.prefix());
                            this.vertx.unregisterVerticleFactory(verticleFactory);
                        });
                        this.vertx.close();
                    }).onFailure(err -> log.error("取消部署verticle失败", err));
        });
    }

    /**
     * 带配置项运行
     *
     * @param startPromise 运行状态控制
     * @param config       配置项
     */
    private void startWithConfig(final Promise startPromise, final JsonObject config) {
        log.info("start with config");
        log.info("添加注入模块");
        final List guiceModules = new LinkedList<>();
        log.info("添加默认的注入模块VertxGuiceModule");
        guiceModules.add(new VertxGuiceModule(this.vertx, config));
        log.info("通过SPI加载AbstractModule模块");
        ServiceLoader guiceModuleServiceLoader = ServiceLoader.load(Module.class);
        guiceModuleServiceLoader.forEach(guiceModules::add);
        log.info("添加自定义的注入模块");
        addGuiceModules(guiceModules);
        log.info("创建注入器");
        final Injector injector = Guice.createInjector(guiceModules);
        log.debug("注入自己(MainVerticle实例)的属性");
        injector.injectMembers(this);
        log.info("注册GuiceVerticleFactory工厂");
        this.vertx.registerVerticleFactory(new GuiceVerticleFactory(injector));

        log.info("部署前事件");
        beforeDeploy();

        log.info("部署verticle");
        final Map> verticleClasses = new LinkedHashMap<>();
        addVerticleClasses(verticleClasses);
        deploymentIds.clear();
        final List> deployFutures = new LinkedList<>();
        for (final Entry> entry : verticleClasses.entrySet()) {
            final JsonObject configJsonObject = config.getJsonObject(entry.getKey());
            if (configJsonObject == null) {
                deployFutures.add(this.vertx.deployVerticle("guice:" + entry.getValue().getName())
                        .onSuccess(deploymentIds::add));
            } else {
                deployFutures.add(this.vertx.deployVerticle("guice:" + entry.getValue().getName(), new DeploymentOptions(configJsonObject))
                        .onSuccess(deploymentIds::add));
            }
        }

        // 部署成功或失败事件
        Future.all(deployFutures)
                .onSuccess(handle -> {
                    log.info("部署Verticle完成,发布部署成功的消息");
                    final String deploySuccessEventBusAddress = EVENT_BUS_DEPLOY_SUCCESS + "::" + this.mainId;
                    log.info("MainVerticle.EVENT_BUS_DEPLOY_SUCCESS address is " + deploySuccessEventBusAddress);
                    this.vertx.eventBus().publish(deploySuccessEventBusAddress, null);

                    log.info("监听配置改变的消息");
                    final String configChangedEventBusAddress = EVENT_BUS_CONFIG_CHANGED + "::" + this.mainId;
                    log.info("MainVerticle.EVENT_BUS_CONFIG_CHANGED address is " + configChangedEventBusAddress);
                    this.configChangedConsumer = this.vertx.eventBus().consumer(configChangedEventBusAddress, this::handleConfigChange);

                    log.info("是否开启 native transport: {}", vertx.isNativeTransportEnabled());
                    log.info("启动完成.");
                    if (startPromise != null) startPromise.complete();
                })
                .onFailure(err -> {
                    log.error("启动失败.", err);
                    if (startPromise != null) startPromise.fail(err);
                    this.vertx.close();
                });
    }

    /**
     * 添加guice模块
     *
     * @param guiceModules 添加guice模块到此列表
     */
    protected void addGuiceModules(final List guiceModules) {
    }

    /**
     * 部署前
     */
    protected void beforeDeploy() {
    }

    /**
     * 添加要部署的Verticle类列表
     *
     * @param verticleClasses 添加Verticle类到此列表
     */
    protected abstract void addVerticleClasses(Map> verticleClasses);

    @Override
    public void stop() {
        log.info("MainVerticle stop");
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy