4.1.10 函数之触发与定时(改)

函数的触发和定时在很多场景中会用到,也是一个oinone的基础能力。比如我们的流程产品中在定义流程触发时就会让用户选择模型触发还是时间触发,就是用到了函数的触发与定时能力。

整体链路示意图(如下图4-1-10-1 所示),本文只讲trigger里的两类任务,一个是触发任务,一个是定时任务,异步任务放在4.1.11【函数之异步执行】一文中单独去介绍。

4.1.10 函数之触发与定时(改)

图4-1-10-1 整体链路示意图

一、触发任务TriggerTaskAction(举例)

触发任务的创建,使用pamirs-middleware-canal监听mysql的binlog事件,通过rocketmq发送变更数据消息,收到MQ消息后,创建TriggerAutoTask。

触发任务的执行,使用TBSchedule拉取触发任务后,执行相应函数。

注意:pamirs-middleware-canal监听的数据库表必须包含触发模型的数据库表。

Step1 下载canal中间件

下载pamirs-middleware-canal-deployer-3.0.1.zip,去.txt后缀为pamirs-middleware-canal-deployer-3.0.1.zip,解压文件如下:

4.1.10 函数之触发与定时(改)

图4-1-10-2 下载canal中间件

Step2 引入依赖pamirs-core-trigger模块

  1. pamirs-demo-api增加pamirs-trigger-api
<dependency>
        <groupId>pro.shushi.pamirs.core</groupId>
        <artifactId>pamirs-trigger-api</artifactId>
</dependency>

图4-1-10-3 pamirs-trigger-api依赖包

  1. DemoModule在模块依赖定义中增加@Module(dependencies={TriggerModule.MODULE_MODULE})
@Component
@Module(
        name = DemoModule.MODULE_NAME,
        displayName = "oinoneDemo工程",
        version = "1.0.0",
        dependencies = {ModuleConstants.MODULE_BASE, CommonModule.MODULE_MODULE, UserModule.MODULE_MODULE, TriggerModule.MODULE_MODULE}
)
@Module.module(DemoModule.MODULE_MODULE)
@Module.Advanced(selfBuilt = true, application = true)
@UxHomepage(PetShopProxy.MODEL_MODEL)
public class DemoModule implements PamirsModule {
    ……其他代码
}

图4-1-10-4 模块依赖中增加Trigger模块

  1. pamirs-demo-boot 增加pamirs-trigger-core和pamirs-trigger-bridge-tbschedule的依赖
<dependency>
        <groupId>pro.shushi.pamirs.core</groupId>
        <artifactId>pamirs-trigger-core</artifactId>
</dependency>
<dependency>
        <groupId>pro.shushi.pamirs.core</groupId>
        <artifactId>pamirs-trigger-bridge-tbschedule</artifactId>
</dependency>

图4-1-10-5 增加pamirs-trigger-core和pamirs-trigger-bridge-tbschedule的依赖

  1. 修改pamirs-demo-boot的applcation-dev.yml

    1. 修改pamris.event.enabled和pamris.event.schedule.enabled为true

    2. pamirs_boot_modules增加启动模块:trigger

pamirs: 
  event:
    enabled: true
    schedule:
      enabled: true
    rocket-mq:
      namesrv-addr: 127.0.0.1:9876
  boot:
    init: true
    sync: true
    modules:
      - base
      - common
      - sequence
      - resource
      - user
      - auth
      - message
      - international
      - business
      - trigger
      - demo_core

图4-1-10-6 启动模块中增加trigger模块

Step3 启动canal中间件

  1. canal的库表需要手工建
create schema canal_tsdb collate utf8mb4_bin

图4-1-10-7 canal的建库语句

