一个简单的响应式接口开发框架vertx-framework

介绍

这是一个简单的响应式接口开发框架,基于对vertx的路由创建,服务注册,Verticle部署等基本操作的简单封装,让接口开发过程更接近于传统的开发模式,简化响应式接口开发流程,降低开发门槛。

框架目前接入了jdbc,redis,后续根据具体需要在接入其他组件,更多组件信息见:https://vertx.io/docs
源码地址:https://gitee.com/javacoo/vertx-framework

软件架构

软件架构说明

安装教程

  1. 引入pom

    <dependency>
                <groupId>org.javacoo.vertx</groupId>
                <artifactId>vertx-core</artifactId>
                <version>1.0</version>
            </dependency>
            <dependency>
                <groupId>org.javacoo.vertx</groupId>
                <artifactId>vertx-database</artifactId>
                <version>1.0</version>
            </dependency>
    

开发步骤

  1. 配置文件:

    主配置文件:application.yaml

    server:
      port: 8080
      contextPath: /
    redis:
      urls:
        - redis://127.0.0.1:6379/0
      clientType: STANDALONE
      poolName: p-red
      poolCleanerInterval: 30000
      maxPoolSize: 8
      maxPoolWaiting: 32
      password: 1239abcz
    vertx:
      eventLoopPoolSize: 2
      workerPoolSize: 4
      eventBusOptions:
        connectTimeout: 6000
    vertxScan:
      routerPackage: test.api.rest
      servicePackage: test.api.service
      serviceInstances: 1
    
    

    环境配置文件:application-cloud.yaml

    env: cloud
    dataSource:
      driverClass: org.postgresql.Driver
      url: jdbc:postgresql://127.0.0.1:9000/test?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8&tinyInt1isBit=false
      user: postgres
      password: 123456
      maxPoolSize: 30
    sqlConfig:
      init: SELECT 1=1
      # 总览-根据区域查询学校
      getSchoolsInfoByArea: SELECT t.xxmc schoolName, t.xxdz schoolAddress, t.xxbsm schoolCode, t.jgjd longitude, t.jgwd latitude FROM gis_school_location t WHERE t.xxbxlx = !{eduType} AND t.area_code LIKE !{queryAreaCode} LIMIT !{pageSize} OFFSET !{offset}
    
  2. 启动类

    public class ApiLauncher implements Launcher {
        private static final Logger LOGGER = LoggerFactory.getLogger(ApiLauncher.class);
        /**
         * 环境
         */
        private static String env = EnvEnum.CLOUD.getCode();
        /**
         * 服务端口
         */
        private static int serverPort = Constant.HTTP_PORT;
        private Vertx vertx;
        @Override
        public void start() {
            Vertx tempVertx = Vertx.vertx();
            // 加载配置
            ConfigRetriever retriever = initConfigRetriever(tempVertx);
    
            retriever.getConfig(json -> {
                tempVertx.close();
                if(json.succeeded()){
                    doStart(json);
                }else{
                    LOGGER.error("加载配置");
                }
            });
        }
        /**
         * 执行启动
         * <li></li>
         * @author duanyong
         * @date 2023/3/31 21:26
    
         * @param json
         * @return: void
         */
        private void doStart(AsyncResult<JsonObject> json){
            JsonObject envConfig = json.result();
            LOGGER.info("配置读取成功");
            //默认读取dev开发环境配置
            JsonObject vertxConfig = envConfig.getJsonObject("vertx");
            Vertx mainVertx = Vertx.vertx(new VertxOptions(vertxConfig));
            //初始化
            VertxHolder.init(mainVertx);
            //配置保存在共享数据中
            SharedData sharedData = mainVertx.sharedData();
            LocalMap<String, Object> localMap = sharedData.getLocalMap("/");
            localMap.put("env", env);
            localMap.put("envConfig", envConfig);
            //先初始化再发布Http服务
            mainVertx.executeBlocking(p -> {
                //顺序不能乱
                try {
                    //初始化ConfigProperties
                    ConfigPropertiesHolder.init(envConfig);
                    //初始化RedisClientHolder
                    RedisAPIHolder.init(mainVertx, envConfig, redisConnectionAsyncResult -> {
                        if(redisConnectionAsyncResult.succeeded()){
                            LOGGER.info("redis初始化成功");
                        }else{
                            LOGGER.error("redis初始化失败",redisConnectionAsyncResult.cause());
                        }
                    });
                    //初始化JDBCClient
                    JDBCClientHolder.init(envConfig, ready -> {
                        if (ready.succeeded()) {
                            p.complete();
                            LOGGER.info("JDBCClient初始化成功");
                        } else {
                            p.fail(ready.cause());
                            LOGGER.error("JDBCClient初始化失败",ready.cause());
                        }
                    });
                } catch (Exception e) {
                    LOGGER.error("初始化失败:{}",e);
                    p.fail(e);
                }
            }).onComplete(ar2 -> {
                if (ar2.succeeded()) {
                    JsonObject vertxScanConfig = envConfig.getJsonObject("vertxScan");
                    JsonObject serverConfig = envConfig.getJsonObject("server");
    
                    if(EnvEnum.CLOUD.getCode().equals(env)){
                        serverPort = serverConfig.getInteger("port");
                    }
                    LOGGER.info("准备启动服务,http服务端口:{}",serverPort);
                    EventConsumerConfig eventConsumerConfig = new EventConsumerConfig();
                    Router router = new RouterHandlerFactory(vertxScanConfig.getString("routerPackage"), serverConfig.getString("contextPath")).createRouter();
                    DeployVertxServer.startDeploy(eventConsumerConfig.consumer(),router, vertxScanConfig.getString("servicePackage"), serverPort, vertxScanConfig.getInteger("serviceInstances"));
                } else {
                    LOGGER.error(ar2.cause().getMessage(), ar2.cause());
                }
            });
        }
    
        @Override
        public void stop() {
            vertx.close();
        }
        /**
         * 初始化ConfigRetriever
         * <li></li>
         * @author duanyong
         * @date 2023/3/27 22:08
         * @param vertx
         * @return: ConfigRetriever
         */
        private ConfigRetriever initConfigRetriever(Vertx vertx) {
            //初始化
            ConfigRetrieverOptions options = initOptions();
            //创建
            return ConfigRetriever.create(vertx, options);
        }
        /**
         * 初始化ConfigRetrieverOptions
         * <li></li>
         * @author duanyong
         * @date 2023/3/27 23:08
         * @return: ConfigRetrieverOptions
         */
        private ConfigRetrieverOptions initOptions() {
            // 使用默认ConfigStore
            ConfigRetrieverOptions options = new ConfigRetrieverOptions().setIncludeDefaultStores(false);
            // 禁用配置刷新
            options.setScanPeriod(-1);
            //加载主配置
            options.addStore(new ConfigStoreOptions()
                    .setType("file")
                    .setFormat("yaml")
                    .setOptional(true)
                    .setConfig(new JsonObject().put("path", "application.yaml")));
            String envFile = new StringBuilder("application-").append(env).append(".yaml").toString();
            //加载环境配置
            options.addStore(new ConfigStoreOptions()
                    .setType("file")
                    .setFormat("yaml")
                    .setOptional(true)
                    .setConfig(new JsonObject().put("path", envFile)));
            // 禁用缓存
            options.getStores().forEach(store -> {
                store.getConfig().put("cache", "false");
            });
            return options;
        }
    
        public static void main(String[] args) {
            if (args.length >= 1 && !CONFIG_DEFAULT.equals(args[0])) {
                env = args[0];
            }
            if (args.length >= 2 && !CONFIG_DEFAULT.equals(args[1])) {
                serverPort = Integer.valueOf(args[1]);
            }
            Launcher launcher = new GisApiLauncher();
            launcher.start();
        }
    }
    
  3. 编写服务

    接口类:TestService

    /**
     * 测试服务接口
     * <li></li>
     *
     * @author duanyong
     * @version 1.0
     * @date 2023/6/24 22:17
     */
    @ProxyGen
    @VertxGen
    public interface TestService {
        @GenIgnore
        static test.api.reactivex.service.TestService createRxProxy() {
            return new test.api.reactivex.service.TestService(AsyncServiceUtil.getAsyncServiceInstance(TestService.class));
        }
        
        /**
         * 测试方法
         * <li></li>
         * @author duanyong
         * @date 2023/6/24 22:47
    
         * @param query
         * @param resultHandler
         * @return: OverviewService
         */
        @Fluent
        TestService test(JsonObject query,Handler<AsyncResult<List<JsonObject>>> resultHandler);
    }
    

    实现类:TestServiceImpl

    /**
     * 总览服务接口实现类
     * <li></li>
     *
     * @author duanyong
     * @version 1.0
     * @date 2023/6/24 23:20
     */
    public class TestServiceImpl extends BaseAsyncService implements TestService {
        protected ConfigProperties.SqlConfigProperties sqlConfig;
        protected JDBCClient dbClient;
        public OverviewServiceImpl() {
            sqlConfig = ConfigPropertiesHolder.getConfigProperties().getSqlConfig();
            dbClient = JDBCClientHolder.getJDBCClient();
        }
        @Override
        public TestService test(JsonObject query, Handler<AsyncResult<List<JsonObject>>> resultHandler) {
            String areaCode = getAreaCodePrefix(query);
            String eduType = query.getString("eduType");
            Integer pageSize = query.getInteger("pageSize");
            Integer pageNum = query.getInteger("pageNum");
    
            JsonObject result = parseTemplateSQL(sqlConfig.getGetSchoolsInfoByArea(),query);
            String finalSql = result.getString(SQL_KEY);
            JsonArray data = result.getJsonArray(PARAM_KEY);
            String cacheKey = new StringBuilder("getSchoolsInfoByArea_").append(areaCode).append(eduType).append(pageSize).append(pageNum).toString();
            loadListData(dbClient,resultHandler, finalSql, data, cacheKey);
            return this;
        }
    }
    
  4. 编写接口

    /**
     * 测试服务接口
     * <li></li>
     *
     * @author duanyong
     * @version 1.0
     * @date 2023/6/25 22:27
     */
    @RouteHandler
    public class TestApi extends BaseRestApi {
        /**
         * 测试服务
         */
        private TestService testService = test.api.service.TestService.createRxProxy();
       
        /**
         * 测试
         * @return
         */
        @RouteMapping(value = "/api/test", method = RouteMethod.POST)
        public Handler<RoutingContext> test() {
            return ctx -> {
                JsonObject query = ctx.body().asJsonObject();
                if (!validateJsonPageDocument(ctx, query, "areaCode","level")) {
                    return;
                }
                testService.rxTest(query)
                        .subscribe(totalList -> fireJsonResponse(ctx, new JsonResult(totalList)), t -> fireErrorJsonResponse(ctx, t.getMessage()));
            };
        }
    }
    

    至此便完成整个开发流程

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容