在oinone的体系中分布式比较独特,boot工程中启动模块中包含就走本地,不包含就走远程,本文带您体验下分布式部署以及分布式部署需要注意点。
看下面例子之前先把话术统一下:启动或请求SecondModule代表启动或请求pamirs-second-boot工程,启动或请求DemoModule代表启动或请求pamirs-demo-boot工程,并没有严格意义上启动哪个模块之说,只有启动工程包含哪个模块。
一、构建SecondModule模块
Step1 构建模块工程
参考3.2.1【构建第一个Module】一文,利用脚手架工具构建一个SecondModule,记住需要修改脚本。
脚本修改如下:
#!/bin/bash
# 项目生成脚手架
# 用于新项目的构建
# 脚手架使用目录
# 本地 local
# 本地脚手架信息存储路径 ~/.m2/repository/archetype-catalog.xml
archetypeCatalog=local
# 以下参数以pamirs-second为例
# 新项目的groupId
groupId=pro.shushi.pamirs.second
# 新项目的artifactId
artifactId=pamirs-second
# 新项目的version
version=1.0.0-SNAPSHOT
# Java包名前缀
packagePrefix=pro.shushi
# Java包名后缀
packageSuffix=pamirs.second
# 新项目的pamirs platform version
pamirsVersion=4.6.0
# Java类名称前缀
javaClassNamePrefix=Second
# 项目名称 module.displayName
projectName=OinoneSecond
# 模块 MODULE_MODULE 常量
moduleModule=second_core
# 模块 MODULE_NAME 常量
moduleName=SecondCore
# spring.application.name
applicationName=pamirs-second
# tomcat server address
serverAddress=0.0.0.0
# tomcat server port
serverPort=8090
# redis host
redisHost=127.0.0.1
# redis port
redisPort=6379
# 数据库名
db=demo
# zookeeper connect string
zkConnectString=127.0.0.1:2181
# zookeeper rootPath
zkRootPath=/second
mvn archetype:generate \
-DinteractiveMode=false \
-DarchetypeCatalog=${archetypeCatalog} \
-DarchetypeGroupId=pro.shushi.pamirs.archetype \
-DarchetypeArtifactId=pamirs-project-archetype \
-DarchetypeVersion=4.6.0 \
-DgroupId=${groupId} \
-DartifactId=${artifactId} \
-Dversion=${version} \
-DpamirsVersion=${pamirsVersion} \
-Dpackage=${packagePrefix}.${packageSuffix} \
-DpackagePrefix=${packagePrefix} \
-DpackageSuffix=${packageSuffix} \
-DjavaClassNamePrefix=${javaClassNamePrefix} \
-DprojectName="${projectName}" \
-DmoduleModule=${moduleModule} \
-DmoduleName=${moduleName} \
-DapplicationName=${applicationName} \
-DserverAddress=${serverAddress} \
-DserverPort=${serverPort} \
-DredisHost=${redisHost} \
-DredisPort=${redisPort} \
-Ddb=${db} \
-DzkConnectString=${zkConnectString} \
-DzkRootPath=${zkRootPath}
脚步执行生成工程如下:
Step2 调整配置
修改application-dev.yml文件
修改SecondModule的application-dev.yml的内容
- base库换成与DemoModule一样的配置,配置项为:pamirs.datasource.base
pamirs:
datasource:
base:
driverClassName: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://127.0.0.1:3306/demo_base?useSSL=false&allowPublicKeyRetrieval=true&useServerPrepStmts=true&cachePrepStmts=true&useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&autoReconnect=true&allowMultiQueries=true
username: root # 数据库用户
password: oinone # 数据库用户对应的密码
initialSize: 5
maxActive: 200
minIdle: 5
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
asyncInit: true
- 修改后端Server的启动端口号为9091
server:
address: 0.0.0.0
port: 9091
sessionTimeout: 3600
修改bootstrap.yml文件
- 设置dubbo序列化方式为pamirs,记得DemoModule也要改。
spring:
profiles:
active: dev
application:
name: pamirs-second
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-second
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-second
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:
Step3 构建一个RemoteTestModel
package pro.shushi.pamirs.second.api.model;
import pro.shushi.pamirs.meta.annotation.Field;
import pro.shushi.pamirs.meta.annotation.Model;
import pro.shushi.pamirs.meta.base.common.CodeModel;
@Model.model(RemoteTestModel.MODEL_MODEL)
@Model(displayName = "远程测试模型",labelFields = "name")
public class RemoteTestModel extends CodeModel {
public static final String MODEL_MODEL="second.RemoteTestModel";
@Field(displayName = "name")
private String name;
}
新建RemoteTestModel模型,用于远程调用体验:
Step4 新增SecondSessionHook
@Hook(module= SecondModule.MODULE_MODULE), 规定只有增对SecondModule模块访问的请求该拦截器才会生效,不然其他模块的请求都会被SecondSessionHook拦截。
package pro.shushi.pamirs.second.core.hook;
import org.springframework.stereotype.Component;
import pro.shushi.pamirs.meta.annotation.Hook;
import pro.shushi.pamirs.meta.annotation.fun.extern.Slf4j;
import pro.shushi.pamirs.meta.api.core.faas.HookBefore;
import pro.shushi.pamirs.meta.api.dto.fun.Function;
import pro.shushi.pamirs.second.api.SecondModule;
@Component
@Slf4j
public class SecondSessionHook implements HookBefore {
@Override
@Hook(priority = 1,module= SecondModule.MODULE_MODULE)
public Object run(Function function, Object... args) {
log.info("second hook");
return function;
}
}
Step5 新建RemoteTestModelAction
在SecondModule中新建RemoteTestModelAction,自定义RemoteTestModel模型的queryPage函数。方便debug,看看效果
package pro.shushi.pamirs.second.core.action;
import pro.shushi.pamirs.meta.annotation.Function;
import pro.shushi.pamirs.meta.annotation.Model;
import pro.shushi.pamirs.meta.api.dto.condition.Pagination;
import pro.shushi.pamirs.meta.api.dto.wrapper.IWrapper;
import pro.shushi.pamirs.meta.constant.FunctionConstants;
import pro.shushi.pamirs.meta.enmu.FunctionOpenEnum;
import pro.shushi.pamirs.meta.enmu.FunctionTypeEnum;
import pro.shushi.pamirs.second.api.model.RemoteTestModel;
@Model.model(RemoteTestModel.MODEL_MODEL)
public class RemoteTestModelAction {
@Function.Advanced(type= FunctionTypeEnum.QUERY)
@Function.fun(FunctionConstants.queryPage)
@Function(openLevel = {FunctionOpenEnum.API})
public Pagination<RemoteTestModel> queryPage(Pagination<RemoteTestModel> page, IWrapper<RemoteTestModel> queryWrapper){
return new RemoteTestModel().queryPage(page,queryWrapper);
}
}
Step6 boot工程需要引入Oinone的RPC包
- 父pom的依赖管理中先加入pamirs-distribution-faas的依赖
<dependency>
<groupId>pro.shushi.pamirs.distribution</groupId>
<artifactId>pamirs-distribution-faas</artifactId>
<version>${pamirs.boot.version}</version>
</dependency>
- 在pamirs-second-boot中增加入pamirs-distribution-faas的依赖
<dependency>
<groupId>pro.shushi.pamirs.distribution</groupId>
<artifactId>pamirs-distribution-faas</artifactId>
</dependency>
- 为SecondApplication类增加类注解@EnableDubbo
@EnableDubbo
public class SecondApplication {
}
Step7 启动SecondModule
别忘了启动指令要为INSTALL,参考DemoModule的启动说明。
Step8 Second工程本地mvn install,方便DemoModule包依赖
二、DemoModule模块准备
Step1 DemoModule引入Oinone的RPC包和Second的API包
- 父pom的依赖管理中先加入pamirs-second-api和pamirs-distribution-faas的依赖
<dependency>
<groupId>pro.shushi.pamirs.second</groupId>
<artifactId>pamirs-second-api</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>pro.shushi.pamirs.distribution</groupId>
<artifactId>pamirs-distribution-faas</artifactId>
<version>${pamirs.boot.version}</version>
</dependency>
- 在pamirs-demo-api中增加入pamirs-second-api的依赖
<dependency>
<groupId>pro.shushi.pamirs.second</groupId>
<artifactId>pamirs-second-api</artifactId>
</dependency>
- 在pamirs-demo-boot中增加入pamirs-distribution-faas的依赖
<dependency>
<groupId>pro.shushi.pamirs.distribution</groupId>
<artifactId>pamirs-distribution-faas</artifactId>
</dependency>
- 为DemoApplication类增加类注解@EnableDubbo
@EnableDubbo
public class DemoApplication {
}
Step2 修改DemoModule定义
修改DemoModule的依赖注解,增加SecondModule.MODULE_MODULE
@Module(dependencies = {SecondModule.MODULE_MODULE})
Step3 修改pamirs-demo-boot的bootstrap.yml文件
参考SecondModule修改dubbo的序列化方式为pamirs
Step4 修改pamirs-demo-boot的DemoApplication
为依赖模块配置扫描包路径,修改DemoApplication增加second的扫描包,在日常开发中小伙伴的应用肯定不是以pamirs开头,所以大家别忘了SpringBoot的基本配置。在3.2.1【构建第一个Module】一文也提到我们在启动工程中需要配置启动模块和依赖模块的扫描路径。
三、分布式部署体验
以上工作准备好以后,我们就可以通过DemoModule来远程调用SecondModule。
第一个Case(前端把请求分别打到DemoModule、SecondModule)
在日常研发中,不同模块的菜单整合也是非常常见的,比如把B模块的菜单挂在A模块中。这个Case就是在讲解如何做到跨模块的菜单整合
Step1 修改前端工程的vue.config.js
利用node的proxy,分别把DemoCore转发到8090端口,把SecondCore转发到8091端口,把默认其他模块转发到8090端口。localhost还是127.0.0.1跟浏览器地址框输入保持一致
const WidgetLoaderPlugin = require('@kunlun/widget-loader/dist/plugin.js').default;
const Dotenv = require('dotenv-webpack');
module.exports = {
lintOnSave: false,
configureWebpack: {
module: {
rules: [
{
test: /\.widget$/,
loader: '@kunlun/widget-loader',
},
],
},
plugins: [new WidgetLoaderPlugin(), new Dotenv()],
resolveLoader: {
alias: {
'@kunlun/widget-loader': require.resolve('@kunlun/widget-loader'),
},
},
},
devServer: {
port: 8080,
disableHostCheck: true,
progress: false,
proxy: {
'pamirs/DemoCore': {
// 支持跨域
changeOrigin: true,
target: 'http://localhost:8090',
},
'pamirs/SecondCore': {
// 支持跨域
changeOrigin: true,
target: 'http://localhost:8091',
},
pamirs: {
// 支持跨域
changeOrigin: true,
target: 'http://localhost:8090',
},
},
},
};
Step2 修改DemoMenus
增加一个RemoteTestModel的管理入口,这里需要指定菜单的module为DemoModule,不然应用会切换到SecondModule
@UxMenu("远程模型")@UxRoute(model = RemoteTestModel.MODEL_MODEL,module= DemoModule.MODULE_MODULE) class RemoteTestModelMenu{}
Step3 重启前端应用看效果
- 点击远程模型菜单,可以正常进行增、删、改、查操作。
- SecondSessionHook起作用
“second hook”并打印出来,前端请求没有再进过DemoModule再远程调用SecondModule,而是直接打到了SecondModule上。
第二个Case(跨模块代理,自动走远程)
Step1 新建RemoteTestModelProxy
在DemoModule中新建RemoteTestModelProxy代理继承RemoteTestModel
package pro.shushi.pamirs.demo.api.proxy;
import pro.shushi.pamirs.meta.annotation.Model;
import pro.shushi.pamirs.meta.enmu.ModelTypeEnum;
import pro.shushi.pamirs.second.api.model.RemoteTestModel;
@Model.model(RemoteTestModelProxy.MODEL_MODEL)
@Model.Advanced(type = ModelTypeEnum.PROXY)
@Model(displayName = "远程模型的代理",summary="远程模型的代理")
public class RemoteTestModelProxy extends RemoteTestModel {
public static final String MODEL_MODEL="demo.RemoteTestModelProxy";
}
Step2 修改DemoMenus
增加一个RemoteTestModelProxy的管理入口
@UxMenu("远程代理")@UxMenu.route(RemoteTestModelProxy.MODEL_MODEL) class RemoteTestModelProxyMenu{}
Step3 重启看效果
- 点击菜单【远程代理】
- 在SecondModule的RemoteTestModelAction中debug,会发现DemoModule会自动调用RemoteTestModel模型的queryPage。
- 但不走SecondSessionHook
“second hook”并没有打印出来,为什么?在3.4.3.2【面向切面-拦截器】一文中介绍到“不是前端直接发起的请求不会生效”,可能小伙伴有疑问,我不是前端点击的吗?是的,但经过DemoModule再调用SecondModule,系统会判定为后端调用。
第三个Case(远程调用自定义函数)
Step1 新增函数RemoteTestModelService和RemoteTestModelServiceImpl
SecondModule定义RemoteTestModelService,用于DemoModule的调用。SecondModule记得再次mvn install
package pro.shushi.pamirs.second.api.service;
import pro.shushi.pamirs.meta.annotation.Fun;
import pro.shushi.pamirs.meta.annotation.Function;
import pro.shushi.pamirs.meta.api.dto.condition.Pagination;
import pro.shushi.pamirs.meta.api.dto.wrapper.IWrapper;
import pro.shushi.pamirs.second.api.model.RemoteTestModel;
@Fun(RemoteTestModelService.FUN_NAMESPACE)
public interface RemoteTestModelService {
String FUN_NAMESPACE ="second.RemoteTestModelService";
@Function
Pagination<RemoteTestModel> queryPage(Pagination<RemoteTestModel> page, IWrapper<RemoteTestModel> queryWrapper);
@Function
String hello(String name);
@Function
String remoteTest(RemoteTestModel remoteTest);
}
package pro.shushi.pamirs.second.core.service;
import org.springframework.stereotype.Component;
import pro.shushi.pamirs.meta.annotation.Fun;
import pro.shushi.pamirs.meta.annotation.Function;
import pro.shushi.pamirs.meta.api.dto.condition.Pagination;
import pro.shushi.pamirs.meta.api.dto.wrapper.IWrapper;
import pro.shushi.pamirs.second.api.model.RemoteTestModel;
import pro.shushi.pamirs.second.api.service.RemoteTestModelService;
@Fun(RemoteTestModelService.FUN_NAMESPACE)
@Component
public class RemoteTestModelServiceImpl implements RemoteTestModelService {
@Override
@Function
public Pagination<RemoteTestModel> queryPage(Pagination<RemoteTestModel> page, IWrapper<RemoteTestModel> queryWrapper) {
return new RemoteTestModel().queryPage(page,queryWrapper);
}
@Override
@Function
public String hello(String name) {
return "hello" + name ;
}
@Override
@Function
public String remoteTest(RemoteTestModel remoteTest) {
return "remoteTest" + remoteTest.getName() ;
}
}
Step2 新增RemoteTestModelProxyAction
-
新增RemoteTestModelProxyAction,并定义queryPage、hello、remoteTest等Action,方便前端点击测试
-
引入RemoteTestModelService,分别调用queryPage、hello、remoteTest方法
package pro.shushi.pamirs.demo.core.action;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import pro.shushi.pamirs.demo.api.proxy.RemoteTestModelProxy;
import pro.shushi.pamirs.framework.faas.utils.ArgUtils;
import pro.shushi.pamirs.meta.annotation.Action;
import pro.shushi.pamirs.meta.annotation.Function;
import pro.shushi.pamirs.meta.annotation.Model;
import pro.shushi.pamirs.meta.annotation.fun.extern.Slf4j;–
import pro.shushi.pamirs.meta.api.dto.condition.Pagination;
import pro.shushi.pamirs.meta.api.dto.wrapper.IWrapper;
import pro.shushi.pamirs.meta.constant.FunctionConstants;
import pro.shushi.pamirs.meta.enmu.FunctionOpenEnum;
import pro.shushi.pamirs.meta.enmu.FunctionTypeEnum;
import pro.shushi.pamirs.second.api.model.RemoteTestModel;
import pro.shushi.pamirs.second.api.service.RemoteTestModelService;
@Model.model(RemoteTestModelProxy.MODEL_MODEL)
@Component
@Slf4j
public class RemoteTestModelProxyAction {
@Autowired
private RemoteTestModelService remoteTestModelService;
@Function.Advanced(type= FunctionTypeEnum.QUERY)
@Function.fun(FunctionConstants.queryPage)
@Function(openLevel = {FunctionOpenEnum.API})
public Pagination<RemoteTestModelProxy> queryPage(Pagination<RemoteTestModelProxy> page, IWrapper<RemoteTestModelProxy> queryWrapper){
Pagination<RemoteTestModel> pageConvert = ArgUtils.convert(RemoteTestModelProxy.MODEL_MODEL,RemoteTestModel.MODEL_MODEL,page);
IWrapper<RemoteTestModel> queryWrapperConvert = ArgUtils.convert(RemoteTestModelProxy.MODEL_MODEL,RemoteTestModel.MODEL_MODEL,queryWrapper);
Pagination<RemoteTestModel> resultTmp= remoteTestModelService.queryPage(pageConvert,queryWrapperConvert);
Pagination<RemoteTestModelProxy> result = ArgUtils.convert(RemoteTestModel.MODEL_MODEL,RemoteTestModelProxy.MODEL_MODEL,resultTmp);
return result;
}
@Action(displayName = "hello")
public RemoteTestModelProxy hello(RemoteTestModelProxy data){
String hello = remoteTestModelService.hello(data.getName());
log.info(hello);
return data;
}
@Action(displayName = "remoteTest")
public RemoteTestModelProxy remoteTest(RemoteTestModelProxy data){
RemoteTestModel s = ArgUtils.convert(RemoteTestModelProxy.MODEL_MODEL,RemoteTestModel.MODEL_MODEL,data);
String testRemote = remoteTestModelService.remoteTest(s);
log.info(testRemote);
return data;
}
}
Step3 重启看效果
点击remoteTest按钮看看效果
总结一、二 、三 Case
把SecondModule模块停掉,则上面一、二 、三 Case都会报错。如果DemoModule的pamirs-demo-boot中依赖加上pamirs-second-core,同时application-dev.yml文件中pamirs.boot.modules加上second_core的配置,则DemoModule和SecondModule部署在一起,再把前端node请求代理都转发到DemoModule模块上。则只需要启动pamirs-demo-boot就可以了。这个留给小伙伴们自己实验。
分布式不分布式只是部署方式的差异,代码层面没有差异,有了体感以后再去看2.4.1【Oinone独特性之单体与分布式的灵活切换】一文可能会有更深的体会。
注意点:
-
【编码】远程调用服务时要确保入参和服务端定义的一样,可以用平台提供ArgUtils.convert进行转换
-
【部署】分布式情况下部署要用ng或其他方式进行转发,针对前端发起的请求,根据请求url中带的模块信息转发到对应有启动该模块的boot应用中
Oinone社区 作者:史, 昂原创文章,如若转载,请注明出处:https://doc.oinone.top/oio4/9310.html
访问Oinone官网:https://www.oinone.top获取数式Oinone低代码应用平台体验