1 - 开放包容理念

核心价值观

SOFAServerless 社区的核心价值观是 “开放” 和 “包容”。社区里所有的用户、开发者完全平等,体现在如下几个方面:

  1. 社区参考了 Apache 开源项目的运作方式,对社区做出任意贡献的同学,尤其是非代码贡献的同学(文档、官网、Issue 回复、运营布道、发展建议等),都是我们的 Contributor,都有机会成为社区的 Committer 甚至是 PMC(Project Management Committee)成员。

  2. 社区所有的 OKR、RoadMap、讨论、会议、技术方案等都是完全开放的,所有人都可以看见,并且都可以参与其中,社区会认证倾听、考虑大家的所有建议和意见,一旦采纳就会确保执行落地。希望大家带着无所顾虑、求同尊异的心态参与 SOFAServerless 社区。

  3. 社区不限地域国籍,所有源代码必须是英文注释确保大家都能理解,官网也是中英文双语。所有微信群、钉钉群、GitHub Issues 讨论都可以是中英双语。但由于当前我们主要聚焦在国内用户,因此大部分文档暂时只有中文版,未来会提供英文版。

2023 年 OKR

O1 打造社区健康、有行业影响力的 Serverless 开源产品

KR1 新增 10 个 Contributors,年底 OpenRank 指数 > 15(当前 5)、活跃度 > 50(当前 44)

KR1.1 完成 5 次布道和 5 次文章分享,触达 200 家企业,深度交流 30+ 企业。
KR1.2 形成完善的社区共建机制(包括 Issue 管理、文档、问题响应、培养与晋升机制),发布 2+ 培训课程与产品手册,共建开发者可在一周内上手,开发总吞吐率达到 20+ Issues/周。

KR2 新增 5 家企业在生产环境使用或完成试点接入(当前新增 1),3 家企业参与社区

KR2.1 产出初步行业分析报告,帮助定位适用不同场景的重点企业对象。
KR2.2 5 家企业生产真实使用或完成试点接入,3 家企业参与社区,覆盖 3 个场景并沉淀 3+ 用户案例。

O2 打造技术先进、效果显著的降本增效解决方案

KR1 落地模块化技术实现机器减少 30%、部署验证耗时降低至 30 秒、需求交付效率提升 50%

KR1.1 搭建 1 分钟快速试用平台,完善的文档、官网与配套支持,用户可在 10 分钟完成一个模块拆分。
KR1.2 完成 20 种中间件和三方包治理,同时形成多应用与热卸载评测和自动检测标准。
KR1.3 模块具备热部署启动耗时降低至 10 秒级,多模块具备合并部署资源减少 30%,同时让用户需求交付效率提升 50%。
KR1.4 落地开源版 Arklet,支持 SOFABoot 和 SpringBoot。提供运维管道、指标采集、模块生命周期管理、多模块运行环境、Bean 与服务发现及调用能力。
KR1.5 落地研发工具 ArkCtl,具备快速开发验证、灵活部署(合并与独立部署)、模块低成本拆分改造能力。

KR2 运维调度 1.0 版本上线。全链路高频端到端测试用例成功率 99.9%,自身端到端耗时 P90 < 500ms

KR2.1 上线基于 K8S Operator 的开源版运维调度能力,至少具备发布、回滚、下线、扩缩容、替换、副本保持、2+ 调度策略、模块流控、部署策略、对等和非对等运维能力。
KR2.2 建设开源版 CI 和 25+ 高频端到端测试用例,不断打磨并推动端到端 P90 耗时 < 500ms、所有预演成功率> 99.9%、单测覆盖率达到行 > 80% 分支 > 60%(通过率 100%)。

KR3 开源版自动伸缩初步上线,模块具备人工画像和分时伸缩能力

RoadMap

  • 2023.08 完成 SOFABoot 完整的部署功能验证,产出兼容性 Benchmark 基线。
  • 2023.09 发布基础运维和调度系统 ModuleController 0.5 版本。
  • 2023.09 发布研发运维工具 Arkctl 与 Arklet 0.5 版本。
  • 2023.09 官网和完整用户手册上线。
  • 2023.10 新增 2+ 公司使用。
  • 2023.11 支持 SpringBoot 完整能力和 5+ 社区常用中间件。
  • 2023.11 SOFAServerless 1.0 版本上线(ModuleController、Arkctl、Arklet、SpringBoot 兼容)。
  • 2023.12 SOFAServerless 1.1 版本上线(包括基础自动伸缩、模块基础拆分工具、20+ 中间件与三方包兼容)。
  • 2023.12 新增 5+ 家公司真实使用,10+ Contributors 参与。


2 - 交流渠道

SOFAServerless 提供如下沟通交流渠道,欢迎加入我们一起分享、一起使用、一起收获:

SOFAServerless 社区交流与协作钉钉群:24970018417

如果您对 SOFAServerless 感兴趣、或者有初步意向使用 SOFAServerless、或者已经是 SOFAServerless / SOFAArk 的用户、或者有兴趣成为社区 Contributor,都可以加入该钉钉群和我们随时随地一起交流讨论、一起贡献代码。

SOFAServerless 用户微信群