CREATE TABLE IF NOT EXISTS `meta_snapshot` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `gmt_create` datetime NOT NULL COMMENT '创建时间',
  `gmt_modified` datetime NOT NULL COMMENT '修改时间',
  `destination` varchar(128) DEFAULT NULL COMMENT '通道名称',
  `binlog_file` varchar(64) DEFAULT NULL COMMENT 'binlog文件名',
  `binlog_offest` bigint(20) DEFAULT NULL COMMENT 'binlog偏移量',
  `binlog_master_id` varchar(64) DEFAULT NULL COMMENT 'binlog节点id',
  `binlog_timestamp` bigint(20) DEFAULT NULL COMMENT 'binlog应用的时间戳',
  `data` longtext DEFAULT NULL COMMENT '表结构数据',
  `extra` text DEFAULT NULL COMMENT '额外的扩展信息',
  PRIMARY KEY (`id`),
  UNIQUE KEY binlog_file_offest(`destination`,`binlog_master_id`,`binlog_file`,`binlog_offest`),
  KEY `destination` (`destination`),
  KEY `destination_timestamp` (`destination`,`binlog_timestamp`),
  KEY `gmt_modified` (`gmt_modified`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='表结构记录表快照表';

CREATE TABLE IF NOT EXISTS `meta_history` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `gmt_create` datetime NOT NULL COMMENT '创建时间',
  `gmt_modified` datetime NOT NULL COMMENT '修改时间',
  `destination` varchar(128) DEFAULT NULL COMMENT '通道名称',
  `binlog_file` varchar(64) DEFAULT NULL COMMENT 'binlog文件名',
  `binlog_offest` bigint(20) DEFAULT NULL COMMENT 'binlog偏移量',
  `binlog_master_id` varchar(64) DEFAULT NULL COMMENT 'binlog节点id',
  `binlog_timestamp` bigint(20) DEFAULT NULL COMMENT 'binlog应用的时间戳',
  `use_schema` varchar(1024) DEFAULT NULL COMMENT '执行sql时对应的schema',
  `sql_schema` varchar(1024) DEFAULT NULL COMMENT '对应的schema',
  `sql_table` varchar(1024) DEFAULT NULL COMMENT '对应的table',
  `sql_text` longtext DEFAULT NULL COMMENT '执行的sql',
  `sql_type` varchar(256) DEFAULT NULL COMMENT 'sql类型',
  `extra` text DEFAULT NULL COMMENT '额外的扩展信息',
  PRIMARY KEY (`id`),
  UNIQUE KEY binlog_file_offest(`destination`,`binlog_master_id`,`binlog_file`,`binlog_offest`),
  KEY `destination` (`destination`),
  KEY `destination_timestamp` (`destination`,`binlog_timestamp`),
  KEY `gmt_modified` (`gmt_modified`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='表结构变化明细表';

CREATE TABLE IF NOT EXISTS `canal_filter` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `destination` varchar(128) NOT NULL COMMENT '通道名称',
  `filter` text NOT NULL COMMENT '过滤表达式',
  `create_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `write_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY destination(`destination`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='通道过滤';

CREATE TABLE IF NOT EXISTS `canal_destination` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `destination` varchar(128) NOT NULL COMMENT '通道名称',
  `content` text NOT NULL COMMENT '通道配置内容',
  `create_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `write_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `destination` (`destination`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='通道配置'

图4-1-10-8 canal的建表语句

  1. 修改canal的启动配置

    1. canal_tsdb就是上面建库,用户名和密码换成本机的

    2. 配置filter: demo.demo_core_pet_talent,监听PetTalent模型数据变化,filter为canal第一次启动默认监听的表。如果数据库中canal_filter表有数据这个修改无效

    3. filter目前需要手工配置,在下个版本中已经去掉了手工配置,而且文中的canal中间件已经是2.2.2版本了,兼容当前教程的版本

pamirs:
  middleware:
    data-source:
      jdbc-url: jdbc:mysql://localhost:3306/canal_tsdb?useUnicode=true&characterEncoding=utf-8&verifyServerCertificate=false&useSSL=false&requireSSL=false
      driver-class-name: com.mysql.cj.jdbc.Driver
      username: root
      password: oinone
  canal:
    ip: 127.0.0.1
    port: 1111
    metricsPort: 1112
    zkClusters:
      - 127.0.0.1:2181
    destinations:
      - destinaion: pamirschangedata
        name: pamirschangedata
        desc: pamirschangedata
        slaveId: 1235
        filter: demo\.demo_core_pet_talent
        dbUserName: root
        dbPassword: oinone
        memoryStorageBufferSize: 65536
        topic: CHANGE_DATA_EVENT_TOPIC
        dynamicTopic: false
        dbs:
          - { address: 127.0.0.1, port: 3306 }
    tsdb:
      enable: true
      jdbcUrl: "jdbc:mysql://127.0.0.1:3306/canal_tsdb"
      userName: root
      password: oinone
    mq: rocketmq
    rocketmq:
      namesrv: 127.0.0.1:9876
      retryTimesWhenSendFailed: 5
dubbo:
  application:
    name: canal-server
    version: 1.0.0
  registry:
    address: zookeeper://127.0.0.1:2181
  protocol:
    name: dubbo
    port: 20881
  scan:
    base-packages: pro.shushi  
server:
  address: 0.0.0.0
  port: 10010
  sessionTimeout: 3600

图4-1-10-9 修改canal的启动配置

pamirs.canal相关配置说明

canal.ip: 运行时ip

canal.port: 运行时端口

canal.zkClusters: 连接zookeeper集群地址

canal.destinations: 运行时Canal的所有实例

canal.destinations.destinaion: Canal的单个实例配置值为所有实例唯一

canal.destinations.destinaion.id: 与canal.destinations.destinaion.slaveId配置一致

canal.destinations.destinaion.slaveId: 为Canal的实例ID

canal.destinations.destinaion.filter: 为Canal监听过滤正则

canal.destinations.destinaion.dbUserName: 为Canal监听的MySQL的用户名

canal.destinations.destinaion.dbPassword: 为Canal监听的MySQL的用户密码

canal.destinations.destinaion.topic: 为监听到数据后往RocketMQ的指定Topic发送消息

canal.destinations.destinaion.dbs: 为连接的MySQL实例的ip和端口,如果为主从配置且主从都开启了Binlog同步功能则可以配置两个地址

canal.destinations.destinaion.memoryStorageBufferSize与canal.destinations.destinaion.dynamicTopic为固定值不需要更改

canal.tsdb: 用来保存canal运行时的元数据,配置为canal_tsdb库相关的连接信息

  1. 启动canal中间件

    1. 进入canal中间件解压目录执行下面命令就可以启动

    2. --spring.config.location请配置绝对路径,换成自己本机的就可以

java -jar  pamirs-middleware-canal-deployer-3.0.1-SNAPSHOT.jar --spring.config.location=/Users/oinone/Documents/oinone/canel/pamirs-middleware-canal-deployer-3.0.1/canal-config/application-dev.yml --spring.profiles.active=dev

图4-1-10-10 启动canal中间件

Step4 新建触发任务

新建PetTalentTrigger类,当PetTalent模型的数据记录被新建时触发系统做一些事情

package pro.shushi.pamirs.demo.core.trigger;

import pro.shushi.pamirs.demo.api.model.PetTalent;
import pro.shushi.pamirs.meta.annotation.Fun;
import pro.shushi.pamirs.meta.annotation.Function;
import pro.shushi.pamirs.meta.annotation.fun.extern.Slf4j;
import pro.shushi.pamirs.trigger.annotation.Trigger;
import pro.shushi.pamirs.trigger.enmu.TriggerConditionEnum;

@Fun(PetTalent.MODEL_MODEL)
@Slf4j
public class PetTalentTrigger {
    @Function
    @Trigger(displayName = "PetTalent创建时触发",name = "PetTalent#Trigger#onCreate",condition = TriggerConditionEnum.ON_CREATE)
    public PetTalent onCreate(PetTalent data){
        log.info(data.getName() + ",被创建");
        //可以增加逻辑
        return data;
    }
}

图4-1-10-11 新建触发任务

Step5 重启应用看效果

  1. 解决启动是dubbo报错

启动过程中会报如下错误,虽然不影响结果,但是还是要把它消灭掉。修改bootstarp.yml文件关闭SpringCloud的自动注册就好了

4.1.10 函数之触发与定时(改)

图4-1-10-12 解决启动是dubbo报错

spring:
  profiles:
    active: dev
  application:
    name: pamirs-demo
  cloud:
    service-registry:
        auto-registration:
        enabled: false
pamirs:
  default:
    environment-check: true
    tenant-check: true

---
spring:
  profiles: dev
  cloud:
    config:
      enabled: false
      uri: http://127.0.0.1:7001
      label: master
      profile: dev
    nacos:
      server-addr: http://127.0.0.1:8848
      discovery:
        enabled: false
        namespace:
        prefix: application
        file-extension: yml
      config:
        enabled: false
        namespace:
        prefix: application
        file-extension: yml

dubbo:
  application:
    name: pamirs-demo
    version: 1.0.0
  registry:
    address: zookeeper://127.0.0.1:2181
  protocol:
    name: dubbo
    port: -1
  #    serialization: pamirs
  scan:
    base-packages: pro.shushi
  cloud:
    subscribed-services:

---
spring:
  profiles: test
  cloud:
    config:
      enabled: false
      uri: http://127.0.0.1:7001
      label: master
      profile: test
    nacos:
      server-addr: http://127.0.0.1:8848
      discovery:
        enabled: false
        namespace:
        prefix: application
        file-extension: yml
      config:
        enabled: false
        namespace:
        prefix: application
        file-extension: yml

dubbo:
  application:
    name: pamirs-demo
    version: 1.0.0
  registry:
    address: zookeeper://127.0.0.1:2181
  protocol:
    name: dubbo
    port: -1
  #    serialization: pamirs
  scan:
    base-packages: pro.shushi
  cloud:
    subscribed-services:

图4-1-10-13 关闭SpringCloud的自动注册

  1. 再次重启查看效果

前端新增一个宠物达人,在后台Console搜索“被创建”,我们就看到对应由触发器打印出来的日志

4.1.10 函数之触发与定时(改)

图4-1-10-14 示例效果

4.1.10 函数之触发与定时(改)

图4-1-10-15 示例效果

Step6 修改canal的Topic

在分布式环境下可以通过修改canal的topic(canal.destinations.destinaion.topic)来个隔离多个应用的触发消息。

  1. 新建DemoNotifyTopicEdit利用Topic修改api,增加后缀【"_"+ DemoModule.MODULE_MODULE】

  2. canal的配置canal.destinations.destinaion.topic改为:CHANGE_DATA_EVENT_TOPIC_demo_core

  3. 小伙伴自行测试

package pro.shushi.pamirs.demo.core.mq;

import org.springframework.stereotype.Component;
import pro.shushi.pamirs.demo.api.DemoModule;
import pro.shushi.pamirs.framework.connectors.event.spi.NotifyTopicEditorApi;
import pro.shushi.pamirs.trigger.constant.NotifyConstant;

@Component
public class DemoNotifyTopicEdit  implements NotifyTopicEditorApi {
    @Override
    public String handlerTopic(String topic) {
        if(NotifyConstant.AUTO_TRIGGER_TOPIC.equals(topic)){
            return NotifyConstant.AUTO_TRIGGER_TOPIC +"_"+ DemoModule.MODULE_MODULE;
        }
        return topic;
    }
}

图4-1-10-16 修改canal的Topic

pamirs:
  middleware:
    data-source:
      jdbc-url: jdbc:mysql://localhost:3306/canal_tsdb?useUnicode=true&characterEncoding=utf-8&verifyServerCertificate=false&useSSL=false&requireSSL=false
      driver-class-name: com.mysql.cj.jdbc.Driver
      username: root
      password: oinone
  canal:
    ip: 127.0.0.1
    port: 1111
    metricsPort: 1112
    zkClusters:
      - 127.0.0.1:2181
    destinations:
      - destinaion: pamirschangedata
        name: pamirschangedata
        desc: pamirschangedata
        slaveId: 1235
        filter: demo\.demo_core_pet_talent
        dbUserName: root
        dbPassword: oinone
        memoryStorageBufferSize: 65536
        topic: CHANGE_DATA_EVENT_TOPIC_demo_core
        dynamicTopic: false
        dbs:
          - { address: 127.0.0.1, port: 3306 }
    tsdb:
      enable: true
      jdbcUrl: "jdbc:mysql://127.0.0.1:3306/canal_tsdb"
      userName: root
      password: oinone
    mq: rocketmq
    rocketmq:
      namesrv: 127.0.0.1:9876
      retryTimesWhenSendFailed: 5
dubbo:
  application:
    name: canal-server
    version: 1.0.0
  registry:
    address: zookeeper://127.0.0.1:2181
  protocol:
    name: dubbo
    port: 20881
  scan:
    base-packages: pro.shushi  
server:
  address: 0.0.0.0
  port: 10010
  sessionTimeout: 3600

图4-1-10-17 修改canal.destinations.destinaion.topic配置

二、定时任务

定时任务是一种非常常见的模式,这里就不介绍概念了,直接进入示例环节

Step1 新建PetTalentAutoTask实现ScheduleAction

注:

  1. getInterfaceName()需要跟taskAction.setExecuteNamespace定义保持一致,都是函数的命名空间

  2. taskAction.setExecuteFun("execute");跟执行函数名“execute”一致

  3. TaskType需配置为CYCLE_SCHEDULE_NO_TRANSACTION_TASK,把定时任务的schedule线程分开,要不然有一个时间长的任务会导致普通异步或触发任务全部延时

package pro.shushi.pamirs.demo.core.task;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import pro.shushi.pamirs.core.common.enmu.TimeUnitEnum;
import pro.shushi.pamirs.demo.api.model.PetTalent;
import pro.shushi.pamirs.meta.annotation.Fun;
import pro.shushi.pamirs.meta.annotation.Function;
import pro.shushi.pamirs.meta.annotation.fun.extern.Slf4j;
import pro.shushi.pamirs.meta.domain.fun.FunctionDefinition;
import pro.shushi.pamirs.middleware.schedule.api.ScheduleAction;
import pro.shushi.pamirs.middleware.schedule.common.Result;
import pro.shushi.pamirs.middleware.schedule.domain.ScheduleItem;
import pro.shushi.pamirs.middleware.schedule.eunmeration.TaskType;
import pro.shushi.pamirs.trigger.enmu.TriggerTimeAnchorEnum;
import pro.shushi.pamirs.trigger.model.ScheduleTaskAction;
import pro.shushi.pamirs.trigger.service.ScheduleTaskActionService;

@Slf4j
@Component
@Fun(PetTalent.MODEL_MODEL)
public class PetTalentAutoTask implements ScheduleAction {

    @Autowired
    private ScheduleTaskActionService scheduleTaskActionService;

    public void initTask(){
        ScheduleTaskAction taskAction = new ScheduleTaskAction();
        taskAction.setDisplayName("定时任务测试"); //定时任务描述
        taskAction.setDescription("定时任务测试");
        taskAction.setTechnicalName(PetTalent.MODEL_MODEL+"#"+PetTalentAutoTask.class.getSimpleName()+"#"+"testAutoTask");       //设置定时任务技术名
        taskAction.setLimitExecuteNumber(-1);   //设置执行次数
        taskAction.setPeriodTimeValue(1);       //设置执行周期规则
        taskAction.setPeriodTimeUnit(TimeUnitEnum.MINUTE);
        taskAction.setPeriodTimeAnchor(TriggerTimeAnchorEnum.START);
        taskAction.setLimitRetryNumber(1);      //设置失败重试规则
        taskAction.setNextRetryTimeValue(1);
        taskAction.setNextRetryTimeUnit(TimeUnitEnum.MINUTE);
        taskAction.setExecuteNamespace(PetTalent.MODEL_MODEL);
        taskAction.setExecuteFun("execute");
        taskAction.setExecuteFunction(new FunctionDefinition().setTimeout(5000));
        taskAction.setTaskType(TaskType.CYCLE_SCHEDULE_NO_TRANSACTION_TASK.getValue()); //设置定时任务,执行任务类型
        taskAction.setContext(null);            //用户传递上下文参数
        taskAction.setActive(true);             //定时任务是否生效
        taskAction.setFirstExecuteTime(System.currentTimeMillis());
        scheduleTaskActionService.submit(taskAction);//初始化任务,幂等可重复执行
    }

    @Override
    public String getInterfaceName() {return PetTalent.MODEL_MODEL;}

    @Override
    @Function
    public Result<Void> execute(ScheduleItem item) {
        log.info("testAutoTask,上次执行时间"+item.getLastExecuteTime());
        return new Result<>();
    }
}

图4-1-10-18 新建PetTalentAutoTask实现ScheduleAction

Step2 修改DemoModuleBizInit,进行定时任务初始化

模块更新的时候调用
petTalentAutoTask.initTask(),initTask本身是幂等的所以多掉几次没有关系。在4.1.3【模块之生命周期】一文介绍过InstallDataInit、UpgradeDataInit、ReloadDataInit,您有兴趣可以去回顾下。

package pro.shushi.pamirs.demo.core.init;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import pro.shushi.pamirs.boot.common.api.command.AppLifecycleCommand;
import pro.shushi.pamirs.boot.common.api.init.InstallDataInit;
import pro.shushi.pamirs.boot.common.api.init.ReloadDataInit;
import pro.shushi.pamirs.boot.common.api.init.UpgradeDataInit;
import pro.shushi.pamirs.demo.api.DemoModule;
import pro.shushi.pamirs.demo.api.enumeration.DemoExpEnumerate;
import pro.shushi.pamirs.demo.core.task.PetTalentAutoTask;
import pro.shushi.pamirs.meta.common.exception.PamirsException;

import java.util.Collections;
import java.util.List;

@Component
public class DemoModuleBizInit implements InstallDataInit,
    UpgradeDataInit, ReloadDataInit {

    @Autowired
    private PetTalentAutoTask petTalentAutoTask;

    @Override
    public boolean upgrade(AppLifecycleCommand command, String version, 
                           String existVersion) {
        petTalentAutoTask.initTask(); //初始化petTalent的定时任务
        return Boolean.TRUE;
    }

    @Override
    public List<String> modules() {
        return Collections.singletonList(DemoModule.MODULE_MODULE);
    }

    @Override
    public int priority() {return 0;}
}

图4-1-10-19 修改DemoModuleBizInit,进行定时任务初始化

Step3 重启看效果

4.1.10 函数之触发与定时(改)

图4-1-10-20 示例效果

Oinone社区 作者:史, 昂原创文章,如若转载,请注明出处:https://doc.oinone.top/oio4/9285.html

访问Oinone官网:https://www.oinone.top获取数式Oinone低代码应用平台体验

(0)
史, 昂的头像史, 昂数式管理员
上一篇 2024年5月23日 am8:53
下一篇 2024年5月23日 am8:55

相关推荐

  • 4.1.1 模块之yml文件结构详解

    本节是对demo的boot工程的application-*.yml文件关于oinone相关配置的扩充讲解,大家可以先通读留个影响,以备不时之需 在基础入门的模块一章中大家构建,并通过启动前后应用,直观地感受到我们自己建的demo模块。在上述过程中想必大家都了解到我们oinone的boot工程是专门用来做应用启动管理,它完全没有任何业务逻辑,它只决定启动哪些模块、启动方式、以及相关配置。它跟Spring Boot的一个普通工程没有什么差异。所有我们只要看application-*.yml文件,oinone提供了哪些特殊配置就能窥探一二。 这里主要介绍pamirs路径下的核心以及常用的配置项 一、pamirs.boot pamirs.boot.init 描述:启动加载程序,是否启动元数据、业务数据和基础设施的加载与更新程序,在应用启动时同时对模块进行生命周期管理 true ##标准版,只支持true pamirs.boot.sync 描述:同步执行加载程序,启动时对模块进行生命周期管理采用同步方式 true ##标准版,只支持true pamirs.boot.modules a. 描述:启动模块列表。这里只有base模块是必须的。为了匹配我们的前端模版,在demo的例子中加入了其他几个通用业务模块。当然这些通用业务模块也是可以大大降低大家的开发难度以及提升业务系统的设计质量 b. – base #oinone的基础模块 c. – common #oinone的一些基础辅助功能 d. – sequence #序列的能力 e. – resource #基础资源如 f. – user #基础用户 g. – auth #权限 h. – message #消息 i. – international #国际化 j. – business #商业关系 k. – file #文件,demo里没有默认加入,如果要开发导入导出相关功能,可以对应引入改模块 l. – …… 还有很多通用业务模块以及这些模块的详细介绍,我们在介绍第六章【oinone的通用能力】的章节去展开 pamirs.boot.mode ⅰ. dev:不走缓存,可以直接修改元数据。特别是我们在说页面设计的时候,可以修改base_view表直接生效不需要重启系统 配置举例 pamirs: boot: init: true sync: true modules: – base – common – sequence – resource – user – auth – message – international – business – demo_core 图4-1-1-1 pamirs.boot.mode配置举例 二、pamirs.boot.profile与pamirs.boot.options pamirs.boot.option, 在pamirs.boot.options中可以自定义可选项,也可以根据pamirs.boot.profile属性来指定这些可选项,pamirs.boot.profile属性的默认值为CUSTOMIZE。只有pamirs.boot.profile=CUSTOMIZE时,才能在pamirs.boot.options中自定义可选项。 可选项 说明 默认值 AUTO READONLY PACKAGE DDL reloadModule 是否加载存储在数据库中的模块信息 false true true true true checkModule 校验依赖模块是否安装 false true true true true loadMeta 是否扫描包读取模块元数据 true true false true true reloadMeta 是否加载存储在数据库中元数据 false true true true true computeMeta 是否重算元数据 true true false true true editMeta 编辑元数据,是否支持编程式编辑元数据 true true false true true diffMeta 差量减计算元数据 false true false true false refreshSessionMeta 刷新元数据缓存 true true true true true rebuildHttpApi 刷新重建前后端协议 true true true false false diffTable 差量追踪表结构变更 false true false true false rebuildTable 更新重建表结构 true true false true false…

    2024年5月23日
    1.3K00
  • 3.5.4 Ux注解详解

    我们默认视图已经基本可以用了,但实际业务中还是会有一些不大不小的自定义需求,写自定义视图又太麻烦,今天我们来学习一种更加轻量的模式即:后端研发可以通过注解来配置视觉交互。该系列注解以Ux开头,例如@UxHomepage、@UxMenu、@UxAction、@UxView、@UxWidget等等。 视图XML的配置优先级大于在代码上的注解,也就是代码上的注解影响的是默认展示逻辑。 一、Ux家族图谱 我们先简单通过家族图谱做个简单了解,脑海里有一个影响当有需要的时候知道能不能做,深入了解还需要大家多多动手去尝试 图3-5-4-1 Ux家族图谱 二、默认视图后端配置举例 在下面的代码片段中UxTable、UxForm、UxDetail、UxTableSearch都有涉及,几个特殊点做些解释其他的留大家自行测试 Group分组的配置逻辑:为了不让一个分组内的字段不断的写Group,所以采取了第一个字段写了Group,到下一个出现的group之间的字段都自动归为一个Group 搜素整体不展示可以用“@UxTable(enableSearch = false)”配置在模型的类上。 字段搜索用“UxTableSearch”配置在模型的字段上,其特殊逻辑是只要你配了一个字段,系统就不自动补充了,例子中表格页的搜索栏只会留下店铺名称和店铺编码 ……其他代码 //@UxTable(enableSearch = false),整体不支持搜索 public class PetShop extends AbstractDemoIdModel { public static final String MODEL_MODEL="demo.PetShop"; @Field(displayName = "店铺编码") @UxForm.FieldWidget(@UxWidget(group = "Form基础数据"))//Form分组 @UxTableSearch.FieldWidget(@UxWidget())//支持搜索 private String code; @Field(displayName = "店铺编码2") @Field.Sequence(sequence = "DATE_ORDERLY_SEQ",prefix = "C",size=6,step=1,initial = 10000,format = "yyyyMMdd") private String codeTwo; @UxTableSearch.FieldWidget(@UxWidget())//支持搜索 @UxTable.FieldWidget(@UxWidget(invisible = "true"))//表格中不展示支持搜索 @Field(displayName = "店铺名称",required = true,immutable=true) private String shopName; @Field(displayName = "一年内新店") @UxForm.FieldWidget(@UxWidget(widget = "Switch",group = "Form基础数据"))//Switch,Checkbox可以切换着看,字段可选widget参考【字段的配置】一文 private Boolean oneYear; @Field(displayName = "开店时间",required = true) @UxDetail.FieldWidget(@UxWidget(invisible = "true"))//详情不展示 private Time openTime; @Field(displayName = "闭店时间",required = true) @UxDetail.FieldWidget(@UxWidget(invisible = "true"))//详情不展示 private Time closeTime; …… 其他代码 } 图3-5-4-2 默认视图后端配置举例

    2024年5月23日
    1.1K00
  • 4.1.21 框架之分布式消息

    消息中间件是在分布式开发中常见的一种技术手段,用于模块间的解耦、异步处理、数据最终一致等场景。 一、介绍 oinone对开源的RocketMQ进行了封装,是平台提供的一种较为简单的使用方式,并非是对RocketMQ进行的功能扩展。同时也伴随着两个非常至关重要的目的: 适配不同企业对RocketMQ的不同版本选择,不至于改上层业务代码。目前已经适配RocketMQ的开源版本和阿里云版本。 下个版本会对API进行升级支持不同类型MQ,以适配不同企业对MQ的不同要求,应对一些企业客户已经对MQ进行技术选择 对协议头进行扩展:如多租户的封装,saas模式中为了共用MQ基础资源,需要在消息头中加入必要租户信息。 二、使用准备 demo工程默认已经依赖消息,这里只是做介绍无需大家额外操作,大家可以用maven依赖树命令查看引用关系。 依赖包 增加对pamirs-connectors-event的依赖 <dependency> <groupId>pro.shushi.pamirs.framework</groupId> <artifactId>pamirs-connectors-event</artifactId> </dependency> 图4-1-21-1 分布式消息的依赖包 相关功能引入 增加模型、触发器都依赖MQ <!– 增强模型 –> <!– 增强模型 –> <dependency> <groupId>pro.shushi.pamirs.core</groupId> <artifactId>pamirs-channel</artifactId> </dependency> <!– 触发器 api –> <dependency> <groupId>pro.shushi.pamirs.core</groupId> <artifactId>pamirs-trigger-api</artifactId> </dependency> <!– 触发器 core –> <dependency> <groupId>pro.shushi.pamirs.core</groupId> <artifactId>pamirs-trigger-core</artifactId> </dependency> 图4-1-21-2 增加模型、触发器都依赖MQ yml配置文件参考 详见4.1.1【模块之yml文件结构详解】的“pamirs.event”部分。 三、使用说明 发送消息(NotifyProducer) 概述 NotifyProducer是Pamirs Event中所有生产者的基本API,它仅仅定义了消息发送的基本行为,例如生产者自身的属性,启动和停止,当前状态,以及消息发送方法。它本身并不决定消息如何发送,而是根据具体的实现确定其功能。 目前仅实现了RocketMQProducer,你可以使用下面介绍的方法轻松使用这些功能。 使用方法 Notify注解方式 使用示例 @Component public class DemoProducer { @Notify(topic = "test", tags = "model") public DemoModel sendModel() { return new DemoModel(); } @Notify(topic = "test", tags = "dto") public DemoDTO sendDTO() { return new DemoDTO(); } } 图4-1-21-3 Notify注解方式使用示例 解释说明 使用Component注解方式注册Spring Bean。 Notify注解指定topic和tags。 topic和tags对应NotifyEvent中的topic和tags。 RocketMQProducer方法调用 使用示例 @Component public class SendRocketMQMessage { @Autowired private RocketMQProducer rocketMQProducer; /** * 发送普通消息 */ public void sendNormalMessage() { rocketMQProducer.send(new NotifyEvent("test", "model", new DemoModel())); rocketMQProducer.send(new NotifyEvent("test", "dto", new DemoDTO())); } /** * 发送有序消息 */ public void sendOrderlyMessage() { DemoModel data = new DemoModel(); data.setAge(10); rocketMQProducer.send(new NotifyEvent("test", "model", data) .setQueueSelector((queueSize, event) -> { DemoModel body = (DemoModel) event.getBody(); return body.getAge() % queueSize; })); } /** * 发送事务消息 */ public void sendTransactionMessage() { rocketMQProducer.send(new NotifyEvent("test", "model", new DemoModel()) .setIsTransaction(true) .setGroup("demoTransactionListener")); } } 图4-1-21-4 RocketMQProducer方法调用…

    2024年5月23日
    1.0K00
  • 蒋江伟

    企业数字化转型经过多年演进,其趋势价值已经毋庸置疑。近些年来,随着流媒体平台的崛起,对企业的营销方式、渠道建设方式甚至供应链都带来了新的挑战,我们可以清晰地感觉到世界每时每刻都在发生变化。在未来的企业竞争中,谁数字化走在前沿,谁就更能掌握主动权。数字化是为了满足业务的持续创新,只有持续创新才能更好的迎接未知变化。而过去很多企业的技术路径是一个采购型的发展路径,买来的ERP和CRM,升级都是各自管各自的,有一天推出一个新概念或者业务发生新需求,又去采购另外一家企业的ERP和CRM,整个替换掉了,烟囱式地迭代演进模式。企业不怕重复建设,怕的是不断重复建设,企业不怕系统延期上线,怕的是错过业务发展的机会窗口。 本书主要介绍了一种全新的数字化构建理念和技术落地方式——用低代码的方式一站式支撑企业的商业场景并能满足商业化持续创新,和其他低代码不同的是:既结合了中台架构,又兼顾了传统企业的IT发展水平,更符合企业数字化发展需求,持续保持企业竞争力,对各行业在做数字化选型的时候有很大的帮助。 很高兴看到阿里校友陈鹏程(本书作者)在这条路上发光发热,也把此书推荐给IT从业者、程序员以及爱好计算机应用软件的所有同学,希望对大家学习新型、更高效的系统构建方式有所启发。 阿里巴巴高级研究员 蒋江伟(小邪)

    Oinone 7天入门到精通 2024年5月23日
    2.1K00
  • 3.4.3.2 面向切面-拦截器

    一、拦截器 拦截器为平台满足条件的函数以非侵入方式根据优先级扩展函数执行前和执行后的逻辑。 使用方法上的@Hook注解可以标识方法为拦截器。前置扩展点需要实现HookBefore接口;后置扩展点需要实现HookAfter接口。入参包含当前拦截函数定义与该函数的入参。拦截器可以根据函数定义与入参增加处理逻辑。 拦截器分为前置拦截器和后置拦截器,前者的出入参为所拦截函数的入参,后者的出入参为所拦截函数的出参。可以使用@Hook注解或Hook模型的非必填字段module、model、fun、函数类型、active来筛选出对当前拦截方法所需要生效的拦截器。若未配置任何过滤属性,拦截器将对所有函数生效。 根据拦截器的优先级priority属性可以对拦截器的执行顺序进行调整。priority数字越小,越先执行。 二、前置拦截(举例) 增加一个前置拦截,对PetShop的sayHello函数进行前置拦截,修改函数的入参的shopName属性,在其前面增加"hookbefore:"字符串。并查看效果 Step1 新增PetShopSayHelloHookBefore实现HookBefore接口 为run方法增加@Hook注解 配置module={DemoModule.MODULE_MODULE},这里module代表的是执行模块,该Hook只匹配由DemoModule模块为发起入口的请求 配置model={PetShop.MODEL_MODEL},该Hook只匹配PetShop模型 配置fun={"sayHello"},该Hook只匹配函数编码为sayHello的函数 package pro.shushi.pamirs.demo.core.hook; import org.springframework.stereotype.Component; import pro.shushi.pamirs.demo.api.DemoModule; import pro.shushi.pamirs.demo.api.model.PetShop; import pro.shushi.pamirs.meta.annotation.Hook; import pro.shushi.pamirs.meta.api.core.faas.HookBefore; import pro.shushi.pamirs.meta.api.dto.fun.Function; @Component public class PetShopSayHelloHookBefore implements HookBefore { @Override @Hook(module = {DemoModule.MODULE_MODULE},model = {PetShop.MODEL_MODEL},fun = {"sayHello"}) public Object run(Function function, Object… args) { if(args!=null && args[0]!=null){ PetShop arg = (PetShop)args[0]; arg.setShopName("hookbefore:"+ arg.getShopName()); } return args; } } 图3-4-3-5 新增PetShopSayHelloHookBefore实现HookBefore接口 Step2 重启查看效果 用graphQL工具Insomnia查看效果,如果访问提示未登陆,则请先登陆。详见3.4.1【构建第一个Function】一文 用 http://127.0.0.1:8090/pamirs/base 访问,结果我们会发现PetShopSayHelloHookBefore不起作用。是因为本次请求是以base模块作为发起模块,而我们用module={DemoModule.MODULE_MODULE}声明了该Hook只匹配由DemoModule模块为发起入口的请求 图3-4-3-6 示例效果 用 http://127.0.0.1:8090/pamirs/demoCore 访问,前端是以模块名作为访问入口不是模块编码这里大家要注意下 图3-4-3-7 示例效果 用 http://127.0.0.1:8090/pamirs/demoCore 访问,更换到petShop的子模型petShopProxy来访问sayHello函数,结果我们发现是没有效果的。因为配置model={PetShop.MODEL_MODEL},该Hook只匹配PetShop模型 三、后置拦截(举例) 增加一个后置拦截,对PetShop的sayHello函数进行后置拦截,修改函数的返回结果的shopName属性,在其后面增加"hookAfter:"字符串。并查看效果 Step1 新增PetShopSayHelloHookAfter实现HookAfter接口 为run方法增加@Hook注解 配置model={PetShop.MODEL_MODEL},该Hook只匹配PetShop模型 配置fun={"sayHello"},该Hook只匹配函数编码为sayHello的函数 package pro.shushi.pamirs.demo.core.hook; import org.springframework.stereotype.Component; import pro.shushi.pamirs.demo.api.model.PetShop; import pro.shushi.pamirs.meta.annotation.Hook; import pro.shushi.pamirs.meta.api.core.faas.HookAfter; import pro.shushi.pamirs.meta.api.dto.fun.Function; @Component public class PetShopSayHelloHookAfter implements HookAfter { @Override @Hook(model = {PetShop.MODEL_MODEL},fun = {"sayHello"}) public Object run(Function function, Object ret) { if (ret == null) { return null; } PetShop result =null; if (ret instanceof Object[]) { Object[] rets = (Object[])((Object[])ret); if (rets.length == 1) { result = (PetShop)rets[0]; } } else { result = (PetShop)ret; } result.setShopName(result.getShopName()+":hookAfter"); return result; } } 3-4-3-8 新增PetShopSayHelloHookAfter实现HookAfter接口 Step2 重启查看效果 用 http://127.0.0.1:8090/pamirs/base 访问,结果我们会发现PetShopSayHelloHookAfter是起作用。PetShopSayHelloHookBefore没有配置模块过滤。 3-4-3-9 示例效果 用访问,结果我们会发现PetShopSayHelloHookAfte和PetShopSayHelloHookBefore同时起作用 3-4-3-10 示例效果 我们会发现HookAfter只对结果做了修改,所以message中可以看到hookbefore,但看不到hookAfter 四、注意点 不管前置拦截器,还是后置拦截器都可以配置多个,根据拦截器的优先级priority属性可以对拦截器的执行顺序进行调整。priority数字越小,越先执行。小伙伴们可以自行尝试 拦截器必须是jar依赖,不然执行会报错。特别是有的小伙伴配置了一个没有过滤条件的拦截器,就要非常小心 模块启动yml文件可以过滤不需要执行的hook,具体配置详见4.1.1【模块之yml文件结构详解】一文 调用入口不是由前端发起而是后端编程中直接调用,默认不会生效,如果要生效请参考4.1.9【函数之元位指令】一文

    2024年5月23日
    1.2K00

Leave a Reply

登录后才能评论