服务模块规范
服务模块概述
- 每个微服务应用中可以包含多个服务模块。
- 每个微服务应用的资源根目录是 model ,在 model/service 目录中包含了当前微服务应用的所有服务模块,服务模块目录不允许嵌套。
服务模块基本组成
目录和文件 | 类型 | 说明 |
---|---|---|
model/service/{serviceName} | dir | 服务主目录 |
model/service/{serviceName}/deploy.m | file | 服务目录标识文件 |
model/service/{serviceName}/app.m | file | 服务网关注册文件 |
model/service/{serviceName}/deploy | dir | 服务定义目录 |
model/service/{serviceName}/deploy/*.def.xml | file | 服务定义文件 |
model/service/{serviceName}/deploy/*.sh | file | 自定义脚本文件 |
model/service/{serviceName}/dist | dir | 服务资源目录,例如:war、jar |
服务目录标识文件 deploy.m
deploy.m 文件用于标识当前目录是服务模块目录,例如:model/service/app1/deploy.m 表示需要部署一个服务名为 app1 的服务,缺省的 deploy.m 文件是一个空文件。
服务网关注册文件 app.m
app.m 用于配置代理网关,每个 app.m 对应在网关上配置的一个接口.根据需要可以通过子目录创建多个 app.m 来实现在网关上配置多个接口,目录名不限,例如:
- model/service/{serviceName}/app.m
- model/service/{serviceName}/root/app.m
- model/service/{serviceName}/…/app.m
<?xml version="1.0" encoding="UTF-8" ?>
<service>
<!-- 接口名 -->
<name>davinci</name>
<!-- 接口路由配置 -->
<request_path>/davinci</request_path>
<preserve_host>true</preserve_host>
<strip_request_path>false</strip_request_path>
<upstream_url>http://localhost:8787</upstream_url>
<!-- 接口插件配置 -->
<plugin name="authentication" enabled="false">
<app_key>{{.Env.API_KEY}}</app_key>
<app_secret>{{.Env.API_SECRET}}</app_secret>
</plugin>
<plugin name="health-check" enabled="true">
<health_url>/davinci</health_url>
</plugin>
</service>
服务定义文件 *.def.xml
服务在开发调试环境和部署运行环境下有不同的服务定义文件,分别对应架构设计工具中的开发架构和运行架构(kube或pool)。 一般服务环境定义文件有以下几个:
文件 | 说明 |
---|---|
dev.def.xml | 开发调试环境定义文件 |
production.def.xml | 默认的部署运行环境服务定义 |
production.kube.def.xml | 部署运行环境 kube 部署的服务定义,如果此文件存在,优先级高于默认的 production.def.xml |
production.pool.def.xml | 部署运行环境 pool 部署的服务定义,如果此文件存在,优先级高于默认的 production.def.xml |
服务定义文件 *.def.xml 包含: properties 服务属性定义、uses 服务引用定义 和 spec 服务部署定义 三个部分。
<service>
<!-- 服务属性定义 -->
<properties>
<property name="serviceAddress" value="${appsvc.serviceAddress}" readonly="true">
<label label = "服务地址"/>
</property>
<property name="port" value="${appsvc.port}" readonly="true">
<label label = "端口(port)"/>
</property>
<property name="dbType" value="${db.dbType}" readonly="true">
<label label = "数据库类型"/>
</property>
<property name="dbHost" value="${db.dbHost}" readonly="true">
<label label = "数据库服务地址"/>
</property>
<property name="dbPort" value="${db.dbPort}" readonly="true">
<label label = "数据库端口"/>
</property>
<property name="dbUser" value="${db.dbUser}" readonly="true">
<label label = "数据库用户名"/>
</property>
<property name="dbPasswd" value="${db.dbPasswd}" readonly="true">
<label label = "数据库密码"/>
</property>
<property name="dbName" value="${db.dbName}" readonly="true">
<label label = "数据库名称"/>
</property>
<property name="dbSchema" value="${db.dbSchema}" readonly="true">
<label label = "dbSchema"/>
</property>
<property name="dbProperties" value="${db.dbProperties}" readonly="true">
<label label = "dbProperties"/>
</property>
<property optional="true" name="dbextSharding" value="${db.dbextSharding}" readonly="true">
<label label = "sharding"/>
</property>
</properties>
<!-- 服务引用定义 -->
<uses>
<use name="db" instance="database"></use>
<use name="minio" instance="minio"></use>
</uses>
<!-- 服务部署定义 -->
<spec platform="app-container">
<option name="name" value="appsvc" />
<option name="instance" value="tomcat" />
</spec>
</service>
服务属性定义 properties
服务属性使用 properties/property 定义,用于定义服务的配置属性和环境变量,例如: 地址、端口、用户名、密码、数据连接等。 每个 property 定义默认生成 ${serviceName}_${propertyName} 的环境变量(全大写,减号替换为下划线),可以在 property 上通过 env 属性控制环境变量的命名,env="-" 表示不产生环境变量。 说明:这里的环境变量就是服务运行时的环境变量,在 java 中可以通过 System.getenv 函数获取。
property 的属性关键字 | 说明 |
---|---|
name | 属性名 |
value | 属性值 |
readonly | true / false(默认值),是否只读属性,指在架构设计工具中属性值是否只读 |
optional | true / false(默认值),是否可选属性,可选属性的属性值如果无效则不生成环境变量,代码中可以通过判断环境变量是否存在执行不同的逻辑 |
env | 指定环境变量名称,- 表示不产生环境变量 |
output | true / false(默认值),是否自动输出属性,自动输出属性会在引用的服务中自动生成对应的属性定义 |
tag | 标签,用于定义和区分同一类的属性 |
property / value 可以使用的系统变量 | 说明 | 适用服务 |
---|---|---|
@serviceName@ | 服务逻辑名称 | 所有服务 |
@serviceAddress@ | 容器内部的服务访问地址 | 定义了yaml的服务 |
@containerName@ | 容器名称 | 定义了yaml的服务 |
@justepHomeDir@ | justep home 目录 | 所有服务 |
@appId@ | 应用ID | 所有服务 |
@apiKey@ | apiKey | 所有服务 |
@apiSecret@ | apiSecret | 所有服务 |
@domainPrefix@ | 应用域名前缀 | 所有服务 |
@baseDomain@ | 应用域名 | 所有服务 |
@indexUrl@ | app.conf中定义的 index_url | 所有服务 |
@pcindexUrl@ | app.conf中定义的 pc_index_url | 所有服务 |
@mindexUrl@ | app.conf中定义的 m_index_url | 所有服务 |
@adminUrl@ | app.conf中定义的 admin_url | 所有服务 |
@tenantId@ | 租户ID | 所有服务 |
@appHttpPort@ | 应用http端口 | 所有服务 |
@appHttpsPort@ | 应用https端口 | 所有服务 |
property / value 可以使用的系统函数 | 说明 | 适用服务 |
---|---|---|
@systemAdminUser()@ | 系统管理用户名 | 所有服务 |
@systemAdminPasswd()@ | 系统管理密码 | 所有服务 |
@jdbcDriver(\'dbName\' )@ | 从数据库定义生成 jdbc driver | 所有服务 |
@jdbcUrl(\'dbName\' )@ | 从数据库定义生成 jdbc url | 所有服务 |
@env(\'envName\' )@ | 访问环境变量 | 所有服务 |
@redisAllocKey(\'name\' )@ | 生成redis分配键 | 所有服务 |
服务引用定义 uses
服务引用使用 uses/use 定义,用于定义服务之间的引用关系。 服务引用后,在服务的属性定义中就可以通过 ${serviceInstance.property} 引用其他服务的属性来配置当前服务的属性值.
use 的属性关键字 | 说明 |
---|---|
name | 引用后的别名 |
instance | 引用的服务实例 |
optional | true / false(默认值),是否可选引用 |
可选服务引用和可选属性
在某些应用场景下,服务A对服务B的引用关系不是强制要求的,B是否存在,不影响A的正常运行,仅影响到A的执行逻辑。针对这种场景,服务定义允许定义可选的服务引用和属性。
<!-- 可选引用 -->
<uses>
<use optional="true" name="dbproxy" instance="comp.dbproxy"></use>
</uses>
对于所有声明的可选服务引用 {name} ,有一个系统属性 @{name}Exists@ 用于获取目标服务是否存在的值, true/false。这个系统属性可用于定义引用端的property。
<!-- 可选属性 -->
<property name="dbproxyExists" value="@dbproxyExists@" readonly="true">
<label label = "dbproxy 是否存在"/>
</property>
应用运行时可读取 Exists 环境变量,根据环境变量的值分别处理目标服务存在和不存在的逻辑。 关于可选引用的 property,有以下特殊规则:
- 通过可选引用引入的服务定义的 property,具备可选特性
- 可选 property 具备传递性,一个 property 如果引用了可选 property,也具备可选特性,例如 A.a 引用了 B.b,如果 B.b 具备可选特性,那么 A.a 也具备可选特性
- 具备可选特性的 property,如果服务不存在,该 property 在模型解析时被忽略,也不会生成环境变量。
- 由于容器的 yaml 定义不具备条件判断的能力,所以具有可选特性的 property,不允许用于容器的yaml中。 除了上述4点外,其它特性与普通的 property 一致。
服务部署定义 spec
服务部署使用 spec 定义,用于定义服务的部署方式。
<spec platform="docker-compose"/>
说明这是一个通过容器镜像部署的服务<spec platform="app-container">
说明这是一个部署到运行容器的服务
服务部署定义的参数通过 option 子节点定义:
option name | option value |
---|---|
name | 运行容器的别名,在属性定义中可以通过别名引用容器实例的属性值 |
value | 运行容器实例,tomcat、nginx、java-runtime、node-runtime... |
container | 运行容器类型,java、nginx、nodejs... |
deploy | true(默认值)/ false,是否部署 dist 资源 |
<!-- 容器镜像 -->
<spec platform="docker-compose" />
<!-- Java Tomcat -->
<spec platform="app-container">
<option name="name" value="appsvc" />
<option name="instance" value="tomcat" />
<option name="container" value="java" />
<option name="deploy" value="true" />
</spec>
<!-- Nginx -->
<spec platform="app-container">
<option name="name" value="appsvc" />
<option name="instance" value="nginx-runtime" />
<option name="container" value="nginx" />
<option name="deploy" value="true" />
</spec>
<!-- Java Spring Boot -->
<spec platform="app-container">
<option name="name" value="appsvc" />
<option name="instance" value="java-runtime" />
<option name="container" value="java" />
<option name="deploy" value="true" />
</spec>
自定义脚本文件与服务模块的运行生命周期
服务模块可以看成是一个标准服务,有初始化、启动、停止。初始化属于部署后需要执行的逻辑,在新部署、升级部署、关联模版变更时候执行;启动和停止是在每次池绑定或者解绑时执行。
服务模块的运行生命周期包含以下几个阶段:
顺序 | 阶段 | 说明 |
---|---|---|
1 | install | 安装依赖,例如 maven 的 jar,或者自定义安装某些软件(如果是容器型组件,在容器里面执行,否则在app-init里面执行),install 可看成 init0 的细分阶段 |
2 | init0 | start 之前执行的初始化,例如:数据库初始化等 |
3 | start | 启动服务,注意: java-runtime、node-runtime 等运行时的服务需要写自己的 start.sh 用于启动服务,而 tomcat 或 nginx 的运行时 start 是整个 tomcat 或 nginx 的启动,服务单独写 start.sh 也将被忽略 |
4 | init | start之后执行的初始化,依赖服务运行 |
5 | inited | 所有服务就绪后(网关放行) |
6 | stop | 停止服务,不用单独写stop.sh,停止服务由系统默认提供(基于supervisor对start启动的进程和子进程进行管理) |
对于用户自定义的服务模块,可以通过自定义脚本文件,在相应的服务运行阶段执行 shell 脚本。自定义脚本文件的命名规范:
- 文件路径:model/service/{serviceName}/deploy/*.sh 。
- 默认文件名:${阶段}.sh,install.sh,init0.sh,start.sh,init.sh,inited.sh, stop.sh 。
- 按部署类型区分:${部署类型}.${阶段}.sh,部署类型的取值范围:dev、production、kube.production、pool.production,例如:如果对于 kube 和 pool 的生产部署需要有不同的 start 脚本,可以分别定义 kube.production.start.sh 和 pool.production.start.sh 。