如果您对 SOFAServerless 感兴趣、或者有初步意向使用 SOFAServerless、或者已经是 SOFAServerless / SOFAArk 的用户,都可以加入该微信群随时随地一起交流讨论。

社区双周会

每两周周二晚 19:30 - 20:30 会举办社区会议,下次社区双周会时间:2023 年 11 月 28 日 19:30 ~ 20:30,欢迎大家积极参与旁听或讨论。社区钉钉会议入会方式:
入会链接:https://meeting.dingtalk.com/j/blp36k9mTbc
钉钉会议号:90957500367
电话呼入:+867936169179 (中国大陆)、+867388953916 (中国大陆)
具体会议时间也可关注社区钉钉协作群(群号:24970018417)


每个月底的周一会召开社区各组件 PMC 成员迭代规划会议,讨论并敲定下一个月需求规划。下次 PMC 成员月会时间:2023 年 11 月 27 日 19:30 ~ 20:30。入会方式同上。



3 - 贡献社区

3.1 - 本地开发测试

SOFAArk 和 Arklet

SOFAArk 是一个普通 Java SDK 项目,使用 Maven 作为依赖管理和构建工具,只需要本地安装 Maven 3.6 及以上版本即可正常开发代码和单元测试,无需其它的环境准备工作。
关于代码提交细节请参考:完成第一次 PR 提交

ModuleController

ModuleController 是一个标准的 K8S Golang Operator 组件,里面包含了 ModuleDeployment Operator、ModuleReplicaSet Operator、Module Operator,在本地可以使用 minikube 做开发测试,具体请参考本地快速开始
编译构建请在 module-controller 目录下执行:

go mod download   # if compile module-controller first time
go build -a -o manager cmd/main.go  

单元测试执行请在 module-controller 目录下执行:

make test

您也可以使用 IDE 进行编译构建、开发调试和单元测试执行。
module-controller 开发方式和标准 K8S Operator 开发方式完全一样,您可以参考 K8S Operator 开发官方文档

Arkctl

Arkctl 是一个普通 Golang 项目,他是一个命令行工具集,包含了用户在本地开发和运维模块过程中的常用工具,它和普通 Golang 程序开发完全一样,当前初始版本还在开发中


3.2 - 完成第一次 PR 提交

认领或提交 Issue

不论您是修复 bug、新增功能或者改进现有功能,在您提交代码之前,请在 SOFAServerlessSOFAArk GitHub 上认领一个 Issue 并将 Assignee 指定为自己(新人建议认领 good-first-issue 标签的新手任务)。或者提交一个新的 Issue,描述您要修复的问题或者要增加、改进的功能。这样做的好处是能避免与其他人的工作重复

获取源码

要修改或新增功能,在提 Issue 或者领取现有 Issue 后,点击左上角的fork按钮,复制一份 SOFAServerless 或 SOFAArk 主干代码到您的代码仓库。

拉分支

SOFAServerless 和 SOFAArk 所有修改都在个人分支上进行,修改完后提交 pull request,当前在跑通 PR 流水线之后,会由相应组件的 PMC 或 Maintainer 负责 Review 与合并代码到主干(master)。因此,在 fork 源码后,您需要:

  • 下载代码到本地,这一步您可以选择 git/https 方式:
git clone https://github.com/您的账号名/sofa-serverless.git
git clone https://github.com/您的账号名/sofa-ark.git
  • 拉分支准备修改代码:
git branch add_xxx_feature


执行完上述命令后,您的代码仓库就切换到相应分支了。执行如下命令可以看到您当前分支:

  git branch -a

如果您想切换回主干,执行下面命令:

  git checkout -b master

如果您想切换回分支,执行下面命令:

  git checkout -b "branchName"

修改代码提交到本地

拉完分支后,就可以修改代码了。

修改代码注意事项

  • 代码风格保持一致。SOFAServerless arklet 和 sofa-ark 通过 Maven 插件来保持代码格式一致,在提交代码前,务必先本地执行:
mvn clean compile

module-controller 和 arkctl Golang 代码的格式化能力还在建设中。

  • 补充单元测试代码。
  • 确保新修改通过所有单元测试。
  • 如果是 bug 修复,应该提供新的单元测试来证明以前的代码存在 bug,而新的代码已经解决了这些 bug。对于 arklet 和 sofa-ark 您可以用如下命令运行所有测试:
mvn clean test

对于 module-controller 和 arkctl,您可以用如下命令运行所有测试:

make test

也可以通过 IDE 来辅助运行。

其它注意事项

  • 请保持您编辑的代码使用原有风格,尤其是空格换行等。
  • 对于无用的注释,请直接删除。注释必须使用英文。
  • 对逻辑和功能不容易被理解的地方添加注释。
  • 务必第一时间更新 docs/content/zh-cn/ 目录中的 “docs”、“contribution-guidelines” 目录中的相关文档。

修改完代码后,执行如下命令提交所有修改到本地:

git commit -am '添加xx功能'

提交代码到远程仓库

在代码提交到本地后,就是与远程仓库同步代码了。执行如下命令提交本地修改到 github 上:

git push origin "branchname"

