Skip to content

贡献者入门指南

欢迎来到 CoSky。本指南将带你从零开始成为一名高效的贡献者。我们假设你已经了解 Kotlin 或 Java,熟悉 Spring Boot,并对 Redis 有基本认识。其余内容将在本指南中学习。

第一部分 — 基础知识

CoSky 是什么?

CoSky 是一个高性能的微服务治理平台,提供服务发现配置管理功能,完全基于 Redis 构建。与部署独立的协调服务器(如 etcd、Consul 或 ZooKeeper)不同,CoSky 将你现有的 Redis 基础设施转化为服务网格控制平面。

项目名称来源于 Co(Configuration,配置)+ Sky(Service Discovery,服务发现)。项目采用 Apache 2.0 开源协议。

技术栈

层级技术
语言Kotlin(JVM 17 工具链)
框架Spring Boot 3.x、Spring Cloud
响应式Project Reactor(Mono/Flux
存储Redis(通过 ReactiveStringRedisTemplate
原子性所有变更操作通过 Lua 脚本实现
构建Gradle(Kotlin DSL)
测试JUnit 5、MockK、FluentAssert
代码风格Detekt
基准测试JMH(Java Microbenchmark Harness)

构建项目

bash
# 克隆仓库
git clone https://github.com/Ahoo-Wang/CoSky.git
cd CoSky

# 构建全部(编译 + 测试)
./gradlew build

# 跳过测试构建(开发时更快)
./gradlew build -x test

# 检查代码风格
./gradlew detekt

# 运行指定模块的测试
./gradlew :cosky-config:test
./gradlew :cosky-discovery:test

# 本地运行 REST API 服务器
./gradlew :cosky-rest-api:bootRun

# 运行基准测试
./gradlew :cosky-config:jmh
./gradlew :cosky-discovery:jmh

Redis 要求

集成测试需要运行中的 Redis 实例。测试基类 AbstractReactiveRedisTest 默认连接 localhost:6379。请在运行测试前启动 Redis:

bash
# Docker 方式
docker run -d -p 6379:6379 redis:latest

# 或通过 Homebrew
brew services start redis

第二部分 — 架构与领域

模块结构

CoSky 采用多模块 Gradle 项目组织。在进行修改之前,理解依赖关系图至关重要。

mermaid
flowchart TB
    subgraph "核心层"
        CORE["cosky-core<br>命名空间、Redis 键、<br>PubSub 基础"]
    end

    subgraph "领域层"
        CONFIG["cosky-config<br>配置增删改查、<br>版本管理、回滚"]
        DISCOVERY["cosky-discovery<br>服务注册、<br>发现、负载均衡"]
    end

    subgraph "Spring Cloud 层"
        SCCORE["cosky-spring-cloud-core<br>自动配置、<br>共享属性"]
        SCCONFIG["cosky-spring-cloud-starter-config<br>属性源定位器、<br>配置刷新"]
        SCDISC["cosky-spring-cloud-starter-discovery<br>服务注册、<br>发现客户端"]
    end

    subgraph "服务层"
        RESTAPI["cosky-rest-api<br>REST 控制器、<br>仪表盘、RBAC"]
    end

    subgraph "辅助模块"
        BOM["cosky-bom"]
        DEPS["cosky-dependencies"]
        TEST["cosky-test"]
    end

    CONFIG --> CORE
    DISCOVERY --> CORE
    SCCORE --> CORE
    SCCONFIG --> CONFIG
    SCCONFIG --> SCCORE
    SCDISC --> DISCOVERY
    SCDISC --> SCCORE
    RESTAPI --> CONFIG
    RESTAPI --> DISCOVERY

    style CORE fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style CONFIG fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style DISCOVERY fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style SCCORE fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style SCCONFIG fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style SCDISC fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style RESTAPI fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style BOM fill:#2d333b,stroke:#30363d,color:#8b949e
    style DEPS fill:#2d333b,stroke:#30363d,color:#8b949e
    style TEST fill:#2d333b,stroke:#30363d,color:#8b949e
模块用途核心接口
cosky-core命名空间管理、Redis 键工具、PubSub 事件基础NamespaceServiceEventListenerContainerNamespaced
cosky-config配置增删改查、版本管理、回滚、一致性缓存ConfigServiceConfigRollback
cosky-discovery服务注册、发现、负载均衡、拓扑关系ServiceRegistryServiceDiscoveryLoadBalancer
cosky-spring-cloud-core共享 Spring Boot 自动配置CoSkyPropertiesCoSkyAutoConfiguration
cosky-spring-cloud-starter-configSpring Cloud 配置加载与刷新CoSkyPropertySourceLocatorCoSkyConfigRefresher
cosky-spring-cloud-starter-discoverySpring Cloud 服务注册与发现CoSkyDiscoveryClientCoSkyAutoServiceRegistration
cosky-rest-apiREST API 服务器、仪表盘、安全、RBACServiceControllerConfigController
cosky-bom依赖管理的物料清单
cosky-test共享测试工具、Lua 清理脚本AbstractReactiveRedisTest

关键规则cosky-core 不依赖任何其他 CoSky 模块。依赖图必须保持无环。

一致性模式:本地缓存 + PubSub 失效

这是 CoSky 中最重要的架构概念。请仔细阅读。

CoSky 通过以下组合实现极高的读取性能:

  1. 本地进程内缓存 — 数据存储在每个应用进程内的 ConcurrentHashMap 中。读取以纳秒级速度命中此缓存。
  2. Redis PubSub — 当 Redis 中的数据发生变化时,PubSub 消息通知所有订阅者使其失效或刷新本地缓存。
  3. 懒订阅 — 缓存条目在首次访问时创建,并自动订阅该键的 PubSub 事件。
mermaid
sequenceDiagram
    autonumber
    participant App1 as 应用 1
    participant Redis as Redis
    participant App2 as 应用 2

    Note over App1,App2: 初始读取(缓存未命中)
    App1->>Redis: getConfig("database.yaml")
    Redis-->>App1: 配置数据
    App1->>Redis: SUBSCRIBE 配置变更

    Note over App1,App2: 第二个应用读取
    App2->>Redis: getConfig("database.yaml")
    Redis-->>App2: 配置数据
    App2->>Redis: SUBSCRIBE 配置变更

    Note over App1,App2: 配置更新
    App1->>Redis: setConfig("database.yaml", 新数据)
    Redis->>Redis: Lua 脚本:写入 + PUBLISH
    Redis-->>App1: PubSub 通知
    Redis-->>App2: PubSub 通知

    Note over App1,App2: 缓存刷新
    App1->>Redis: getConfig("database.yaml")
    App2->>Redis: getConfig("database.yaml")
    App1->>App1: 更新本地缓存
    App2->>App2: 更新本地缓存

一致性包装器包括:

性能提升非常显著:

操作直接访问 Redis使用一致性层
getConfig~241K ops/s~257M ops/s(快 1000 倍)
getInstances~227K ops/s~77M ops/s(快 340 倍)
getServices~305K ops/s~456M ops/s(快 1500 倍)

Redis 键模式

所有 Redis 键都是命名空间作用域的。命名空间作为租户分隔符,使用 Redis 哈希标签 {...} 包裹以兼容集群模式。

配置键ConfigKeyGenerator):

用途键模式Redis 类型
配置索引{namespace}:cfg_idxSET
当前配置{namespace}:cfg:{configId}HASH
历史索引{namespace}:cfg_htr_idx:{configId}ZSET
历史版本{namespace}:cfg_htr:{configId}:{version}HASH

服务发现键DiscoveryKeyGenerator):

