Oinone环境保护(v5.2.3以上)

概述

Oinone平台为合作伙伴提供了环境保护功能,以确保在一套环境可以在较为安全前提下修改配置文件,启动多个JVM等部署操作。

本章内容主要介绍与环境保护功能相关的启动参数。

名词解释

  • 本地开发环境:开发人员在本地启动业务工程的环境
  • 公共环境:包含设计器镜像和业务工程的环境

环境保护参数介绍

【注意】参数是加在程序实参 (Program arguments)上,通常可能错误的加在Active Profiles上了
Oinone环境保护(v5.2.3以上)

-PenvProtected=${value}

是否启用环境保护,默认为true

环境保护是通过与最近一次保存在数据库的base_platform_environment表中数据进行比对,并根据每个参数的配置特性进行判断,在启动时将有错误的内容打印在启动日志中,以便于开发者进行问题排查。

除此之外,环境保护功能还提供了一些生产配置的优化建议,开发者可以在启动时关注这些日志,从而对生产环境的配置进行调优。

-PsaveEnvironments=${value}

是否将此次启动的环境参数保存到数据库,默认为true

在某些特殊情况下,为了避免公共环境中的保护参数发生不必要的变化,我们可以选择不保存此次启动时的配置参数到数据库中,这样就不会影响其他JVM启动时发生校验失败而无法启动的问题。

-PstrictProtected=${value}

是否使用严格校验模式,默认为false

通常我们建议在公共环境启用严格校验模式,这样可以最大程度的保护公共环境的元数据不受其他环境干扰。

PS:在启用严格校验模式时,需避免内外网使用不同连接地址的场景。如无法避免,则无法启用严格校验模式。

常见问题

需要迁移数据库,并更换了数据库连接地址该如何操作?

  1. 将原有数据库迁移到新数据库。
  2. 修改配置文件中数据库的连接地址。
  3. 在启动脚本中增加-PenvProtected=false关闭环境保护。
  4. 启动JVM服务可以看到有错误的日志提示,但不会中断本次启动。
  5. 移除启动脚本中的-PenvProtected=false或将值改为true,下次启动时将继续进行环境保护检查。
  6. 可查看数据库中base_platform_environment表中对应数据库连接配置已发生修改,此时若其他JVM在启动前未正确修改,则无法启动。

本地开发时需要修改Redis连接地址到本地,但希望不影响公共环境的使用该如何操作?

PS:由于Redis中的元数据缓存是根据数据库差量进行同步的,此操作会导致公共环境在启动时无法正确刷新Redis中的元数据缓存,需要配合pamirs.distribution.session.allMetaRefresh参数进行操作。如无特殊必要,我们不建议使用该形式进行协同开发,多次修改配置会导致出错的概率增加。

  1. 本地环境首次启动时,除了修改Redis相关配置外,还需要配置pamirs.distribution.session.allMetaRefresh=true,将本地新连接的Redis进行初始化。
  2. 在本地启动时,增加-PenvProtected=false -PsaveEnvironments=false启动参数,以确保本地启动不会修改公共环境的配置,并且可以正常通过环境保护检测。
  3. 本地环境成功启动并正常开发功能后,需要发布到公共环境进行测试时,需要先修改公共环境中业务工程配置pamirs.distribution.session.allMetaRefresh=true后,再启动业务工程。
  4. 启动一次业务工程后,将配置还原为pamirs.distribution.session.allMetaRefresh=false

Oinone社区 作者:张博昊原创文章,如若转载,请注明出处:https://doc.oinone.top/backend/18451.html

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

(0)
张博昊的头像张博昊数式管理员
上一篇 2024年10月21日 pm9:58
下一篇 2024年10月23日 am9:23