如果前面您是通过 fork 来做的,那么这里的 origin 是 push 到您的代码仓库,而不是 SOFAServerless 的代码仓库。

提交合并代码到主干的请求

在的代码提交到 GitHub 后,您就可以发送请求来把您改好的代码合入 SOFAServerless 或 SOFAArk 主干代码了。此时您需要进入您的 GitHub 上的对应仓库,按右上角的 pull request按钮。选择目标分支,一般就是 master,当前需要选择组件的 MaintainerPMC 作为 Code Reviewer,如果 PR 流水线校验和 Code Review 都通过,您的代码就会合入主干成为 SOFAServerless 的一部分。

PR 流水线校验

PR 流水线校验包括:

  1. CLA 签署。第一次提交 PR 必须完成 CLA 协议的签署,如果打不开 CLA 签署页面请尝试使用代理。
  2. 自动为每个文件追加 Apache 2.0 License 声明和作者。
  3. 执行全部单元测试且必须全部通过。
  4. 检测覆盖率是否达到行覆盖 >= 80%,分支覆盖 >= 60%。
  5. 检测提交的代码是否存在安全漏洞。
  6. 检测提交的代码是否符合基本代码规范。

以上校验必须全部通过,PR 流水线才会通过并进入到 Code Review 环节。

Code Review

当您选择对应组件的 MaintainerPMC 作为 Code Reviewer 数天后,仍然没有人对您的提交给予任何回复,可以在 PR 下面留言并 at 相关人员,或者在社区钉钉协作群中(钉钉群号:24970018417)直接 at 相关人员 Review 代码。对于 Code Review 的意见,Code Reviewer 会直接备注到到对应的 PR 或者 Issue 中,如果您觉得建议是合理的,也请您把这些建议更新到您的代码中并重新提交 PR。

合并代码到主干

在 PR 流水线校验和 Code Review 都通过后,就由 SOFAServerless 维护人员操作合入主干了,代码合并之后您会收到合并成功的提示。


3.3 - 文档、Issue、流程贡献

文档贡献

使用文档、技术文档、官网内容需要社区每一位 Contributor 共同维护,对任意文档和官网内容做出贡献的同学都是我们的 Contributor,并且根据活跃度有机会成为 SOFAServerless 组件的 Committer 甚至 PMC 成员,共同主导 SOFAServerless 的技术演进。

Issue 提交与回复贡献

任何使用过程中的问题、Bug、新功能、改进优化请创建 GitHub Issue,社区每天会有值班同学负责跟进 Issue。任何人提出或者回复 Issue 都是 SOFAServerless 的 Contributor,对回复 Issue 活跃的 Contributor 可以晋升为 Committer,如果特别活跃甚至可以晋升为 PMC 成员,共同主导 SOFAServerless 的技术演进。

Issue 模板

SOFAServerless(含 SOFAArk)Issue 有两种模板,一种是 “Question or Bug Report”,一种是 “Feature Request”。
image.png

Question or Bug Report

所有使用过程中遇到的问题或者疑似 Bug,请选择 “Question or Bug Report”,并提供详细的复现信息如下:

### Describe the question or bug

A clear and concise description of what the question or bug is.

### Expected behavior

A clear and concise description of what you expected to happen.

### Actual behavior

A clear and concise description of what actually happened.

### Steps to reproduce

Steps to reproduce the problem:

1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error

### Screenshots

If applicable, add screenshots to help explain your problem.

### Minimal yet complete reproducer code (or GitHub URL to code)

### Environment

- SOFAArk version:
- JVM version (e.g. `java -version`):
- OS version (e.g. `uname -a`):
- Maven version:
- IDE version:

Feature Request

新功能、已有功能改进优化或者其它讨论,请选择 “Feature Request”。

流程贡献

SOFAServerless 当前制定了代码规约、PR 流程、CI 流水线、迭代管理、周会、交流渠道等各种协作规范,您可以对我们的协作规范和流程在 GitHub 上提出建议,即可成为我们的 Contributor。



3.4 - 组织会议和运营布道

我们鼓励大家宣传、布道 SOFAServerless,通过运营成为 SOFAServerless 的 Contributor、Committer 甚至 PMC,每一次 Contributor 的晋升,我们也会发放纪念品奖励。运营方式包括但不限于:

  1. 在线上或线下技术会议、Meetup 中发表 SOFAServerless 的使用或者技术实现相关演讲。
  2. 与其他企业分享交流 SOFAServerless 的使用场景等。
  3. 在各种渠道发表关于 SOFAServerless 的使用或者技术实现相关文章或视频。
  4. 其它运营方式。

4 - 社区角色与晋升

角色职责与晋升机制

SOFAServerless 社区角色参考了 Apache 开源产品组织方式,SOFAArk、Arklet、ModuleController、ArkCtl 每个组件都有各自的角色。每个组件的角色职责从低到高分别是:Contributor、Committer、PMC (Project Management Committee)、Maintainer