用途键模式Redis 类型
服务索引{namespace}:svc_idxSET
服务统计{namespace}:svc_statHASH
实例索引{namespace}:svc_itc_idx:{serviceId}SET
实例数据{namespace}:svc_itc:{instanceId}HASH

为什么使用哈希标签? Redis Cluster 根据 {} 之间的文本将键路由到槽位。包裹命名空间可确保同一命名空间的所有键落在同一分片上,从而通过 Lua 脚本实现跨键的原子操作。参见 cosky-core/src/main/kotlin/me/ahoo/cosky/core/util/RedisKeys.kt:24

Lua 脚本实现原子操作

所有写操作都以 Lua 脚本的形式在 Redis 内部执行。这保证了原子性 — 没有客户端能观察到部分完成的状态。代码库中没有多命令事务;一切都是单次 Lua 脚本调用。

关键 Lua 脚本:

脚本模块用途
config_set.luacosky-config原子写入 + 版本管理 + 历史 + 发布
config_remove.luacosky-config原子删除 + 历史 + 发布
config_rollback.luacosky-config原子回滚到目标版本 + 发布
registry_register.luacosky-discovery原子注册实例 + 设置 TTL + 发布
registry_deregister.luacosky-discovery原子注销 + 删除键 + 发布
registry_renew.luacosky-discovery带节流发布的心跳续约
discovery_get_instances.luacosky-discovery读取实例 + 懒过期清理无效实例