相关推荐

  • 扩展操作日志字段,实现操作日志界面显示自定义字段

    注:该功能在pamirs-core 4.3.27 / 4.7.8.12以上版本可用 在模块依赖里新增DataAuditModule.MODULE_MODULE模块依赖。 @Module( name = DemoModule.MODULE_NAME, dependencies = { CommonModule.MODULE_MODULE, DataAuditModule.MODULE_MODULE }, displayName = “****”, version = “1.0.0” ) 继承OperationBody模型,设置需要在操作日志中显示的字段,并重写clone方法,设置自定义字段值。用于在计入日志处传递参数。 public class MyOperationBody extends OperationBody { public MyOperationBody(String operationModel, String operationName) { super(operationModel, operationName); } private String itemNames; public String getItemNames() { return itemNames; } public void setItemNames(String itemNames) { this.itemNames = itemNames; } @Override public OperationBody clone() { //设置自定义字段值 MyOperationBody body = OperationBody.transfer(this, new MyOperationBody(this.getOperationModel(), this.getOperationName())); body.setItemNames(this.getItemNames()); return body; } } 继承OperationLog模型,新增需要在操作日志中显示的字段。用于界面展示该自定义字段。 @Model.model(MyOperationLog.MODEL_MODEL) @Model(displayName = “自定义操作日志”, labelFields = {“itemNames”}) public class MyOperationLog extends OperationLog { public static final String MODEL_MODEL = “operation.MyOperationLog”; @Field(displayName = “新增日志字段”) @Field.String private String itemNames; } 定义一个常量 public interface OperationLogConstants { String MY_SCOPE = “MY_SCOPE”; } 在计入日志处,构造出MyOperationBody对象,向该对象中设置自定义日志字段。构造OperationLogBuilder对象并设置scope的值,用于跳转自定义服务实现。 MyOperationBody body = new MyOperationBody(CustomerCompanyUserProxy.MODEL_MODEL, CustomerCompanyUserProxyDataAudit.UPDATE); body.setItemNames(“新增日志字段”); OperationLogBuilder builder = OperationLogBuilder.newInstance(body); //设置一个scope,用于跳转自定义服务实现.OperationLogConstants.MY_SCOPE是常量,请自行定义 builder.setScope(OperationLogConstants.MY_SCOPE); //记录日志 builder.record(data.queryByPk(), data); 实现OperationLogService接口,加上@SPI.Service()注解,并设置常量,一般为类名。定义scope(注意:保持和计入日志处传入的scope值一致),用于计入日志处找到该自定义服务实现。根据逻辑重写父类中方法,便可以扩展操作日志,实现自定义记录了。 @Slf4j @Service @SPI.Service(“myOperationLogServiceImpl”) public class MyOperationLogServiceImpl< T extends D > extends OperationLogServiceImpl< T > implements OperationLogService< T >{ //定义scope,用于计入日志处找到该自定义服务实现 private static final String[] MY_SCOPE = new String[]{OperationLogConstants.MY_SCOPE}; @Override public String[] scopes() { return MY_SCOPE; } //此方法用于创建操作日志 @Override protected OperationLog createOperationLog(OperationBody body, OperationLogConfig config) { MyOperationBody body1 = (MyOperationBody)…

    2024年6月27日 后端
    1.0K00
  • Oinone连接外部数据源方案

    场景描述 在实际业务场景中,有是有这样的需求:链接外部数据进行数据的获取;通常的做法:1、【推荐】通过集成平台的数据连接器,链接外部数据源进行数据操作;2、项目代码中链接数据源,即通过程序代码操作外部数据源的数据; 本篇文章只介绍通过程序代码操作外部数据源的方式. 整体方案 Oinone管理外部数据源,即yml中配置外部数据源; 后端通过Mapper的方式进行数据操作(增/删/查/改); 调用Mapper接口的时候,指定到外部数据源; 详细步骤 1、数据源配置(application.yml), 与正常的数据源配置一样 out_ds_name(外部数据源别名): driverClassName: com.mysql.cj.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource # local环境配置调整 url: jdbc:mysql://ip(host):端口/数据库Schema?useSSL=false&allowPublicKeyRetrieval=true&useServerPrepStmts=true&cachePrepStmts=true&useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&autoReconnect=true&allowMultiQueries=true username: 用户名 password: 命名 initialSize: 5 maxActive: 200 minIdle: 5 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true asyncInit: true 2、外部数据源其他配置外部数据源限制创建表结构的执行,可以通过配置指定【不创建DB,不创建数据表】 persistence: global: auto-create-database: true auto-create-table: true ds: out_ds_name(外部数据源别名): # 不创建DB auto-create-database: false # 不创建数据表 auto-create-table: false 3、后端写Mapper SQL Mapper跟使用原生mybaits/mybaits-plus写法一样,无特殊限制; Mapper和SQL写到一起,或者分开两个文件都可以 4、Mapper被Service或者Action调用1)启动的Application中@MapperScan需要扫描到对应的包。2)调用是与普通bean一样(即调用方式跟传统的方式样),唯一的区别就是加上DsHintApi,即指定Mapper所使用的数据源。 @Autowired private ScheduleItemMapper scheduleItemMapper; public saveData(Object data) { ScheduleQuery scheduleQuery = new ScheduleQuery(); //scheduleQuery.setActionName(); try (DsHintApi dsHint = DsHintApi.use(“外部数据源名称”)) { List<ScheduleItem> scheduleItems = scheduleItemMapper.selectListForSerial(scheduleQuery); // 具体业务逻辑 } } 其他参考:如何自定义sql语句:https://doc.oinone.top/backend/4759.html

    2024年5月17日
    1.4K00
  • 如何选择适合的模型类型?

    介绍 通过Oinone 7天从入门到精通的模型的类型章节我们可以知道模型有抽象模型、存储模型、代理模型、传输模型这四种。但是在在定义模型的时候我们可能不知道该如何选择类型,下面结合业务场景为大家讲解几种模型的典型使用场景。 抽象模型 抽象模型往往是提供公共能力和字段的模型,它本身不会直接用于构建协议和基础设施(如表结构等)。 场景:猫、鸟都继承自动物这个抽象模型 package pro.shushi.pamirs.demo.api.model; import pro.shushi.pamirs.meta.annotation.Field; import pro.shushi.pamirs.meta.annotation.Model; import pro.shushi.pamirs.meta.annotation.sys.Base; import pro.shushi.pamirs.meta.base.IdModel; import pro.shushi.pamirs.meta.enmu.ModelTypeEnum; @Base @Model.model(AbstractAnimal.MODEL_MODEL) @Model.Advanced(type = ModelTypeEnum.ABSTRACT) @Model(displayName = "动物") public abstract class AbstractAnimal extends IdModel { public static final String MODEL_MODEL = "demo.AbstractAnimal"; @Field.String @Field(displayName = "名称") private String name; @Field.String @Field(displayName = "颜色") private String color; } package pro.shushi.pamirs.demo.api.model; import pro.shushi.pamirs.meta.annotation.Field; import pro.shushi.pamirs.meta.annotation.Model; @Model.model(Cat.MODEL_MODEL) @Model(displayName = "猫") public class Cat extends AbstractAnimal { private static final long serialVersionUID = -5104390780952634397L; public static final String MODEL_MODEL = "demo.Cat"; @Field.Integer @Field(displayName = "尾巴长度") private Integer tailLength; } package pro.shushi.pamirs.demo.api.model; import pro.shushi.pamirs.meta.annotation.Field; import pro.shushi.pamirs.meta.annotation.Model; @Model.model(Bird.MODEL_MODEL) @Model(displayName = "鸟") public class Bird extends AbstractAnimal { private static final long serialVersionUID = -5144390780952634397L; public static final String MODEL_MODEL = "demo.Bird"; @Field.Integer @Field(displayName = "翼展宽度") private Integer wingSpanWidth; } 存储模型 存储模型用于定义数据表结构和数据的增删改查(数据管理器)功能,是直接与连接器进行交互的数据容器。 场景:存储模型对应传统开发模式中的数据表,上面例子中的Cat和Birdd都属于传输模型,由于模型定义的注解@Model.Advanced(type = ModelTypeEnum.STORE)默认值就是存储模型,所以一般不用手动指定 代理模型 代理模型是用于代理存储模型的数据管理器能力,同时又可以扩展出非存储数据信息的交互功能的模型。 场景一:隔离数据权限 场景二:增强列表的搜索项 场景三:导入导出的时候增加其他特殊信息 场景四:重写下拉组件的查询逻辑做数据过滤 传输模型 传输模型不会在数据库生成的表,只是作为数据的传输使用,跟传统开发模式中的DTO有一点相似。 场景一:批量处理数据 场景二:处理一些跟数据表无关的操作,如:清理指定业务的缓存、查看一些系统监控信息,可以根据业务信息建立对应的传输模型,在传输模型上创建action动作 场景三:通过传输模型完成复杂页面数据传输

    2024年4月7日
    1.3K00
  • Dubbo配置详解

    概述 Dubbo是一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。 Oinone平台默认使用dubbo-v2.7.22版本,本文以该版本为例进行描述。 基本概念 Dubbo在注册provider/consumer时使用Netty作为RPC调用的核心服务,其具备客户端/服务端(C/S)的基本特性。即:provider作为服务端,consumer作为客户端。 客户端通过服务中心发现有服务可被调用时,将通过服务中心提供的服务端调用信息,连接服务端并发起请求,从而实现远程调用。 服务注册(绑定Host/Port) JAVA程序启动时,需要将provider的信息注册到服务中心,并在当前环境为Netty服务开启Host/Port监听,以实现服务注册功能。 在下文中,我们通过绑定Host/Port表示Netty服务的访问地址,通过注册Host/Port表示客户端的访问地址。 使用yaml配置绑定Host/Port PS:该配置可在多种环境中通用,改变部署方式无需修改此配置。 dubbo: protocol: name: dubbo # host: 0.0.0.0 port: -1 假设当前环境的可用IP为192.168.1.100 以上配置将使得Netty服务默认绑定在0.0.0.0:20880地址,服务注册地址为192.168.1.100:20880 客户端将通过192.168.1.100:20880调用服务端服务 若发生20880端口占用,则自动向后查找可用端口。如20881、20882等等 若当前可用端口为20881,则以上配置将使得Netty服务默认绑定在0.0.0.0:20881地址,服务注册地址为192.168.1.100:20881 使用环境变量配置注册Host/Port 当服务端被放置在容器环境中时,由于容器环境的特殊性,其内部的网络配置相对于宿主机而言是独立的。因此为保证客户端可以正常调用服务端,还需在容器中配置环境变量,以确保客户端可以通过指定的注册Host/Port进行访问。 以下示例为体现无法使用20880端口的情况,将宿主机可访问端口从20880改为20881。 DUBBO_IP_TO_REGISTRY=192.168.1.100 DUBBO_PORT_TO_REGISTRY=20881 假设当前宿主机环境的可用IP为192.168.1.100 以上配置将使得Netty服务默认绑定在0.0.0.0:20881地址,服务注册地址为192.168.1.100:20881 客户端将通过192.168.1.100:20881调用服务端服务 使用docker/docker-compose启动 需添加端口映射,将20881端口映射至宿主机20881端口。(此处容器内的端口发生变化,若需要了解具体原因,可参考题外话章节) docker-run IP=192.168.1.100 docker run -d –name designer-allinone-full \ -e DUBBO_IP_TO_REGISTRY=$IP \ -e DUBBO_PORT_TO_REGISTRY=20881 \ -p 20881:20881 \ docker-compose services: backend: container_name: designer-backend image: harbor.oinone.top/oinone/designer-backend-v5.0 restart: always environment: DUBBO_IP_TO_REGISTRY: 192.168.1.100 DUBBO_PORT_TO_REGISTRY: 20881 ports: – 20881:20881 # dubbo端口 使用kubernetes启动 工作负载(Deployment) kind: Deployment apiVersion: apps/v1 spec: replicas: 1 template: spec: containers: – name: designer-backend image: harbor.oinone.top/oinone/designer-backend-v5.0 ports: – name: dubbo containerPort: 20881 protocol: TCP env: – name: DUBBO_IP_TO_REGISTRY value: "192.168.1.100" – name: DUBBO_PORT_TO_REGISTRY value: "20881" 服务(Services) kind: Service apiVersion: v1 spec: type: NodePort ports: – name: dubbo protocol: TCP port: 20881 targetPort: dubbo nodePort: 20881 PS:此处的targetPort为对应Deployment#spec. template.spec.containers.ports.name配置的端口名称。若未配置,可使用20881直接指定对应容器的端口号。 使用kubernetes其他暴露服务方式 在Kubernetes中部署服务,有多种配置方式均可用暴露服务。上述配置仅用于通过Service/NodePort将20881端口暴露至宿主机,其他服务可用通过任意Kubernetes节点IP进行调用。 若其他服务也在Kubernetes中进行部署,则可以通过Service/Service方式进行调用。将DUBBO_IP_TO_REGISTRY配置为${serviceName}.${namespace}即可。 若其他服务无法直接访问Kubernetes的master服务,则可以通过Ingress/Service方式进行调用。将DUBBO_IP_TO_REGISTRY配置为Ingress可解析域名即可。 Dubbo调用链路图解 PS: Consumer的绑定Host/Port是其作为Provider使用的,下面所有图解仅演示单向的调用链路。 名词解释 Provider: 服务提供者(JVM) Physical Machine Provider: 服务提供者所在物理机 Provider Container: 服务提供者所在容器 Kubernetes Service: Kubernetes Service资源类型 Consumer: 服务消费者(JVM) Registration Center: 注册中心;可以是zookeeper、nacos等。 bind: 服务绑定Host/Port到指定ip:port。 registry: 服务注册;注册Host/Port到注册中心的信息。 discovery: 服务发现;注册Host/Port到消费者的信息。 invoke: 服务调用;消费者通过注册中心提供的提供者信息向提供者发起服务调用。 forward: 网络转发;通常在容器环境需要进行必要的网络转发,以使得服务调用可以到达服务提供者。 物理机/物理机调用链路 “` mermaidsequenceDiagram participant p as Provider<br>(bind 0.0.0.0:20880)participant m as Physical Machine Provider<br>(bind 192.168.1.100:20881)participant…

    2024年8月10日
    1.4K00
  • 标品实施:从标品构建到定制(扩展)包的开发

    总体描述 Oinone有一个非常重要的特性:通过平台承载标准化产品(标品)。针对不同客户的个性化需求,不再直接修改标准产品代码,而是以扩展包的形式进行扩展和定制化,通过继承和重写标准产品的能力来满足客户需求。 本文讲解述怎么通过标品构建扩展工程的过程。 构建标品 按照Oinone的规范构建标品工程 构建扩展包 在定制模块中指定上游模块 上游依赖模块upstreams,模块定义如下: @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface Module { // 显示名称 @AliasFor("displayName") String value() default ""; // 依赖模块名列表 String[] dependencies() default ModuleConstants.MODULE_BASE; // 上游模块名列表 String[] upstreams() default {}; …… 扩展模块示例 @Component @Module( name = SecondModule.MODULE_NAME, displayName = "DEMO扩展", version = "1.0.0", // 指定上游模块(标品模块,可以为多个) upstreams = DemoModule.MODULE_MODULE, priority = 1, dependencies = {ModuleConstants.MODULE_BASE, CommonModule.MODULE_MODULE, UserModule.MODULE_MODULE, AuthModule.MODULE_MODULE, BusinessModule.MODULE_MODULE, // 上游模块(标品模块,可以为多个) DemoModule.MODULE_MODULE, } ) @Module.module(SecondModule.MODULE_MODULE) @Module.Advanced(selfBuilt = true, application = true) @UxHomepage(@UxRoute(model = WorkRecord.MODEL_MODEL)) public class SecondModule implements PamirsModule { public static final String MODULE_MODULE = "demo_core_ext"; public static final String MODULE_NAME = "DemoCoreExt"; @Override public String[] packagePrefix() { return new String[]{ "pro.shushi.pamirs.second" }; } } application.yml配置文件 pamirs: boot: modules: ….. – demo_core // 加标准工程 – demo_core_ext maven配置 父工程依赖 <dependencyManagement> <dependencies> ….. <dependency> <groupId>pro.shushi.pamirs.demo</groupId> <artifactId>pamirs-demo-api</artifactId> <version>1.0.0-SNAPSHOT</version> </dependency> <dependency> <groupId>pro.shushi.pamirs.demo</groupId> <artifactId>pamirs-demo-core</artifactId> <version>1.0.0-SNAPSHOT</version> </dependency> ….. </dependencies> </dependencyManagement> api子工程加入依赖 <dependency> <groupId>pro.shushi.pamirs.demo</groupId> <artifactId>pamirs-demo-api</artifactId> </dependency> boot子工程加入依赖 <dependency> <groupId>pro.shushi.pamirs.demo</groupId> <artifactId>pamirs-demo-core</artifactId> </dependency> 数据库设置 base数据库要跟标品工程一致 注意事项 标品工程的第三方依赖,在扩展工程都要有,否则启动会报错 扩展模块功能开发 菜单扩展 1、可以按需隐藏标品的菜单; 2、可以根据扩展包的实际情况增加菜单; 模型扩展 1、扩展包可继承标品已有模型; 新增字段、覆盖字段,不继承 2、扩展包可根据实际情况新增自有模型; 函数扩展 1、扩展包可根据实际情况【覆写】标品中的函数; 2、扩展包可根据实际情况【新增】自有函数; 3、扩展包可通过Hook机制实现业务的个性化; 4、扩展包可根据自身业务情况实现标品中的扩展点; 5、……

    2024年6月1日
    1.8K00

Leave a Reply

登录后才能评论