角色责任与权限晋升到更高角色机制
Contributor所有在社区提 Issue、回答 Issue、对外运营、提交文档内容或者提交任意代码的同学,都是相应组件的 Contributor。Contributor 拥有 Issue 提交、Issue 回复、官网或文档内容提交、代码提交(不包括代码评审)和对外发表文章权限。当 Contributor 完成合并的代码或者文档内容足够多,就可以由该组件的 PMC 成员投票晋升为 Committer。当 Contributor 回答的 Issue 或者参与的运营活动足够多,也可以被 PMC 成员投票晋升为 Committer。
Committer所有在社区积极回答 Issue、对外运营、提交文档内容或者提交代码的同学,按积极度都有可能被 PMC 成员投票晋升为 Committer。Committer 额外拥有代码评审、技术方案评审、Contributor 培养的责任与权限。对长期积极投入或持续有突出贡献的 Committer,经 PMC 成员投票可以晋升为相应组件的 PMC 成员。
PMC对相应组件持续贡献特别活跃的同学有机会晋升为 PMC 成员。PMC 成员额外拥有组件的 RoadMap 制定、技术方案和代码评审、Issue 和迭代管理、Contributor 和 Committer 培养等责任与权限。
MaintainerMaintainer 额外拥有密钥管理和仓库管理等管理员权限,除此之外在其他方面和 PMC 成员的责任与权限是完全对等的。

社区角色成员名单

SOFAArk

Maintainer

yuanyuancin
lvjing2

PMC (Project Management Comittee)

glmapper

Committer

zjulbj5
gaosaroma
QilongZhang133
straybirdzls13
caojie0911

Contributor

lylingzhen10
khotyn
FlyAbner (260+ 行提交,提名 Comitter?)
alaneuler
sususama
ujjboy
JoeKerouac
Lunarscave
HzjNeverStop
AiWu4Damon
vchangpengfei
HuangDayu
shenchao45
DalianRollingKing
nobodyiam
lanicc
azhsmesos
wuqian0808
KangZhiDong
suntao4019
huangyunbin
jiangyunpeng
michalyao
rootsongjc
Zwl0113
tofdragon
lishiguang4
hionwi
343585776
g-stream
zkitcast
davidzj
zyclove
WindSearcher
lovejin52022
smalljunHw
vchangpengfei
sq1015
xwh1108
yuanChina
blysin
yuwenkai666
hadoop835
gitYupan
thirdparty-core
Estom
jijuanwang
DCLe-DA
linkoog
springcoco
zhaowwwjian
xingcici
ixufeng
jnan806
lizhi12q
kongqq
wangxiaotao00
由于篇幅有限,23 年之前提交 Issue 的 Contributor 不在此一一列举,也同样感谢大家对 SOFAArk 的使用和咨询

Arklet

Maintainer

yuanyuancin
lvjing2

PMC (Project Management Committee)

TomorJM

Committer

暂无

Contributor

glmapper
Lunarscave
lylingzhen

ModuleController

Maintainer

gold300jin

PMC (Project Management Committee)

暂无

Committer

暂无

Contributor

liu-657667
Charlie17Li
lylingzhen

Arkctl

Maintainer

yuanyuancin
lvjing2

PMC (Project Management Committee)

暂无

Committer

暂无

Contributor

暂无


6 - Arklet 技术文档

6.1 - Arklet 架构设计与接口设计

概述

Arklet 为 SofaArk 基础和模块的交付提供了一个操作接口。有了 Arklet,Ark Biz 的发布和操作可以轻松灵活地进行。

Arklet 是由 ArkletComponent 内部构建的

image

  • ApiClient: 负责与外界交互的核心组件
  • CommandService: Arklet 对外暴露能力指令定义和扩展
  • OperationService: Ark Biz 与 SofaArk 交互,进行添加、删除、修改和封装基本能力
  • HealthService: 基于健康和稳定性,计算基础、Biz、系统等其他指标

他们之间的协作如图所示 overview

当然,您也可以通过实现 ArkletComponent 接口来扩展 Arklet 的组件功能

命令扩展

Arklet 外部公开了指令 API,并通过每个 API 映射的 CommandHandler 内部处理指令。

CommandHandler 相关的扩展属于 CommandService 组件的统一管理

您可以通过继承 AbstractCommandHandler 来自定义扩展命令

内置命令 API

以下所有的指令 api 都使用 POST(application/json) 请求格式访问 arklet

启用了 http 协议,默认端口是 1238

您可以设置 sofa.serverless.arklet.http.port JVM 启动参数覆盖默认端口

查询支持的命令

  • URL: 127.0.0.1:1238/help
  • 输入样例:
{}
  • 输出样例:
{
    "code":"SUCCESS",
    "data":[
        {
            "desc":"query all ark biz(including master biz)",
            "id":"queryAllBiz"
        },
        {
            "desc":"list all supported commands",
            "id":"help"
        },
        {
            "desc":"uninstall one ark biz",
            "id":"uninstallBiz"
        },
        {
            "desc":"switch one ark biz",
            "id":"switchBiz"
        },
        {
            "desc":"install one ark biz",
            "id":"installBiz"
        }
    ]
}