每个 Lua 脚本遵循相同的模式:

  1. 读取当前状态
  2. 检查前置条件(例如哈希比较以跳过无变更写入)
  3. 执行变更操作
  4. 向该键对应的频道 PUBLISH 通知
  5. 返回状态码

registry_renew.lua 中的节流发布机制尤其值得注意 — 它仅在上次 TTL 发布即将过期时才发布 renew 事件,将 PubSub 流量减少了一个数量级。参见 cosky-discovery/src/main/resources/registry_renew.lua:8

事件模型

CoSky 使用 Redis PubSub 传播状态变更。事件是基于字符串的操作码,发布到与键名直接对应的 Redis 频道。

配置事件ConfigChangedEvent):

事件触发条件操作码
SET配置被创建或更新"set"
REMOVE配置被删除"remove"
ROLLBACK配置回滚到先前版本"rollback"

实例事件InstanceChangedEvent):

事件触发条件操作码
REGISTER新实例注册"register"
DEREGISTER实例被显式移除"deregister"
EXPIRED实例 TTL 过期(懒清理)"expired"
RENEW实例心跳续约 TTL"renew"
SET_METADATA实例元数据更新"set_metadata"

第三部分 — 参与贡献

开发环境搭建

mermaid
flowchart TD
    CLONE["克隆仓库<br>git clone https://github.com/Ahoo-Wang/CoSky"] --> JDK["安装 JDK 17<br>brew install openjdk@17"]
    JDK --> REDIS["启动 Redis<br>docker run -d -p 6379:6379 redis"]
    REDIS --> BUILD["构建项目<br>./gradlew build"]
    BUILD --> IDE["在 IntelliJ IDEA 中打开<br>作为 Gradle 项目导入"]
    IDE --> DETEKT["运行 Detekt<br>./gradlew detekt"]
    DETEKT --> TESTS["运行测试<br>./gradlew test"]
    TESTS --> READY["准备就绪,开始贡献!"]

    style CLONE fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style JDK fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style REDIS fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style BUILD fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style IDE fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style DETEKT fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style TESTS fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style READY fill:#2d333b,stroke:#6d5dfc,color:#e6edf3

前置条件:

  • JDK 17build.gradle.kts 中的 JVM 工具链配置要求
  • Redis — 集成测试必需
  • IntelliJ IDEA — 推荐(Kotlin 支持一流)

运行测试

bash
# 运行所有模块的全部测试
./gradlew test

# 运行指定模块的测试
./gradlew :cosky-core:test
./gradlew :cosky-config:test
./gradlew :cosky-discovery:test

# 运行单个测试类
./gradlew :cosky-config:test --tests "me.ahoo.cosky.config.redis.RedisConfigServiceTest"

# 运行测试并显示详细输出
./gradlew :cosky-config:test --info

所有测试类使用 JUnit 5 和 me.ahoo.test:fluent-assert-core 库进行断言。编写测试时,请始终使用:

kotlin
import me.ahoo.test.asserts.assert

// 正确用法:
actual.assert().isEqualTo(expected)

// 不要使用 AssertJ 的 assertThat() — 它在 Kotlin 中冗长且不是空安全的

代码风格

CoSky 使用 Detekt 进行静态分析,并启用了自动纠正:

bash
# 检查代码风格违规
./gradlew detekt

# Detekt 自动纠正默认在 build.gradle.kts 中启用
# 它会在构建期间自动修复格式问题

