后端开发部署指南
服务开发流程
一、新建gitlab代码仓
1. 仓库规范(推荐):
DasAIoT(组)/EnterpriseAdmin(组)/{应用名称}(组)/{前后端}(组)/{微服务名称}(git仓库)
:::caution
应用名称:大的应用名称,一个应用下可能有多个git仓库
前后端:分Back-end(后端代码)和Fron-end(前端代码)
微服务名称:微服务名称
:::
:::info
以车位引导举例 ParkingGuidance
后端代码仓库:
http://gitlab.com/DasAIoT/EnterpriseAdmin/ParkingGuidance/Back-end/ParkingGuidance.git
前端代码仓库:
http://gitlab.com/DasAIoT/EnterpriseAdmin/ParkingGuidance/Fron-end/ParkingGuidance.git
:::
2. 仓库代码推送步骤
Git 全局设置
git config --global user.name "姓名"
git config --global user.email "邮箱"
创建一个新仓库
git clone http://das-git.chn-das.com/DasAIoT/EnterpriseAdmin/CustomDataSource/Back-end/customdatasourcetest.git
cd customdatasourcetest
git switch -c main
touch README.md
git add README.md
git commit -m "add README"
推送现有文件夹
cd existing_folder
git init --initial-branch=main
git remote add origin http://das-git.chn-das.com/DasAIoT/EnterpriseAdmin/CustomDataSource/Back-end/customdatasourcetest.git
git add .
git commit -m "Initial commit"
推送现有的 Git 仓库
cd existing_repo
git remote rename origin old-origin
git remote add origin http://das-git.chn-das.com/DasAIoT/EnterpriseAdmin/CustomDataSource/Back-end/customdatasourcetest.git
二、项目搭建
1. 统一版本管理
1. 项目pom.xml
声明版本统一由dependency进行控制
<parent>
<groupId>com.das</groupId>
<artifactId>dependency</artifactId>
<version>3.0.0</version>
</parent>
目前稳定版本 3.0.0
2. maven配置
3. 直接依赖脚手架
指定脚手架功能模块版本.项目pom.xml parent非dependency时
2. 创建业务模块
3. 依赖脚手架功能
3.1 基础包
<dependency>
<groupId>com.das.libcore</groupId>
<artifactId>commons-base</artifactId>
<version>3.0.0</version>
</dependency>
功能简介
简介:存放各种公共的且和Spring无关的方法类定义
使用说明
常用公共类 | 用处 |
---|---|
R | 供Feign统一返回使用以及controller错误返回信息包装 |
ResultCode | 常用API返回对象,目前code和httpstatus绑定不能随意添加 |
GlobalRequestConstant | 全局请求信息相关key常量 |
ResultConstant | R 字段对应字符常量 |
GlobalInstanceContext | 用来存储全局唯一实例!多个线程可共享, 主要用来解耦Spring |
GlobalRequestDTO | 全局请求参数 |
TaskProgressDTO | 任务实体类,进度条 |
TaskStatusEnum | 进度条统一状态 |
ApplicationEnum | 应用类型枚举 |
UserGroupTagType | 用户组类型 |
UserTagEnum | 用户标签枚举 |
ApiException | 自定义API异常 |
BizException | 业务异常 |
AuthorizationDataUtil | 请求参数传递辅助类 |
3.2 spring封装包
<dependency>
<groupId>com.das.libcore</groupId>
<artifactId>web-spring-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
功能简介
用处:存放各种公共的且和Spring相关的方法类定义
提供功能:包括异常拦截、统一返回类R、ip工具、Spring工具、servlet工具、解析jwt载荷工具等
使用说明
引入common-spring-boot-starter核心包依赖
相关公共类说明
类 说明 IpUtils ip相关工具类,可以获取请求真实IP地址 JwtBuilder jwt工具类,可以构建jwt,目前载荷内容包括租户,用户id,姓名,是否是服务,flag JwtInfoUtils jwt工具类,获取全局请求参数以及进行鉴权处理 ModelUtil 基本模型数据填充工具类 PageUtils 分页工具类 SecureUtils 加解密工具类 SensitiveInfoUtil 脱敏工具类,目前只有手机号脱敏方法 ServletUtils 客户端工具类 SpringUtils spring工具类,方便在非spring管理环境中获取bean SqlUtil sql操作工具类 StringUtils 字符串工具类,包括判空 去空格 截取等字符串相关操作 TimeZoneUtils 时区时间转换,针对前端时间以及后端时间响应进行转换 ActionResult 返回工具类,统一使用该类进行请求结果返回 CustomMsgResultCode 参数校验抛出的异常,可自定义错误描述的ResultCode 提供注解说明
注解 说明 @EnableJwtInfoAnnotation 在启动类或者配置类上加上注解标识需要解析jwt信息的功能,因为放在common包里,避免只需要引入common其他功能但是不需要jwt解析功能 @Sensitive 用于controller层或者service层的方法上面,判断返回数据是否需要脱敏相关处理 @SensitiveInfo 用于属性上面,判断相关数据是否需要加密或脱敏相关处理 @SensitiveInput 用于controller层或者service层的方法上面,判断入参数据是否需要加密处理 @WebLog 用于方法上面,描述日志信息 @AllowableValues 状态值校验工具 @XssCheckValid Xss校验 部分工具类使用说明
- 分页功能
```
在需要分页的地方使用工具类方法返回一个Page对象
com.das.libcore.web.utils.PageUtils的bulidPage(Integer pageNo, Integer pageSize)
参数说明:
pageNo 页码(不传有默认值1)
pageSize 页量(不传有默认值10)- 数据脱敏、编解码 ```java 数据加密场景: 1.在方法上增加@SensitiveInput注解 2.在属性上增加@SensitiveInfo注解 @PostMapping @SensitiveInput public List<UserDTo> s3Upload(@RequestBody List<User> user) { return Collections.singletonList(new UserDTo(user, "111")); } public class User { @SensitiveInfo(encode = true) private String name; } 数据解码 并 脱敏场景: 1.在方法上增加@Sensitive注解 2.在属性上增加@SensitiveInfo注解 @PostMapping @Sensitive public List<UserDTo> s3Upload(@RequestBody List<User> user) { return Collections.singletonList(new UserDTo(user, "111")); } public class UserDTo { private List<User> users; private String phone; } public class User { @SensitiveInfo(type = SensitiveType.USER_PHONE, decode = true) private String phone; }
- 分页功能
3.3 自动生成代码包
<dependency>
<groupId>com.das.libcore</groupId>
<artifactId>das-generator</artifactId>
<version>3.0.0</version>
</dependency>
功能简介
用处:用于根据表一键生成controller、entity、service、serviceImpl、mapper、mapper.xml等基础的类
使用说明
- 引入das-generator核心包依赖
- 代码使用
```java
//直接调用GeneratorUtil#GeneratorCode方法
GeneratorUtil.GeneratorCode(String url, String username, String password, String schema,String tableName, String moduleName, String middlePath, String packagePath,
String tablePrefix, String tableSuffix);
3. 参数说明
|参数|说明|
|---|---|
|String url | 数据库连接url |
|String username | 数据库用户名 |
|String password | 数据库密码 |
|String schema | 指定的schema |
|String tableName | 表名(多个用逗号分隔)|
|String moduleName | 模块名(用于controller上面的api拼接的module,可以为空)|
|String middlePath | 中间路径 文件生成的路径是定位在项目根路径的,需要指定到具体模块下的路径|
|String packagePath | 包路径 文件生成路径 /项目路径/模块路径/包路径/|
|String tablePrefix | 忽略表前缀 例如表为das_xxx 不想要加上 das_ 则值为 das_|
|String tableSuffix | 忽略表后缀 例如表为xxx_info 不想要加上 _info 则值为 _info |
4. 生成文件说明
```js
文件统一格式:
controller:默认会生成 添加、修改、查询所有、根据id删除 4个方法
service、impl:会有添加、修改、查询所有3个方法的实现,里面细致的实现需要再去添加代码、根据id删除用的mybatis-plus中的父类方法
mapper、mapper.xml:默认带有baseColumnList baseResultMap两个sql片段
3.4 规则引擎
<dependency>
<groupId>com.das.libcore</groupId>
<artifactId>drools-spring-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
功能简介
用处:对规则引擎的封装,存放加载规则引擎的包
使用说明
引入drools核心包依赖
相关类说明
类 说明 RuleHelper 可检测规则语法规范,获取指定的规则session RuleLoader 规则加载器,动态加载规则 RuleInfoModel 规则信息实体类 动态加载规则
```java
@Autowired
private RuleLoader ruleLoader;ruleLoader.reload(String ruleType, List
ruleList);
- 动态检测规则
```java
@Autowired
private RuleHelper ruleHelper;
ruleHelper.checkRuleContent();
true 表示 通过
false 表示 失败
- 执行规则
@Autowired private RuleHelper ruleHelper; KieSession kieSession = ruleHelper.getKieSessionByKey(ruleKey); //如果需要放入实体就放入 kieSession.insert(model); //加载当前session下所有规则 kieSession.fireAllRules();
3.5 全球化
<dependency>
<groupId>com.das.libcore</groupId>
<artifactId>globalization-spring-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
功能简介
用处:提供全球化功能
使用说明
引入globalization-spring-boot-starter核心包依赖
需要实现获取全球化资源的接口,然后注入到Spring中,这里可以具体定义全球化资源存储方式如:mysql,redis等,现在默认实现是sqlite,可以直接使用
```java
@Component
public class GlobalizationSqliteDataServiceImpl implements GlobalizationDataService {@Autowired
LocalizationRecordMapper localizationRecordMapper;@Override
public ListloadAllGlobalizationData() {
return localizationRecordMapper.selectList(null);
}@Override
public void insertAnUnknownLanguage(LocalizationRecordPO localizationRecordPO) {
localizationRecordMapper.insert(localizationRecordPO);
}
}
- 全球化修改返回值是通过在通用返回类设置扩展接口实现的,相关默认实现在 DefaultUpdateResultAwareImpl 类中
- 默认只对错误msg进行了国际化,如需要序列化特定地方的常量数据,可自行注入messageSource然后取得locale进行手动国际化
```java
Locale locale = LocaleContextHolder.getLocale();
messageSource.getMessage(str, null, locale);
3.6 Kafka服务
<dependency>
<groupId>com.das.libcore</groupId>
<artifactId>kafka-spring-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
功能简介
用处:对kafka进行封装
使用说明
引入kafka核心包依赖
配置kafka连接
das: iot: kafka: #kafka配置 bootstrap-servers: 172.168.110.27:9092 enable-idempotence: false acks: -1 auto-offset-reset: earliest enable-auto-commit: false key-serializer: org.apache.kafka.common.serialization.StringSerializer value-serializer: org.apache.kafka.common.serialization.StringSerializer key-deserializer: org.apache.kafka.common.serialization.StringDeserializer value-deserializer: org.apache.kafka.common.serialization.StringDeserializer auto-restart: true auto-restart-interval: 20 group: DEVICE_INDICATORS_BATCH_GROUP topics: send: deviceTopic: DEVICE_TOPIC receiver: deviceTopic: DEVICE_TOPIC count: 3
注入Kafka工具类
- 发送数据
@Resource KafkaManager kafkaManager; kafkaManager.sendAsync(topic,"sss");
接受数据
首先加上注解@EnableKafka("alarmEventConsumer") 开启kafka,EnableKafka中的value是group,但是KafkaListenter中的group优先级更高
```java
@Service
@Slf4j
@EnableKafka("alarmEventConsumer")
public class TestKafkaListener {@Resource
KafkaManager kafkaManager;@KafkaListener(topic = "${das.iot.kafka.topics.receiver.deviceTopic}",group = "${das.iot.kafka.group}")
public void test(ConsumerRecord record){
Optional msg = Optional.ofNullable(record.value());
if (msg.isPresent()) {
log.info("msg:{}",JsonUtils.toJson(msg.get()));
}
}
}
```
3.7 mqtt
<dependency>
<groupId>com.das.libcore</groupId>
<artifactId>mqtt-spring-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
功能简介
用处:对mqtt进行了封装,提供mqtt连接功能
使用说明
- 引入mqtt-spring-boot-starter核心包依赖
- 相关类说明
类 说明 MsgHandle 使用订阅功能时需要继承实现的接口类,重写MsgHandle#handleMsg方法 MqttConfiguration mqtt配置类 MyMqttCallback mqtt回调 MqttService 对mqttClient简单封装,其中包括连接方法connect(),设置连接参数setMqttConnectOptions(),发布消息publish(),订阅主题subscribe(),取消订阅主题cleanTopic() - 配置连接信息
```yaml
mqtt:
host: tcp://192.168.56.10:1883
username: root
password: root
clientId: boot-test
#注意这里单位是秒s
timeOut: 2
keepAlive: 10
topic: das/demo/mqtt
- 使用方式
直接在需要的地方注入mqttService即可,但是如果需要使用订阅功能需要实现MsgHandler并通过mqttService.setMsgHandle(MsgHandle msgHandle)在容器启动完成注入到mqttService中如下:
```java
/**
* 如果需要处理消息需要实现对应处理类接口再项目启动后注入到mqttService中
*/
@Component
public class MqttConfig implements CommandLineRunner {
@Autowired
private MqttService mqttService;
@Override
public void run(String... args) {
mqttService.setMsgHandle(new MyMsgHandle());
}
}
3.8 pulsar消息队列
<dependency>
<groupId>com.das.libcore</groupId>
<artifactId>pulsar-spring-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
功能简介
用处:对pulsar进行了封装,提供pulsar消息队列使用
关于topic的前置说明:
- pulsar的topic规范格式为:{是否持久化消息标识}://{租户}/{命名空间}/{自定义部分}
- 文档中提到的租户、命名空间 与pulsar中的topic规范格式相对应,文档中的topic通常指的为{自定义部分}
使用说明
1. 引入pulsar-spring-boot-starter核心包依赖
2. 配置
- 脚手架重写了pulsar连接url, 脚手架连接串的格式为:pulsar://{pulsar服务}?auth={认证方式},{认证信息},admin-port={pulsar admin api端口},其他和pulsar原生配置一致。
认证方式:
- token, 认证信息为:token={token值}
- basic, 认证信息为: u={账号},p={密码}
- pulsar.tenant 下配置为请求pulsarcheck服务的配置信息
- namespace-var: 为注解 @PulsarConsumer namespace中自定义配置的变量
配置参考:
``` yaml
pulsar:
client:
serviceUrl: pulsar://ip:6650?auth=basic,u=t01,p=123456,admin-port=18080
listenerThreads: 50
tenant:
pulsar.tenant.refresh-switch: true //是否开启刷新租户订阅
listen-server: http://ip:30059 //租户信息检查服务pulasr-check地址
refresh-period-minute: 1 //拉取租户信息时间间隔
namespace-var: //命名空间中自定义配置的变量
ConsumerTest1: testCreate1,testCreate1 // 自定义的命名空间变量(逗号分割,多个表示订阅多个命名空间)
#客户端配置详细信息参考 PulsarProperties。
```
3. 发送消息
1. 脚手架发送消息相关的方法位于PulsarTemplate类中, 可根据数据类型不同调用PulsarTemplate不同方法。
2. SendMsgMata 可配置生产者发送消息时的一些配置。参数说明:
- tenant: 消息的租户, 默认public
- nameSpace: 消息的命名空间,默认default
- topic: 业务topic定义
- key: 指定消息key
- messageTTLInSecond: 消息ttl时间,单位秒
- delaySendTime:消息延时发送时间(毫秒)
- delayTime:消息延时时间(毫秒),延时到指定时间发送
注:messageTTLInSecond 支持需开启服务端配置
配置文件:conf/broker.conf、conf/standalone.conf
配置项: topicLevelPoliciesEnabled=true
systemTopicEnabled=true
@Autowired
private PulsarTemplate pulsartemplate;
//同步发送消息方法(指定前缀)
SendMsgMata jsonMsgMata = SendMsgMata.builder().tenant("t01").nameSpace("p01").nameSpacePrefix(NameSpacePrefix.PROJ).topic("IStringConsumer").build();
A<A> data = new A();
pulsarTemplate.sendJson(jsonMsgMata, data);
//异步发送消息方法
<T> CompletableFuture<MessageId> sendJsonAsync(SendMsgMata msgMata, T msg);
4. 生产者脚手架默认实现功能
4.1. 租户和命名空间自动创建: 调用PulsarTemplate 发送消息会自动检查租户和命名空间是否存在,不存在会调用pulsar admin api创建。
4.2. 发送json消息转换:调用PulsarTemplate发送json消息, 实体对象会先转json字符串,然后以Schema.BYTES方式发送给pulsar。脚手架json格式消费者会逆向序列化为实体(原因pulsar client对嵌套复杂的JSON实体以Schema.JSON 方式消费者无法正常反序列化,需手动处理)
4.3. 脚手架发送的消息默认都为持久化的消息
5. 拓展脚手架发送配置
当默认实现的发送消息配置无法满足需求,可通过如下方式自定义发送。
5.1. 通过调用PulsarTemplate.getMessageBuilder 获取消息发送的Builder
5.2. 调用发送方法
示例:
```java
SendMsgMata jsonMsgMata = SendMsgMata.builder().tenant("t01").nameSpace("p01").nameSpacePrefix(NameSpacePrefix.PROJ).topic("IStringConsumer").build();
A<A> data = new A();
pulsarTemplate.getMessageBuilder(jsonMsgMata, Schema.STRING);
TypedMessageBuilder msgBuilder = getMessageBuilder(msgMata, Schema.STRING);
msgBuilder.value(msg);
//同步发送消息
msgBuilder.send();
```
6. 原生生产者开发
当对生产者有特殊需求,脚手架实现无法满足时可基于pulsar原生client 进行自定义生产开发。(脚手架默认实现功能需自己处理)
示例:
```java
// 注入pulsar 客户端
@Autowired
private PulsarClient pulsarClient;
//创建生产者
Producer producer = pulsarClient.newProducer(schema)
.topic(pulsarTopic)
.intercept(new DefaultProducerInterceptor())
.create();
```
7. 消费者消费消息
7.1.消费者类添加注解 @PulsarConsumer
@PulsarConsumer 注解说明
- topic:消费者订阅业务主题(无需租户和命名空间), 通配符匹配订阅需使用${xxx}格式(但不能以正则开头)
- subscriptionName: 消费订阅的名称, 类似于kafka 消费者组group,不指定客户端会自动生成。
- subscriptionType: 消费订阅类型, Exclusive一个订阅名只能有一个消费者订阅、 Shared可供多个消费者消费,多topic 默认按轮流方式分配,
不能保证消息顺序、Key_Shared 按消息键分配,保证同一键的消息按顺序分配到同一消费者、Failover可允许备用消费者订阅,只能主消费者消费. 默认Shared.
- initCount: 创建消费者数量,默认1 (Exclusive模式下不要配置)
- tenant:指定某个租户订阅, 不配置或配置*则订阅所有租户(包括public)。
- initialPosition:指定消息从什么位置消费(默认最早未消费SubscriptionInitialPosition.Earliest)
- namespace: 指定某个命名空间订阅,不配置或配置*则订阅所有命名空间, 支持${xxx}变量配置(变量配置需放入pular.namespace-var下,变量名建议用消费者类)
- nameSpacePrefix: 订阅指定命名空间前缀,不指定默认为公共业务没有前缀(前缀枚举: NameSpacePrefix), 指定前缀则不能配置nameSpacePrefix
- 实例相关:IOT("iot_")
- 项目相关:PROJ("proj_")
- 公共:NONE("")
7.2.根据不同序列化方式实现不同的IConsumer
示例:
简单json(简单POJO) 实现 IJsonConsumer
@PulsarConsumer(topic = "IJsonConsumer")
public static class TestConsumer2 implements IJsonConsumer<A>{
@Override
public void handleMessage(A data, MessageHolder messageHolder) throws Exception {
log.debug("IJsonConsumer data:{}", JsonUtils.toJson(data));
}
@Override
public Class<A> getParseClass() {
return A.class;
}
}
复杂json消息对象(带泛型、List)实现 IComplexJsonConsumer
@PulsarConsumer(topic = "IComplexJsonConsumer",
subscriptionType = SubscriptionType.Key_Shared,
initCount = 1)
public static class TestConsumer1 implements IComplexJsonConsumer<List<A<A>>> {
@Override
public void handleMessage(List<A<A>> data, MessageHolder messageHolder) throws Exception {
log.debug("IComplexJsonConsumer data:{}", JsonUtils.toJson(data));
System.out.println(data);
}
@Override
public TypeReference typeReference() {
return new TypeReference<List<A<A>>>() {};
}
}
String 消息实现 IJsonConsumer
@PulsarConsumer(topic = "IStringConsumer")
public static class TestConsumer3 implements IStringConsumer {
@Override
public void handleMessage(String data, MessageHolder messageHolder) throws Exception {
log.debug("IStringConsumer data:{}", data);
}
}
原生byte[]消息 IByteArrayConsumer
8. 消费者脚手架默认实现功能
- 1、租户|命名空间自动匹配订阅: 新增租户或命名空间,如果消费者注解 没有配置tenant或namespace(订阅所有租户|命名空间)或nameSpacePrefix指定命名空间前缀, 当pulsar租户或命名空间改变消费者会自动订阅
- 2、失败重试: 默认开启重试, 消费失败(handleMessage方法抛出异常),重试5次后未成功确认消费移入死信队列。 重试队列重试延迟间隔默认1秒 (延迟重试的乘数因子为2. 因此默认重试间隔为1, 2, 4, 8, 16)
9. 拓展脚手架消费者配置
当脚手架消费者注解配置无法满足时,重写脚手架创建pulsar消费者拓展点(consumerBuilder方法),允许自定义配置消费者。
示例: 配置消费者确认超时与重试机制
配置官方文档:https://pulsar.apache.org/docs/3.0.x/concepts-messaging/
/**
* 当脚手架消费者配置无法满足时,允许自定义配置消费者
* 脚手架创建pulsar消费者拓展点
*
* @param consumerBuilder 脚手架根据注解配置生成的ConsumerBuilder
* @return
*/
default ConsumerBuilder consumerBuilder(ConsumerBuilder consumerBuilder){
consumerBuilder.ackTimeout(10, TimeUnit.SECONDS)
.ackTimeoutRedeliveryBackoff(MultiplierRedeliveryBackoff.builder()
.minDelayMs(1000)
.maxDelayMs(60 * 1000)
.multiplier(2)
.build());
return consumerBuilder;
}
10. 原生消费者开发
当有特殊需求,上述脚手架消费者实现都无法满足时可基于pulsar原生client 进行自定义消费开发。(脚手架默认实现功能需自己处理)
示例:
// 注入pulsar 客户端
@Autowired
private PulsarClient pulsarClient;
public void customerConsumer(){
//生成消费者
Consumer consumer = pulsarClient.newConsumer(Schema.STRING).topic("persistent://public/default/consumerPullEarliestPositionTestTopic0").
consumerName("consumerPullEarliestPositionTestConsumer2").
subscriptionName("consumerPullEarliestPositionTestSubscription").
subscriptionType(SubscriptionType.Shared).
subscriptionInitialPosition(SubscriptionInitialPosition.Earliest).messageListener((consumer, msg) -> {
try {
System.out.println(String.format("msg topic %s, consumer %s, consumer last available msg id %s, msg id: %s data: %s",
msg.getTopicName(), consumer.getConsumerName(), consumer.getLastMessageId().toString(), msg.getMessageId().toString(), msg.getValue()));
consumer.acknowledge(msg);
int a = 1/0;
} catch (PulsarClientException e) {
e.printStackTrace();
}
}).
subscribe();
}
11. 认证授权
pulsar内部支持:TLS、JWT、Athenz、Kerberos、OAuth 2.0、basic
自定义拓展:客户端服务端实现插件接口,服务端的插件类放入broker的classpath. https://pulsar.apache.org/docs/2.10.x/security-extending/
现状:授权采用账号名密码授权,内部使用一个拥有全部权限的账号登录。 待外部需要时再创建租户对应账号,并给该账号授予租户下所有命名空间的权限。
认证方式一:账号名密码
官方文档:https://pulsar.apache.org/docs/2.10.x/security-basic-auth/
利用账号密码生成认证文件内容(md5的那种方式),可利用htpasswd 命令,也可在该网站生成http://www.jsons.cn/htpasswd/,多个账号在文件中换行存储
broker 开启认证和授权,standalone的配置文件conf/standalone.conf。 若集群则是conf/standalone.conf
authenticationEnabled=true
authenticationProviders=org.apache.pulsar.broker.authentication.AuthenticationProviderBasic
basicAuthConf=file:///path/to/.htpasswd
brokerClientAuthenticationPlugin=org.apache.pulsar.client.impl.auth.AuthenticationBasic
brokerClientAuthenticationParameters={"userId":"superuser","password":"admin"}
authenticateOriginalAuthData=true
- 配置 conf/proxy.conf
authenticationEnabled=true
authenticationProviders=org.apache.pulsar.broker.authentication.AuthenticationProviderBasic
basicAuthConf=file:///path/to/.htpasswd
brokerClientAuthenticationPlugin=org.apache.pulsar.client.impl.auth.AuthenticationBasic
brokerClientAuthenticationParameters={"userId":"superuser","password":"admin"}
forwardAuthorizationCredentials=true
- 配置客户端工具认证,conf/client.conf (命令行客户端工具会用到)
java 客户端配置authPlugin=org.apache.pulsar.client.impl.auth.AuthenticationBasic authParams={"userId":"superuser","password":"admin"}
Pulsar restful api请求: 添加Basic auth认证参数(base64(用户名:密码))AuthenticationBasic auth = new AuthenticationBasic(); auth.configure("{\"userId\":\"superuser\",\"password\":\"admin\"}"); PulsarClient client = PulsarClient.builder() .serviceUrl("pulsar://broker.example.com:6650") .authentication(auth) .build();
- token 方式认证
pulsar 服务支持配置多种认证 可以在现在有的basic上再加上token认证
官方文档:[https://pulsar.apache.org/docs/2.11.x/security-jwt/]
创建token密钥:
bin/pulsar tokens create-secret-key --output my-secret.key生成token:
bin/pulsar tokens create --secret-key file:///path/to/my-secret.key --subject superuser配置token 认证(添加token认证插件)
配置文件:conf/standalone.conf: authenticationProviders=org.apache.pulsar.broker.authentication.AuthenticationProviderToken tokenSecretKey=file:///path/to/my-secret.key
3.9 RabbitMQ服务
<dependency>
<groupId>com.das.libcore</groupId>
<artifactId>rabbitmq</artifactId>
<version>3.0.0</version>
</dependency>
功能简介
用处:对rabbitmq进行了封装
使用说明
- 引入rabbitmq核心包依赖
- 配置rabbitmq连接
das: iot: rabbitmq: userName: test password: test address: virtualHost: port:
- 注入RabbitMQ工具类
- 发送数据
注入RabbitMQHelper并调用send方法进行数据发送@Resource RabbitMQHelper rabbitMQHelper; //交换机名称,路由键以及值 rabbitMQHelper.send(RabbitMqConfig.DEAD_LETTER_EXCHANGE,RabbitMqConfig.RK_POINT_RULE_METCHES,"ss");
- 接受数据继承 MessageListenter
@Component public class PointRuleMatchesMessageListener implements MessageListener { private static final Logger logger = LoggerFactory.getLogger(PointRuleMatchesMessageListener.class); @Override public void onMessage(Message message) { logger.info("PointRuleMatchesMessageListener 开始消费"); String body = new String(message.getBody()); logger.info("获取的body为{}",body); } }
- 发送数据
3.10 Redis集成
<dependency>
<groupId>com.das.libcore</groupId>
<artifactId>redis-spring-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
功能简介
用处:对redis进行了封装
使用说明
使用:
引入redis核心包依赖
配置redis连接
#多database模拟多数据源 spring: redis: read: host: 192.168.100.26 port: 6379 password: das@123! write: host: 192.168.100.26 port: 6379 password: das@123! database: 1 log: host: 192.168.100.26 port: 6379 password: das@123! database: 2
代码示例
- 单数据源使用
``` java
@Autowired
private RedisService redisService;
#操作默认redis数据源(同升级前)
redisService.set("das:iot:test-redis-string", "hello,你好");- 单数据源使用
- 多数据源使用
```java
# 使用redisService
redisService.getRedisTemplate("dataSource1").opsForValue().set("test-multi-source-key", "1");
redisService.getRedisTemplate("dataSource2").opsForValue().set("test-multi-source-key", "2");
# 注入【配置文件中定义的redis数据源的名称】+ RedisTemplate
@Autowired(required = false)
@Qualifier("xxxRedisTemplate")
private RedisTemplate xxxRedisTemplate;
3.11 签名校验功能
<dependency>
<groupId>com.das.libcore</groupId>
<artifactId>sign-spring-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
功能简介
用处:签名验证工具包
使用说明
使用:
引入sign-spring-boot-starter核心包依赖
在使用的Controller上添加注解@Sign则每个方法都会进行签名校验,如果哪个方法不需要则加上 @IgnoreSign
```
@Sign
@Validated
@Controller
@RequestMapping("/sign")
public class SignDemoController {
@GetMapping(value = "/test/get/1")
@ResponseBody
public String testGetSignWithoutParam() {
return ActionResult.ok("OK");
}@PostMapping(value = "/test/post/2")
@ResponseBody
public UserVO testPostSignWithBody(@RequestBody @Validated UserVO userVO, @RequestParam String name) {
return ActionResult.ok(userVO);
}@IgnoreSign
@PostMapping(value = "/test/ignore")
@ResponseBody
public String testIgnoreSign() {
return ActionResult.ok("OK");
}
}
- 签名校验功能说明
后端会对参数包括QueryParam参数和Path参数名称和值进行排序包含appid,timestamp,sign_method但是不包含sign,如果有请求体必须是json,则json的body放在最前为空则丢弃,示例如下
http://localhost:8080/sign/test/get/1?timestamp=1666668081091&appid=tang&sign=4462488eabe48476213f5f2d04b46564&sign_method=md5&name=tang&age=26
age=26&appid=tang&name=tang&sign_method=md5×tamp=1666668081091
http://localhost:8080/sign/test/post/2?timestamp=1666668081091&appid=tang&sign=07c6eb5e4dc2540cb9bf54e860f88626&sign_method=md5&name=tang
body={"userId":1,"username":"zhangsan"}&appid=tang&name=tang&sign_method=md5×tamp=1666668081091
然后根据传过来的appId查询secret,同时根据sign_method方法类型选用不同算法进行加密,规则如下
当sign_method = hmac_sha256
HMACSHA256( "body={"police":"noPo"}&appid=knasdfnas&age=17&name=yuhun×tamp=111111" , secret )
当sign_method = md5
md5( "body={"police":"noPo"}&appid=knasdfnas&age=17&name=yuhun×tamp=111111" + secret )
- 签名Secret获取接口,实现SignAccess的getSignSecret方法然后注入到容器中即可
```java
public interface SignAccess {
/**
* 返回sign的Secret
*
* @return {@link String}
*/
String getSignSecret(String appid);
/**
* 定义不同算法实现sign摘要
*
* @param message
* @param secret
* @return {@link String}
*/
String getSignHash(String message, String secret);
}
3.12 文件处理功能
<dependency>
<groupId>com.das.libcore</groupId>
<artifactId>file-spring-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
功能简介
用处:文件导入导出工具包
使用说明
引入file-spring-boot-starter核心包依赖
服务类说明
类 说明 FileExportService 文件导出服务类 FileImportService 文件导入服务类 //通过调用FileExportService#exportFile方法进行文件导出 FileExportService#exportFile(String taskId, String taskKey, String bucket, String objectPathAndName, WriteFileCallback writeFileCallback)
- 参数说明
参数 说明 taskId 任务id taskKey 任务key bucket 存储桶名称 objectPathAndName 对象路径和名称 writeFileCallback 写数据回调 //通过调用FileExportService#exportFile方法进行文件导出 FileImportService#importFile(String taskId, String taskKey, String bucket, String objectPathAndName, WriteFileCallback writeFileCallback)
- 参数说明
参数 说明 taskId 任务id taskKey 任务key bucket 存储桶名称 uploadFilePathAndName 上传文件路径和名称 errorFilePathAndName 错误文件路径和名称 readDataCallback 读取数据回调 insertDataCallback 插入数据回调 handlerErrorDataCallback 处理错误数据回调
- 参数说明
回调说明
回调 说明 HandlerErrorDataCallback 处理错误数据文件回调 InsertDataCallback 插入数据回调 InsertDataEnhancerCallback 插入数据回调 ReadDataCallback 从excel读取数据回调 WriteFileCallback 文件导入服务类
3.13 多租户功能
<dependency>
<groupId>com.das.libcore</groupId>
<artifactId>schema-spring-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
功能简介
简介:支持多租户功能,租户下表结构初始化功能
使用说明
如果采用mybatisplus自动配置不用自己注入mybatis插件
但是重写了mybatisplus配置就需要手动注入插件
默认schema是_,需要事先建立名为_的schema并将表复制到_下
3.14 系统日志记录功能
<dependency>
<groupId>com.das.libcore</groupId>
<artifactId>svclog-spring-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
功能简介
为各服务提供服务的业务日志记录能力
使用说明
- 引入svclog-spring-boot-starter核心包依赖
- 发送日志
// 注入日志记录对象
private final SvcLog svcLog;
// 发送系统访问日志
svcLog.accessLog(AccessLogDTO.builder().type(AccessStatus.LOGIN)
.source(AccessSource.APP)
.msg("登录成功").build());
// 发送业务操作日志
svcLog.businessLog(BusinessLogDTO.builder().module("平台管理")
.function("项目新增")
.operateType(BusinessType.ADD)
.msg("新增项目【深圳达实智能大厦(d6c6039c)】").build());
// 发送消息发送日志
svcLog.messageLog(MessageSendLogDTO.builder().title("告警发送通知")
.channel(SendChannel.MAIL)
.receiver("张三")
.status(StatusEnum.SUCCESS)
.msg("登录成功").build());
// 发送设备操作日志
svcLog.equipmentLog(EquipmentOperateLogDTO.builder().equipmentName("空调机0002")
.equipmentCode("KTJ0002")
.systemId("1234567890")
.msg("空调开关=1")
.source("IBMS")
.status(ResponseStatus.SUCCESS).build());
- 注意事项
此示例为在Spring Boot中使用的示例,使用此组件的项目需引入并正确配置pulsar-spring-boot-starter
在非Spring Boot中,可以通过传入一个可用的PulsarClient
实例的方式,显式初始化SvcLog
:
SvcLog svcLog = new SvcLog(pulsarClient);
- 其他例如用户操作事件,用户信息,IP地址,租户,项目等信息,均会从当前请求的
GlobalRequestDTO
中获取(自动填充所有留空的公共字段),通常不需要显式指定这些内容,但是在子线程或者长时间运行的后台任务服务中,需要显式填充这些值,有两种方式:
AccessLogDTO log = AccessLogDTO.builder().type(AccessStatus.LOGIN)
.source(AccessSource.APP)
.msg("登录成功")
.basicLogInfo(BasicLogInfo.builder()
.sub("213123123123")
.ip("127.0.0.1")
.name("张三")
.build())
.build();
svcLog.accessLog(log);
// 设置当前线程的GlobalRequestDTO
AuthorizationDataUtil.setRequestData(GlobalRequestDTO.builder()
.sub("213123123123").ip("127.0.0.1")
.name("张三")
.build());
// 发送系统访问日志
svcLog.accessLog(AccessLogDTO.builder().type(AccessStatus.LOGIN)
.source(AccessSource.APP)
.msg("登录成功").build());
3.15 分布式任务
<dependency>
<groupId>com.das.libcore</groupId>
<artifactId>xxljob-spring-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
功能简介
简介:分布式任务统一处理
使用说明
- 引入SDK
- 说明:目前支持定时corn http任务,业务侧抛弃以往开发spring-boot 定时任务的方式,直接将逻辑通过rest api的方式暴露,通过创建 xxl-job 定时任务来请求业务接口完成定时逻辑。需要注意的是,业务侧有必要保存任务的相关参数
- 示例:
import com.das.libcore.common.dto.PageResultDTO; import com.das.libcore.xxljob.enums.MethodEnum; import com.das.libcore.xxljob.model.HttpJobExecutorParam; import com.das.libcore.xxljob.model.HttpJobInfo; import com.das.libcore.xxljob.model.ReturnT; import com.das.libcore.xxljob.service.XxlJobService; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequiredArgsConstructor public class IndexController { private final XxlJobService xxlJobService; @RequestMapping("/page") public PageResultDTO<HttpJobInfo> page() { PageResultDTO<HttpJobInfo> dto = xxlJobService.pageList("algorithmengine", 1, 10, -1, null); return dto; } @RequestMapping("/add") public String add() { HttpJobInfo httpJobInfo = HttpJobInfo.builder() .applicationCode("algorithmengine") .jobDesc("测试任务111") .immediateStart(true) .corn("*/10 * * * * ?") .executorParam(HttpJobExecutorParam.builder() .url("http://workbench-api/test") .method(MethodEnum.POST) .postData("{\"from\": 123, \"content\":\"测试内容\", \"orgContent\":\"测试内容\", \"cardSlot\":\"测试内容\"}") .build()) .build(); ReturnT<String> returnT = xxlJobService.add(httpJobInfo); return returnT.getContent(); } @RequestMapping("/update") public String update() { HttpJobInfo httpJobInfo = HttpJobInfo.builder() .applicationCode("algorithmengine") .jobDesc("测试任务111") .id("1") .immediateStart(true) .corn("*/5 * * * * ?") .executorParam(HttpJobExecutorParam.builder() .url("http://workbench-api/test") .method(MethodEnum.POST) .postData("{\"from\": 12322, \"content\":\"testtestsetst\", \"orgContent\":\"sdgsdfgsg\", \"cardSlot\":\"测试内容\"}") .build()) .build(); ReturnT<String> returnT = xxlJobService.update(httpJobInfo); return returnT.getContent(); } @RequestMapping("/remove") public String remove() { ReturnT<String> returnT = xxlJobService.remove("1"); return returnT.getContent(); } @RequestMapping("/start") public String start() { ReturnT<String> returnT = xxlJobService.start("1"); return returnT.getContent(); } @RequestMapping("/stop") public String stop() { ReturnT<String> returnT = xxlJobService.stop("1"); return returnT.getContent(); } @RequestMapping("/trigger") public String trigger() { HttpJobExecutorParam executorParam = HttpJobExecutorParam.builder() .url("http://workbench-api/test") .method(MethodEnum.POST) .postData("{\"from\": 12322, \"content\":\"测试内容\", \"orgContent\":\"测试内容\", \"cardSlot\":\"测试内容\"}") .build(); ReturnT<String> returnT = xxlJobService.triggerJob("1", executorParam); return returnT.getContent(); } }
- 参数说明
HttpJobInfo参数说明如下,SDK代码中也有详细的注释说明
3.16 apollo服务
<dependency>
<groupId>com.das.libcore</groupId>
<artifactId>apollo-client</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>com.das.libcore</groupId>
<artifactId>apollo-spring-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
功能简介
存放加载apollo的包,涉及后续使用apollo做配置中心的相关内容
管理应用需要连接的namespace名称
命名空间名称:NameSpace
key:namespace
value:namespace1,namespace2,namespace3
使用说明
- 引入apollo-client,apollo-spring-boot-starter核心包依赖
- 使用
- 在apollo添加应用
- 绑定公共Namespace NameSpace、BasicConfig
- 新增配置文件
注意:文件名称需要携带后缀
- 在application.yml文件中增加apollo配置
- 不同的环境,通过环境变量来配置
env=dev
- 在apollo添加应用
3.17 feign服务
<dependency>
<groupId>com.das.libcore</groupId>
<artifactId>feign-spring-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
功能简介
封装了openfeign功能,在feign发起请求时添加对应的请求头及参数,并进行相关请求的日志打印以及对feign返回数据解码至对应的数据格式
使用说明
与openfeign使用流程一致
3.18 授权平台服务
<dependency>
<groupId>com.das.libcore</groupId>
<artifactId>justauth-spring-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
功能简介
集成了JustAuth功能,涉及平台授权的相关内容
使用说明
- 添加配置,在
application.yml
中添加配置配置信息justauth: enabled: true extend: # 资源配置类 enum-class: com.das.oauth.extend.ExtendSource config: DAS: # 业务操作类 request-class: com.das.oauth.extend.ExtendDASRequest # 客户端id,对应各平台的appKey client-id: test2 # 客户端Secret,对应各平台的appSecret client-secret: 123456 # 回调地址 redirect-uri: http://localhost:8443/oauth/das/callback # 授权平台地址 back-uri: http://v6aiotdev.rd.chn-das.com/api/justauth # 服务范围校验 scopes: - sever
- 定义接口
通过AuthRequestFactory以及type类型参数来获取对应的AuthRequest请求,然后通过authRequest调用指定接口来实现指定功能@Slf4j @RestController @RequestMapping("/oauth") @RequiredArgsConstructor(onConstructor_ = {@Lazy}) public class TestController { private final AuthRequestFactory factory; @GetMapping public List<String> list() { return factory.oauthList(); } @GetMapping("/login/{type}") public void login(@PathVariable String type, HttpServletResponse response) throws IOException { AuthRequest authRequest = factory.get(type); response.sendRedirect(authRequest.authorize(AuthStateUtils.createState())); } @RequestMapping("/{type}/callback") public AuthResponse login(@PathVariable String type, AuthCallback callback) { AuthRequest authRequest = factory.get(type); if (StringUtils.isNotEmpty(callback.getAuthorization_code())) { log.info("----------------------------"); } AuthResponse response = authRequest.login(callback); log.info("【response】= {}", JSON.toJSONString(response)); return response; } }
3.19 minio服务
<dependency>
<groupId>com.das.libcore</groupId>
<artifactId>minio-spring-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
功能简介
封装了minio功能,通过调用MinIO工具类中的相关方法实现创建minio客户端、判断、查询、删除、下载、上传等文件处理相关功能,主要用于资源文件的存储,包括导入导出,头像上传,文档上传等业务中的静态文件
使用说明
- 使用
后端直接通过minio的SDK客户端将文件传输到minio服务器上,再根据业务需要,将minio地址(相对路径)返回到前端或保存至数据库 - MinioUtil工具类部分说明
方法 说明 createBucket 创建Bucket createBucketAndSetPolicy 创建Bucket,并且设置其权限为公共可读 bucketExists 判断Bucket是否存在 getAllBuckets 获得所有Bucket列表 getBucket 根据存储桶名称获取其相关信息 removeBucket 根据存储桶名称删除Bucket,true:删除成功;false:删除失败,文件或已不存在 isObjectExist 判断文件是否存在 getAllObjectsByPrefix 根据文件前缀查询文件 getObject 获取文件/断点下载 uploadFile 文件上传 getFileStatusInfo 获取文件信息, 如果抛出异常则说明文件不存在 copyFile 拷贝文件 removeFiles 批量删除文件 getPreSignedObjectUrl 获取文件外链 getUtf8ByDecoder 将URLDecoder编码转成UTF8 - Minio文件层级
- 临时目录(主要用于导入导出):temp/租户/服务名 存储桶:temp
- 静态模板目录(主要用于静态模板的下载):template/服务名/ 存储桶:template
- 租户级别业务目录(主要用于业务上传文件):租户Id/服务名/ 存储桶:租户Id(t 开头)
- 项目级别业务目录(主要用于业务上传文件):p项目Id/服务名/ 存储桶:项目Id(p 开头)
- IoT 级别业务目录(主要用于业务上传文件):iot-{IoT 实例 Id}/服务名/ 存储桶:iotId(iot 开头)
- 前端静态资源:resource/front-end/ 如存储css的目录为resource/front-end/css
- 业务静态资源:resource/业务/ 如企管的菜单目录为:resource/enterpriseAdmin/menu
划分依据:以租户或者项目维度进行管理资源,精确控制每个项目或者每个租户消耗的空间大小 服务名:由后端明确告诉前端,上传到所在服务下,方便管理 注意: 业务产生的文件,文件名不允许有规律,如按数字有规律递增或者时间戳等,防止拖库 建议:在现有文件层级后以 yyyy/MM/dd 创建 年月日或者年月层级的文件夹,方便业务统一处理和管理,如按时间清除文件
- IoT 平台设备文件存储
IoT 的设备文件由于外放到边缘上传,不可控性增加,并且业务上对于单个实例下设备文件上传资源占用需要精确计算以做限制或者计费,上传规范以此为准租户-实例/设备Id/
:租户_实例
为存储桶设备Id/
后的路径或者文件由边缘设备开发者自行定义,建议设备上传时遵循一定层级
服务部署流程
jenkins 插件示例
pipeline {
agent{ label 'node22||node24||node25' }
options {
buildDiscarder logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '300', numToKeepStr: '36500')
}
environment {
Hub_Docker_Server='192.168.100.28:8088'
}
parameters {
string(name: 'ImageTag', defaultValue: 'default',description: '默认情况下不用改动,定版时填写版本号。镜像标签,不能填写中文、空格和特殊字符')
string(name: 'GitAddr',defaultValue: 'http://das-git.chn-das.com', description: '代码git地址')
string(name: 'GitBranch',defaultValue: 'dev', description: '代码分支')
string(name: 'AppPath',defaultValue: 'xxx', description: '程序入口文件夹。')
booleanParam defaultValue: true, description: '是否发布,不发布的话就只打包镜像不更新环境', name: 'Release'
string(name: 'DockerfilePath',defaultValue: '', description: '默认情况为空,有特殊服务需要自定义Dockerfile的服务填写Dockerfile文件路径。(不用填写Dockerfile文件名)')
choice choices: ['192.168.100.26','192.168.100.21','192.168.100.22','192.168.100.23','192.168.100.27','192.168.100.29','192.168.100.30','192.168.100.32','192.168.100.33','192.168.100.191','192.168.100.193','192.168.100.194','192.168.100.195','192.168.100.196','192.168.100.197','192.168.100.198','192.168.100.199','192.168.100.204','192.168.100.210','192.168.100.211','192.168.100.212','192.168.100.213','192.168.100.214','192.168.100.215','192.168.100.216','192.168.100.217','192.168.100.218','192.168.100.219','192.168.100.220','192.168.100.221','192.168.100.222'], description: '选择发布的服务器', name: 'RemoteIP'
booleanParam defaultValue: false, description: '清除缓存das/libcore', name: 'ClearCache'
choice choices: ['amd64','arm64'], description: '选择处理器架构', name: 'Architecture'
}
stages {
stage("start"){
steps{
script{
deleteDir()
if("${params.ClearCache}" == "true" )
{
sh 'rm -rf /root/.m2/repository/com/das/libcore/'
}
## 拉取代码
checkout([$class: 'GitSCM', branches: [[name: "${GitBranch}"]], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: 'b94e7780-10e7-4dde-9c66-60b2615902f4', url: "${GitAddr}"]]])
AppName=sh(returnStdout: true, script: 'echo ${AppPath}|tr "/" " "|awk \'{print $NF}\'|tr "." "-"|tr [A-Z] [a-z]').trim()
Group_Name=sh(returnStdout: true, script: 'echo ${GitAddr}|sed "s#http://das-git.chn-das.com/##g"|awk -F"/" \'{print $2}\'|tr [A-Z] [a-z]').trim()
ServerName="${Group_Name}-${AppName}"
if (fileExists('./.k8s-servername')) {
ServerName=sh(returnStdout: true, script: 'cat ./.k8s-servername').trim()
}
imageTag="${params.ImageTag}"
if(imageTag=="default")
{
imageTag = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim()
CreateTime = sh(returnStdout: true, script: 'date +%s').trim()
imageTag = "${GitBranch}-${imageTag}-${CreateTime}"
}
ImageName = "${Hub_Docker_Server}/dasaiot/${ServerName}:${imageTag}"
LatestImageName = "${Hub_Docker_Server}/dasaiot/${ServerName}:latest"
sh """
echo ${params.AppPath}
echo ${ServerName}
echo ${imageTag}
## 打包操作
mvn clean package -U
ls ${params.AppPath}/target/
cp ${params.AppPath}/target/${params.AppPath}.jar ./
"""
## amd64 - 标准, arm64 - 国产化环境
if("${params.Architecture}" == "amd64" )
{
sh """
if [ -z "${params.DockerfilePath}" ];
then
export ServerName=${ServerName}
export AppPath=${params.AppPath}
jar ufe ${params.AppPath}.jar org.springframework.boot.loader.PropertiesLauncher
wget -O Dockerfile_java http://minio.rd.chn-das.com/cicd/dockerfile_template/Dockerfile_java
envsubst < Dockerfile_java > ./Dockerfile
else
cp ${params.DockerfilePath}/Dockerfile ./
if cat Dockerfile | grep -q "PropertiesLauncher"; then
sed -i /PropertiesLauncher/d Dockerfile
jar ufe ${params.AppPath}.jar org.springframework.boot.loader.PropertiesLauncher
fi
fi
docker build --no-cache -t ${ImageName} .
docker tag ${ImageName} ${LatestImageName}
## 发布镜像
docker push ${ImageName}
docker push ${LatestImageName}
"""
} else if("${params.Architecture}" == "arm64" )
{
sh """
if [ -z "${params.DockerfilePath}" ];
then
export ServerName=${ServerName}
export AppPath=${params.AppPath}
jar ufe ${params.AppPath}.jar org.springframework.boot.loader.PropertiesLauncher
wget -O Dockerfile_javaarm64 http://minio.rd.chn-das.com/cicd/dockerfile_template/Dockerfile_javaarm64
envsubst < Dockerfile_javaarm64 > ./Dockerfile
else
cp ${params.DockerfilePath}/Dockerfilearm64 ./Dockerfile
if cat Dockerfile | grep -q "PropertiesLauncher"; then
sed -i /PropertiesLauncher/d Dockerfile
jar ufe ${params.AppPath}.jar org.springframework.boot.loader.PropertiesLauncher
fi
fi
docker build --no-cache -t ${ImageName} .
## 发布镜像
docker push ${ImageName}
"""
}
echo "是否发布"
if ( Release == "true" )
{sh """
if [ -z "`ssh -i /root/.ssh/jenkins -o StrictHostKeyChecking=no -tt root@${RemoteIP} "kubectl get deploy| grep ${ServerName}"`" ];
then
wget -O Javatemplate.yaml http://minio.rd.chn-das.com/cicd/k8s_template/Javatemplate.yaml
export ServerName=${ServerName}
export ImageName=${ImageName}
envsubst < Javatemplate.yaml > ./${ServerName}.yaml
scp -i /root/.ssh/jenkins -o StrictHostKeyChecking=no ${ServerName}.yaml root@${RemoteIP}:/opt/data/appdata/yaml/${ServerName}.yaml
ssh -i /root/.ssh/jenkins -o StrictHostKeyChecking=no -tt root@${RemoteIP} "kubectl apply -f /opt/data/appdata/yaml/${ServerName}.yaml"
curl -X 'POST' 'http://webhook-api.rd.chn-das.com/api/webhook/v1/wechat/notice?token={token}&users=all' -H 'accept: */*' -H 'Content-Type: application/json' -d '"'${RemoteIP}环境新增java服务${ServerName}'"'
else
ssh -i /root/.ssh/jenkins -o StrictHostKeyChecking=no -tt root@${RemoteIP} "kubectl set image deployment/${ServerName} ${ServerName}=${ImageName}"
fi
"""
echo "ServerName=${ServerName}"
echo "ImageName=${ImageName}"
}
}
}
}
}
}
1. 拉取代码
git clone http://das-git.chn-das.com/DasAIoT/EnterpriseAdmin/CustomDataSource/Back-end/demo.git
2. 服务打包
mvn clean package -U
3. 发布镜像
## 创建镜像
docker build --no-cache -t ${ImageName} .
## 标记本地镜像。归档至某个仓库
docker tag ${ImageName} ${LatestImageName}
## 发布镜像至镜像仓库
docker push ${ImageName}
docker push ${LatestImageName}
4. 服务启动
## 找到已部署的服务(判断服务是否存在)
kubectl get deploy| grep ${ServerName}
## 更新deploy配置(服务不存在时)
kubectl apply -f /opt/data/appdata/yaml/${ServerName}.yaml
## 更新当前服务的镜像(服务存在时)
kubectl set image deployment/${ServerName} ${ServerName}=${ImageName}
注意事项
超管菜单
1. 菜单维护
维护地址: 菜单工具
2. 菜单导出并归档
3. 缺少该步骤问题现象:证书下发无法下发新增应用
企管菜单
1. 菜单维护
维护地址: 菜单工具
2. 菜单导出并归档
初始化脚本 必须指定数据库 permission,租户 public
3. 缺少该步骤问题现象:证书下发后 企管未展示新增应用且无法授权
消息模板
1. 消息模板维护
达实租户-消息管理维护消息类型、消息模板
2. 消息模板脚本
数据库(message_center)-租户(admin) 找到待归档的消息模板、消息类型 导出sql进行归档
注意 built_in(系统内置) 必须声明为 true