安装一个 biz

  • URL: 127.0.0.1:1238/installBiz
  • 输入样例:
{
    "bizName": "test",
    "bizVersion": "1.0.0",
    // local path should start with file://, alse support remote url which can be downloaded
    "bizUrl": "file:///Users/jaimezhang/workspace/github/sofa-ark-dynamic-guides/dynamic-provider/target/dynamic-provider-1.0.0-ark-biz.jar"
}
  • 输出样例(成功):
{
  "code":"SUCCESS",
  "data":{
    "bizInfos":[
      {
        "bizName":"dynamic-provider",
        "bizState":"ACTIVATED",
        "bizVersion":"1.0.0",
        "declaredMode":true,
        "identity":"dynamic-provider:1.0.0",
        "mainClass":"io.sofastack.dynamic.provider.ProviderApplication",
        "priority":100,
        "webContextPath":"provider"
      }
    ],
    "code":"SUCCESS",
    "message":"Install Biz: dynamic-provider:1.0.0 success, cost: 1092 ms, started at: 16:07:47,769"
  }
}

-输出样例(失败):

{
  "code":"FAILED",
  "data":{
    "code":"REPEAT_BIZ",
    "message":"Biz: dynamic-provider:1.0.0 has been installed or registered."
  }
}

卸载模块

  • URL: 127.0.0.1:1238/uninstallBiz
  • 输入样例:
{
    "bizName":"dynamic-provider",
    "bizVersion":"1.0.0"
}

-输出样例(成功):

{
  "code":"SUCCESS"
}
  • 输出样例(失败):
{
  "code":"FAILED",
  "data":{
    "code":"NOT_FOUND_BIZ",
    "message":"Uninstall biz: test:1.0.0 not found."
  }
}

Switch a biz

  • URL: 127.0.0.1:1238/switchBiz
  • 输出样例:
{
    "bizName":"dynamic-provider",
    "bizVersion":"1.0.0"
}
  • 输出样例:
{
  "code":"SUCCESS"
}

查询所有 Biz

  • URL: 127.0.0.1:1238/queryAllBiz
  • 输入样例:
{}
  • 输出样例:
{
  "code":"SUCCESS",
  "data":[
    {
      "bizName":"dynamic-provider",
      "bizState":"ACTIVATED",
      "bizVersion":"1.0.0",
      "mainClass":"io.sofastack.dynamic.provider.ProviderApplication",
      "webContextPath":"provider"
    },
    {
      "bizName":"stock-mng",
      "bizState":"ACTIVATED",
      "bizVersion":"1.0.0",
      "mainClass":"embed main",
      "webContextPath":"/"
    }
  ]
}

查询健康状况

  • URL: 127.0.0.1:1238/health

以下根据不同的输入参数,获取到不同的状态信息

查询健康状况

  • 输入样例:
{}
  • 输出样例:
{
  "code": "SUCCESS",
  "data": {
    "healthData": {
      "jvm": {
        "max non heap memory(M)": -9.5367431640625E-7,
        "java version": "1.8.0_331",
        "max memory(M)": 885.5,
        "max heap memory(M)": 885.5,
        "used heap memory(M)": 137.14127349853516,
        "used non heap memory(M)": 62.54662322998047,
        "loaded class count": 10063,
        "init non heap memory(M)": 2.4375,
        "total memory(M)": 174.5,
        "free memory(M)": 37.358726501464844,
        "unload class count": 0,
        "total class count": 10063,
        "committed heap memory(M)": 174.5,
        "java home": "****\\jre",
        "init heap memory(M)": 64.0,
        "committed non heap memory(M)": 66.203125,
        "run time(s)": 34.432
      },
      "cpu": {
        "count": 4,
        "total used (%)": 131749.0,
        "type": "****",
        "user used (%)": 9.926451054656962,
        "free (%)": 81.46475495070172,
        "system used (%)": 6.249762806548817
      },
      "masterBizInfo": {
        "webContextPath": "/",
        "bizName": "bookstore-manager",
        "bizState": "ACTIVATED",
        "bizVersion": "1.0.0"
      },
      "pluginListInfo": [
        {
          "artifactId": "web-ark-plugin",
          "groupId": "com.alipay.sofa",
          "pluginActivator": "com.alipay.sofa.ark.web.embed.WebPluginActivator",
          "pluginName": "web-ark-plugin",
          "pluginUrl": "file:/****/2.2.3-SNAPSHOT/web-ark-plugin-2.2.3-20230901.090402-2.jar!/",
          "pluginVersion": "2.2.3-SNAPSHOT"
        },
        {
          "artifactId": "runtime-sofa-boot-plugin",
          "groupId": "com.alipay.sofa",
          "pluginActivator": "com.alipay.sofa.runtime.ark.plugin.SofaRuntimeActivator",
          "pluginName": "runtime-sofa-boot-plugin",
          "pluginUrl": "file:/****/runtime-sofa-boot-plugin-3.11.0.jar!/",
          "pluginVersion": "3.11.0"
        }
      ],
      "masterBizHealth": {
        "readinessState": "ACCEPTING_TRAFFIC"
      },
      "bizListInfo": [
        {
          "bizName": "bookstore-manager",
          "bizState": "ACTIVATED",
          "bizVersion": "1.0.0",
          "webContextPath": "/"
        }
      ]
    }
  }
}