主要代码风格规则:

  • 所有源文件必须包含 Apache 2.0 许可证头部
  • 编译器标志:-Xjsr305=strict-Xjvm-default=all-compatibility
  • 所有核心 API 返回 MonoFlux(Project Reactor)
  • 所有 Redis 变更操作通过 Lua 脚本执行 — 禁止使用多命令序列

PR 流程

  1. Fork 仓库(如果你有推送权限,也可以创建分支)
  2. main 创建特性分支git checkout -b feature/my-feature
  3. 先编写测试 — 所有新功能和 Bug 修复必须有测试
  4. 运行完整构建./gradlew build detekt
  5. 使用描述性消息提交
  6. main 发起 Pull Request
  7. CI 将运行集成测试、基准测试、代码覆盖率和 Detekt 检查

可以贡献的方向

适合新贡献者的任务:

  • 新的负载均衡策略 — 实现 LoadBalancer 接口
  • 仪表盘改进dashboard/ 目录包含 React 19 前端
  • 文档wiki/ 目录包含 VitePress 文档站点
  • 测试覆盖率 — 在 Jacoco 报告中查找未覆盖的代码路径

需要谨慎审查的领域(修改前请先讨论):

  • Lua 脚本 — 它们保证关键的一致性不变量
  • Redis 键模式 — 变更会破坏向后兼容性
  • 一致性层包装器 — 它们是性能基础

关键文件参考

下表列出了最重要的源文件。请收藏这些文件 — 你会频繁参考它们。

文件用途
cosky-core/.../CoSky.kt品牌常量和键分隔符
cosky-core/.../Namespaced.kt命名空间默认值和系统命名空间
cosky-core/.../RedisKeys.ktRedis Cluster 的哈希标签包裹
cosky-config/.../ConfigKeyGenerator.kt配置 Redis 键模式
cosky-config/.../ConfigService.kt配置服务接口
cosky-config/.../RedisConfigService.kt配置 Redis 实现
cosky-config/.../RedisConsistencyConfigService.kt配置一致性包装器
cosky-config/src/main/resources/config_set.lua原子配置写入 + 版本管理 + 历史
cosky-discovery/.../DiscoveryKeyGenerator.kt服务发现 Redis 键模式
cosky-discovery/.../ServiceRegistry.kt注册接口
cosky-discovery/.../ServiceDiscovery.kt发现接口
cosky-discovery/.../ConsistencyRedisServiceDiscovery.kt发现一致性包装器
cosky-discovery/.../RedisServiceRegistry.kt注册 Redis 实现
cosky-discovery/src/main/resources/registry_register.lua原子实例注册
cosky-discovery/src/main/resources/registry_renew.lua带节流 PubSub 的心跳续约
cosky-rest-api/.../ServiceController.kt服务 REST 端点
build.gradle.kts根构建配置
settings.gradle.kts模块声明

贡献者工作流程图

mermaid
flowchart LR
    subgraph "本地开发"
        BRANCH["创建分支"] --> CODE["编写代码和测试"]
        CODE --> DETEKT_LOCAL["运行 detekt"]
        DETEKT_local --> BUILD_LOCAL["运行构建"]
        BUILD_LOCAL --> COMMIT["提交"]
    end

    subgraph "Pull Request"
        COMMIT --> PUSH["推送到 GitHub"]
        PUSH --> PR["发起 PR"]
    end

    subgraph "CI 流水线"
        PR --> CI_TEST["集成测试"]
        CI_TEST --> CI_DETEKT["Detekt 检查"]
        CI_DETEKT --> CI_COVERAGE["代码覆盖率"]
        CI_COVERAGE --> CI_BENCH["JMH 基准测试"]
    end

    CI_BENCH --> REVIEW["代码审查"]
    REVIEW --> MERGE["合并到 main"]

    style BRANCH fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style CODE fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style DETEKT_local fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style BUILD_LOCAL fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style COMMIT fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style PUSH fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style PR fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style CI_TEST fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style CI_DETEKT fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style CI_COVERAGE fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style CI_BENCH fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style REVIEW fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style MERGE fill:#2d333b,stroke:#6d5dfc,color:#e6edf3

基于 Apache License 2.0 许可发布。