查询系统健康信息

  • 输入样例:
{
  "type": "system",
  // [OPTIONAL] if metrics is null -> query all system health info
  "metrics": ["cpu", "jvm"]
}
  • 输出样例:
{
  "code": "SUCCESS",
  "data": {
    "healthData": {
      "jvm": {...},
      "cpu": {...},
//      "masterBizHealth": {...}
    }
  }
}

查询模块健康信息

  • 输入样例:
{
  "type": "biz",
  // [OPTIONAL] if moduleName is null and moduleVersion is null -> query all biz
  "moduleName": "bookstore-manager",
  // [OPTIONAL] if moduleVersion is null -> query all biz named moduleName
  "moduleVersion": "1.0.0"
}
  • 输出样例:
{
  "code": "SUCCESS",
  "data": {
    "healthData": {
      "bizInfo": {
        "bizName": "bookstore-manager",
        "bizState": "ACTIVATED",
        "bizVersion": "1.0.0",
        "webContextPath": "/"
      }
//      "bizListInfo": [
//        {
//          "bizName": "bookstore-manager",
//          "bizState": "ACTIVATED",
//          "bizVersion": "1.0.0",
//          "webContextPath": "/"
//        }
//      ]
    }
  }
}

查询插件健康信息

  • 输入样例:
{
  "type": "plugin",
  // [OPTIONAL] if moduleName is null -> query all biz
  "moduleName": "web-ark-plugin"
}
  • 输出样例:
{
  "code": "SUCCESS",
  "data": {
    "healthData": {
      "pluginListInfo": [
        {
          "artifactId": "web-ark-plugin",
          "groupId": "com.alipay.sofa",
          "pluginActivator": "com.alipay.sofa.ark.web.embed.WebPluginActivator",
          "pluginName": "web-ark-plugin",
          "pluginUrl": "file:/****/web-ark-plugin-2.2.3-20230901.090402-2.jar!/",
          "pluginVersion": "2.2.3-SNAPSHOT"
        }
      ]
    }
  }
}

使用端点查询健康状况

使用端点获取 k8s 模块的健康信息

默认配置

  • 端点暴露包括:*
  • 端点基本路径:/
  • 端点服务器端口:8080

http 代码结果

  • HEALTHY(200):如果所有健康指标都是健康的,获取健康信息
  • UNHEALTHY(400):一旦健康指标不健康,获取健康信息
  • ENDPOINT_NOT_FOUND(404):找不到端点路径或参数
  • ENDPOINT_PROCESS_INTERNAL_ERROR(500):获取健康过程中抛出错误

查询所有健康信息

curl 127.0.0.1:8080/arkletHealth
  • 输出样例
{   
    "healthy": true,
    "code": 200,    
    "codeType": "HEALTHY",    
    "data": {        
        "jvm": {...},        
        "masterBizHealth": {...},        
        "cpu": {...},        
        "masterBizInfo": {...},        
        "bizListInfo": [...],        
        "pluginListInfo": [...]    
    }
}  

查询所有 biz/plugin 健康信息

curl: 127.0.0.1:8080/arkletHealth/{moduleType} (moduleType 必须在 ['biz', 'plugin'])
  • 输出样例
{   
   "healthy": true,
   "code": 200,    
   "codeType": "HEALTHY",    
   "data": {        
       "bizListInfo": [...],  
       // "pluginListInfo": [...]      
   }
}  

查询单个 biz/plugin 健康信息

curl 127.0.0.1:8080/arkletHealth/{moduleType}/moduleName/moduleVersion (moduleType must in ['biz', 'plugin'])
  • 输出样例:
{   
   "healthy": true,
   "code": 200,    
   "codeType": "HEALTHY",    
   "data": {        
       "bizInfo": {...},  
       // "pluginInfo": {...}      
   }
}

6.2 - 如何发布 Arklet 版本

触发 github Action 发布到 snapshot staging

版本发布到 maven 中央仓库,发布能力集成到了 github action 里:
image.png

该 action 需要手动触发执行
image.png
执行成功后,只会发布到 snapshot staging,如果是 SNAPSHOT 版本,则这里执行完就可以结束。如果是正式版本,发布到 snapshot staging 之后,还需要推送到 release staging。

发布到 Release staging

打开  https://oss.sonatype.org ,点击右上角的 Log In, 登陆信息可找管理员。
点击左侧的 Staging Repositories:

搜索刚才记录的 ID:

钩上之后就可以进行 Release (发布) 或者 Drop (放弃) 的操作。

当看不到这两个选项,只有 Close 选项时,则先选择 Close 操作,这时候如果包没有问题,则接下来可以 Release 或者 Drop。如果有问题,下面的内容中的 Activity 中会显示包不能正常 Close 的原因, 按照提示进行修改就可以了。

仓库包同步与搜索

在包发布到 release 仓库之后, 10 分钟后包会更新,在 http://central.maven.org/maven2/com/alipay/sofa/ 能看到包。2 小时之后,可通过 搜索 查询到包。


7 - ModuleController 技术文档

7.1 - ModuleController 架构设计

介绍

ModuleController 是一个 K8S 控制器,该控制器参考 K8S 架构,定义并且实现了 ModuleDeployment、ModuleReplicaSet、Module 等核心模型与调和能力,从而实现了 Serverless 模块的秒级运维调度,以及与基座的联动运维能力。

基本架构

ModuleController 目前包含 ModuleDeployment Opertor、ModuleReplicaSet Operator、Module Operator 三个组件。和 K8S 原生 Deployment 类似,用户创建 ModuleDeployment 会调和出 ModuleReplicaSet,ModuleReplicaSet 会进一步调和出 Module,最终 Module Operator 会调用 Pod 里的 Arklet SDK 去安装或卸载模块。此外 ModuleController 还会为 ModuleDeployment 自动生成 K8S Service,企业可以监听该 Service 的 IP 变化实现与自身流量控制系统的集成,从而实现模块粒度的切流和挂流。

功能清单和 RoadMap

  • 08.15:0.2 版本上线(包括非对等模块发布、卸载、扩缩容、副本保持、基座运维联动)
  • 08.25:0.3 版本上线(包括回滚链路、各项参数校验、单测达到 80/60、CI 自动化、开发者指南)
  • 09.31:0.5 版本上线(1:1 先扩后缩、模块回滚、两种调度策略、状态回流、1+ 端到端集成测试)
  • 10.30:0.6 版本上线(支持以 K8S Service 方式联动企业四七层流量控制、总计 10+ 端到端集成测试)
  • 11.30:1.0 版本上线(支持对等发布运维、各项修复打磨、总计 20+ 端到端集成测试)
  • 12.30:1.1 版本上线(支持模块和基座自动弹性伸缩、对等与非对等发布运维能力完善)

7.2 - CRD 模型设计

CRD 模型对比

K8S 原生 CRDModuleController CRD关系和区别
PodModulePod:K8S 中创建和管理的、最小的可部署的计算单元。     Module:Serverless 创建和管理的、最小的可部署的计算单元。
PodSpecModuleSpecPodSpec:对 Pod 的描述。包含容器、调度、卷等。     ModuleSpec:对 Module 的描述,包含模块、服务、调度(亲和性)。
PodTemplateModuleTemplatePodTemplate:定义 Pod 的生成副本,包含 PodSpec。     ModuleTemplate:定义 Module 的生成副本,包含 ModuleGroupSpec。
DeploymentModuleDeploymentDeployment:定义 Pod 的期望状态和副本数量。     ModuleDeployment:定义 Module 的期望状态和副本数量。
ReplicaSetModuleReplicaSetReplicaSet:管理 Pod 的运行副本。    
ModuleReplicaSet:管理 Module 的运行副本。

ModuleDeployment CRD 模型

image

Module CRD 模型

image

ModuleTemplate CRD 模型

image

ModuleReplicaSet CRD 模型

image


7.3 - 核心代码结构

image.png

image.png


核心代码逻辑在 moduledeployment_controller.go、modulereplicaset_controller.go、module_controller.go、controller_utils.go,里面有详细注释。



7.4 - 模块生命周期

模块生命周期

4象限描述了模块的生命周期: Prepare、Upgrading、Completed、Available

image

模块状态机

image


7.5 - 核心流程时序图

模块首发

image

模块二发

image

模块下线

image

对等基座扩容

image

对等基座缩容

image

8 - Arkctl 技术文档

8.1 - Arkctl 技术文档

Arkctl 是一个普通 Golang 项目,他是一个命令行工具集,包含了用户在本地开发和运维模块过程中的常用工具,它和普通 Golang 程序开发完全一样,当前初始版本还在开发中


9 - 多模块运行时适配或最佳实践

9.1 - log4j2 的多模块化适配

为什么需要做适配

原生 log4j2 在多模块下,模块没有独立打印的日志目录,统一打印到基座目录里,导致日志和对应的监控无法隔离。这里做适配的目的就是要让模块能有独立的日志目录。

普通应用 log4j2 的初始化

在 Spring 启动前,log4j2 会使用默认值初始化一次各种 logContext 和 Configuration,然后在 Spring 启动过程中,监听 Spring 事件进行初始化 org.springframework.boot.context.logging.LoggingApplicationListener,这里会调用到 Log4j2LoggingSystem.initialize 方法

该方法会根据 loggerContext 来判断是否已经初始化过了

这里在多模块下会存在问题一

这里的 getLoggerContext 是根据 org.apache.logging.log4j.LogManager 所在 classLoader 来获取 LoggerContext。根据某个类所在 ClassLoader 来提取 LoggerContext 在多模块化里会存在不稳定,因为模块一些类可以设置为委托给基座加载,所以模块里启动的时候,可能拿到的 LoggerContext 是基座的,导致这里 isAlreadyInitialized 直接返回,导致模块的 log4j2 日志无法进一步根据用户配置文件配置。

如果没初始化过,则会进入 super.initialize, 这里需要做两部分事情:

  1. 获取到日志配置文件
  2. 解析日志配置文件里的变量值 这两部分在多模块里都可能存在问题,先看下普通应用过程是如何完成这两步的

获取日志配置文件

可以看到是通过 ResourceUtils.getURL 获取的 location 对应日志配置文件的 url,这里通过获取到当前线程上下文 ClassLoader 来获取 URL,这在多模块下没有问题(因为每个模块启动时线程上下文已经是 模块自身的 ClassLoader )。

解析日志配置值

配置文件里有一些变量,例如这些变量

这些变量的解析逻辑在 org.apache.logging.log4j.core.lookup.AbstractLookup 的具体实现里,包括

变量写法代码逻辑地址
${bundle:application:logging.file.path}org.apache.logging.log4j.core.lookup.ResourceBundleLookup根据 ResourceBundleLookup 所在 ClassLoader 提前到 application.properties, 读取里面的值
${ctx:logging.file.path}org.apache.logging.log4j.core.lookup.ContextMapLookup根据 LoggerContext 上下文 ThreadContex 存储的值来提起,这里需要提前把 applicaiton.properties 的值设置到 ThreadContext 中

根据上面判断通过 bundle 的方式配置在多模块里不可行,因为 ResourceBundleLookup 可能只存在于基座中,导致始终只能拿到基座的 application.properties,导致模块的日志配置路径与基座相同,模块日志都打到基座中。所以需要改造成使用 ContextMapLookup。

预期多模块合并下的日志

基座与模块都能使用独立的日志配置、配置值,完全独立。但由于上述分析中,存在两处可能导致模块无法正常初始化的逻辑,故这里需要多 log4j2 进行适配。

多模块适配点

  1. getLoggerContext() 能拿到模块自身的 LoggerContext

  2. 需要调整成使用 ContextMapLookup,从而模块日志能获取到模块应用名,日志能打印到模块目录里

    a. 模块启动时将 application.properties 的值设置到 ThreadContext 中 b. 日志配置时,只能使用 ctx:xxx:xxx 的配置方式

模块改造方式

详细查看源码

9.2 - ehcache 的多模块化最佳实践

为什么需要最佳实践

CacheManager 初始化的时候存在共用 static 变量,多应用使用相同的 ehcache name,导致缓存互相覆盖。

最佳实践的几个要求

  1. 基座里必须引入 ehcache,模块里复用基座

在 springboot 里 ehcache 的初始化需要通过 Spring 里定义的 EhCacheCacheConfiguration 来创建,由于 EhCacheCacheConfiguration 是属于 Spring, Spring 统一放在基座里。 image.png

这里在初始化的时候,在做 Bean 初始化的条件判断时会走到类的检验, image.png 如果 net.sf.ehcache.CacheManager 是。这里会走到 java native 方法上做判断,从当前类所在的 ClassLoader 里查找 net.sf.ehcache.CacheManager 类,所以基座里必须引入这个依赖,否则会报 ClassNotFound 的错误。 image.png

  1. 模块里将引入的 ehcache 排包掉(scope设置成 provide,或者使用自动瘦身能力)

模块使用自己 引入的 ehcache,照理可以避免共用基座 CacheManager 类里的 static 变量,而导致报错的问题。但是实际测试发现,模块安装的时候,在初始化 enCacheCacheManager 时, image.png image.png 这里在 new 对象时,需要先获得对象所属类的 CacheManager 是基座的 CacheManager。这里也不能讲 CacheManager 由模块 compile 引入,否则会出现一个类由多个不同 ClassLoader 引入导致的问题。 image.png

所以结论是,这里需要全部委托给基座加载。

最佳实践的方式

  1. 模块 ehcache 排包瘦身委托给基座加载
  2. 如果多个模块里有多个相同的 cacheName,需要修改 cacheName 为不同值。
  3. 如果不想改代码的方式修改 cache name,可以通过打包插件的方式动态替换 cacheName
 <plugin>
    <groupId>com.google.code.maven-replacer-plugin</groupId>
    <artifactId>replacer</artifactId>
    <version>1.5.3</version>
    <executions>
        <!-- 打包前进行替换 -->
        <execution>
            <phase>prepare-package</phase>
            <goals>
                <goal>replace</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <!-- 自动识别到项目target文件夹 -->
        <basedir>${build.directory}</basedir>
        <!-- 替换的文件所在目录规则 -->
        <includes>
            <include>classes/j2cache/*.properties</include>
        </includes>
        <replacements>
            <replacement>
                <token>ehcache.ehcache.name=f6-cache</token>
                <value>ehcache.ehcache.name=f6-${parent.artifactId}-cache</value>
            </replacement>

        </replacements>
    </configuration>
</plugin>
  1. 需要把 FactoryBean 的 shared 设置成 false
@Bean
    public EhCacheManagerFactoryBean ehCacheManagerFactoryBean() {
        EhCacheManagerFactoryBean factoryBean = new EhCacheManagerFactoryBean();

        // 需要把 factoryBean 的 share 属性设置成 false
        factoryBean.setShared(true);
//        factoryBean.setShared(false);
        factoryBean.setCacheManagerName("biz1EhcacheCacheManager");
        factoryBean.setConfigLocation(new ClassPathResource("ehcache.xml"));
        return factoryBean;
    }

否则会进入这段逻辑,初始化 CacheManager 的static 变量 instance. 该变量如果有值,且如果模块里 shared 也是ture 的化,就会重新复用 CacheManager 的 instance,从而拿到基座的 CacheManager, 从而报错。 image.png image.png

最佳实践的样例

样例工程请参考这里

9.3 -