Skip to content

架构概览

CoSky 是一个高性能的微服务治理平台,提供基于 Redis 的服务发现和配置管理。CoSky 不需要像 ZooKeeper 或 Consul 这样的独立基础设施组件,而是利用 Redis——大多数微服务部署已经依赖的技术——作为唯一的数据源。这种设计消除了运维复杂性,同时通过 Redis 的内存数据结构提供亚毫秒级的读取性能,通过 Lua 脚本原子性保证写入操作,以及通过 Pub/Sub 实现实时变更传播。

架构遵循受整洁架构原则启发的分层、模块驱动设计。核心领域抽象位于 cosky-core 中,领域特定逻辑分为 cosky-configcosky-discovery,Spring Cloud 集成由专用 Starter 模块提供。这种分离确保团队可以仅采用所需的能力(仅配置、仅发现或两者兼用),而不会引入不必要的传递依赖。

概览

模块职责关键文件源码
cosky-core命名空间模型、Redis 键工具、事件监听器抽象、品牌常量CoSky.kt, Namespaced.kt, NamespacedContext.kt, RedisKeys.kt, EventListenerContainer.ktcosky-core/src/main/kotlin/me/ahoo/cosky/core
cosky-config配置 CRUD、版本管理、回滚、变更事件ConfigService.kt, ConfigKeyGenerator.kt, ConfigChangedEvent.ktcosky-config/src/main/kotlin/me/ahoo/cosky/config
cosky-discovery服务注册、发现、实例生命周期、拓扑ServiceRegistry.kt, ServiceDiscovery.kt, DiscoveryKeyGenerator.ktcosky-discovery/src/main/kotlin/me/ahoo/cosky/discovery
cosky-spring-cloud-coreSpring Boot 自动配置、命名空间属性、Redis 模板装配CoSkyAutoConfiguration.kt, CoSkyProperties.ktcosky-spring-cloud-core/src/main/kotlin/me/ahoo/cosky/spring/cloud
cosky-spring-cloud-starter-configSpring Cloud Config PropertySourceLocator 集成、配置刷新CoSkyPropertySourceLocator.kt, CoSkyConfigRefresher.ktcosky-spring-cloud-starter-config
cosky-spring-cloud-starter-discoverySpring Cloud DiscoveryClientServiceRegistry 集成CoSkyDiscoveryClient.kt, CoSkyServiceRegistry.ktcosky-spring-cloud-starter-discovery
cosky-rest-apiRESTful 管理 API 和控制台服务器RestApiServer.kt, ConfigController.kt, ServiceController.ktcosky-rest-api

高层 C4 容器图

下图展示了 Spring Boot 应用如何通过 Starter 模块与 CoSky 集成,而这些 Starter 模块依赖于以 Redis 为后端的核心库。

mermaid
C4Container
    title CoSky - 高层容器图

    Person(app, "微服务应用", "使用 CoSky 的 Spring Boot 应用")

    System_Boundary(cosky, "CoSky 平台") {
        Container(starter_config, "cosky-spring-cloud-starter-config", "Spring Cloud 配置 Starter", "PropertySource 定位器, 配置刷新")
        Container(starter_discovery, "cosky-spring-cloud-starter-discovery", "Spring Cloud 发现 Starter", "DiscoveryClient, ServiceRegistry")
        Container(spring_cloud_core, "cosky-spring-cloud-core", "Spring Cloud 核心", "自动配置, 命名空间属性")
        Container(core, "cosky-core", "核心库", "命名空间模型, Redis 键, 事件")
        Container(config, "cosky-config", "配置模块", "配置 CRUD, 版本管理, 回滚")
        Container(discovery, "cosky-discovery", "发现模块", "服务注册, 实例生命周期")
        Container(rest_api, "cosky-rest-api", "REST API 服务器", "管理 API + 控制台")
    }

    SystemDb(redis, "Redis", "内存数据存储, Pub/Sub")

    Rel(app, starter_config, "使用配置")
    Rel(app, starter_discovery, "使用发现")
    Rel(starter_config, spring_cloud_core, "依赖")
    Rel(starter_discovery, spring_cloud_core, "依赖")
    Rel(starter_config, config, "依赖")
    Rel(starter_discovery, discovery, "依赖")
    Rel(spring_cloud_core, core, "依赖")
    Rel(config, core, "依赖")
    Rel(discovery, core, "依赖")
    Rel(rest_api, starter_config, "依赖")
    Rel(rest_api, starter_discovery, "依赖")
    Rel(core, redis, "读写")
    Rel(config, redis, "读写 Lua 脚本")
    Rel(discovery, redis, "读写 Lua 脚本")

模块依赖图

Gradle 模块结构形成一个有向无环图。cosky-core 是基础;所有其他模块都直接或传递地依赖它。

mermaid
flowchart TD
    subgraph Foundation["基础层"]
        cosky-core["cosky-core<br><i>命名空间, 键, 事件</i>"]
    end

    subgraph Domain["领域层"]
        cosky-config["cosky-config<br><i>配置管理</i>"]
        cosky-discovery["cosky-discovery<br><i>服务发现</i>"]
    end

    subgraph Spring Cloud["Spring Cloud 层"]
        cosky-spring-cloud-core["cosky-spring-cloud-core<br><i>自动配置</i>"]
        cosky-spring-cloud-starter-config["cosky-spring-cloud-starter-config<br><i>配置 Starter</i>"]
        cosky-spring-cloud-starter-discovery["cosky-spring-cloud-starter-discovery<br><i>发现 Starter</i>"]
    end

    subgraph Application["应用层"]
        cosky-rest-api["cosky-rest-api<br><i>REST API + 控制台</i>"]
    end

    cosky-config --> cosky-core
    cosky-discovery --> cosky-core
    cosky-spring-cloud-core --> cosky-core
    cosky-spring-cloud-starter-config --> cosky-spring-cloud-core
    cosky-spring-cloud-starter-config --> cosky-config
    cosky-spring-cloud-starter-discovery --> cosky-spring-cloud-core
    cosky-spring-cloud-starter-discovery --> cosky-discovery
    cosky-rest-api --> cosky-spring-cloud-starter-config
    cosky-rest-api --> cosky-spring-cloud-starter-discovery

    style cosky-core fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style cosky-config fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style cosky-discovery fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style cosky-spring-cloud-core fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style cosky-spring-cloud-starter-config fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style cosky-spring-cloud-starter-discovery fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style cosky-rest-api fill:#2d333b,stroke:#6d5dfc,color:#e6edf3

Redis 键设计

CoSky 中的所有数据都使用一致的键命名策略存储在 Redis 中。键由命名空间前缀、领域特定段和资源标识符组成,以 : 字符分隔(定义为 CoSky.KEY_SEPARATOR)。RedisKeys 工具处理 Redis 集群 hashtag 包装,确保同一命名空间内的键路由到同一槽位。

领域键模式Redis 类型用途源码
命名空间索引{cosky-system}:ns_idxSET存储所有已注册的命名空间RedisNamespaceService.kt:28
服务索引{namespace}:svc_idxSET存储命名空间中的所有服务 IDDiscoveryKeyGenerator.kt:32
实例索引{namespace}:svc_itc_idx:{serviceId}SET存储服务的实例 IDDiscoveryKeyGenerator.kt:55
实例数据{namespace}:svc_itc:{instanceId}STRING(带 TTL)编码的实例详情,临时实例会过期DiscoveryKeyGenerator.kt:63
服务统计{namespace}:svc_statHASH每个服务的实例计数统计DiscoveryKeyGenerator.kt:41
配置索引{namespace}:cfg_idxSET存储命名空间中的所有配置 IDConfigKeyGenerator.kt:36
配置数据{namespace}:cfg:{configId}HASH当前配置数据(数据、哈希、版本、更新时间)ConfigKeyGenerator.kt:60
配置历史索引{namespace}:cfg_htr_idx:{configId}ZSET版本历史键的有序列表ConfigKeyGenerator.kt:44
配置历史{namespace}:cfg_htr:{configId}:{version}HASH每个版本的历史配置快照ConfigKeyGenerator.kt:52

事件驱动架构

CoSky 使用 Redis Pub/Sub 进行实时变更通知。当变更操作发生(服务注册/注销、配置设置/删除)时,执行原子操作的 Lua 脚本也会向相应通道 PUBLISH 消息。消费者通过 EventListenerContainer 实现订阅,这些实现封装了 Spring 的 ReactiveRedisMessageListenerContainer

mermaid
sequenceDiagram
    autonumber
    participant P as 生产者<br>(Lua 脚本)
    participant R as Redis
    participant L as RedisEventListenerContainer
    participant C as 消费者<br>(应用)

    P->>R: EVALSHA 脚本(原子变更 + PUBLISH)
    R-->>R: 原子执行 Lua 脚本
    R->>R: PUBLISH 到通道<br>(例如 {ns}:svc_itc:{instanceId})
    R->>L: 投递 Pub/Sub 消息
    L->>L: 解码消息为<br>InstanceChangedEvent / ConfigChangedEvent
    L->>C: Flux<InstanceChangedEvent>.next()
    C->>C: 更新本地缓存 /<br>刷新 Spring Environment

技术栈

技术版本用途源码
Kotlin1.9+ (JVM 17 工具链)主要编程语言build.gradle.kts:92-93
Spring Boot3.x应用框架cosky-spring-cloud-core/build.gradle.kts
Spring Cloud2024.x云原生抽象(DiscoveryClient、PropertySourceLocator)cosky-spring-cloud-core/build.gradle.kts
Spring Data Redis最新响应式 Redis 操作(ReactiveStringRedisTemplate)cosky-core/build.gradle.kts
Lettuce最新Redis 客户端驱动(异步、响应式)cosky-core/build.gradle.kts
Project Reactor最新响应式编程模型(Flux、Mono)cosky-core/build.gradle.kts
Lua (Redis 脚本)N/A原子多步操作DiscoveryRedisScripts.kt
Gradle (Kotlin DSL)8.x构建系统settings.gradle.kts

设计原则

CP + AP 灵活性

CoSky 同时支持临时(AP,最终一致性)和持久(CP,强一致性)服务实例。临时实例使用基于 Redis TTL 的过期机制——如果注册客户端未能续约,实例会自动过期。持久实例没有 TTL(TTL_AT_FOREVER = -1),必须显式注销。这体现了 Nacos 的设计理念,同时使用 Redis 作为后端存储。

mermaid
stateDiagram-v2
    direction LR
    [*] --> Registered : register()
    Registered --> Renewed : renew() / 重置 TTL
    Renewed --> Renewed : renew() / 重置 TTL
    Registered --> Deregistered : deregister()
    Renewed --> Deregistered : deregister()
    Registered --> Expired : TTL 超时(临时实例)
    Renewed --> Expired : TTL 超时(临时实例)
    Deregistered --> [*]
    Expired --> [*]

Lua 脚本保证原子性

关键写入操作(注册、注销、配置设置、配置回滚)作为 Lua 脚本在 Redis 服务器上执行。这保证了原子性——例如,注册脚本原子地将实例添加到服务索引、存储实例数据、发布变更事件并更新统计信息,全部在单个 Redis 命令中完成。在发现和配置模块中共有 13 个 Lua 脚本

mermaid
flowchart LR
    subgraph Discovery Lua Scripts["发现 Lua 脚本"]
        A[registry_register.lua]
        B[registry_deregister.lua]
        C[registry_renew.lua]
        D[registry_set_service.lua]
        E[registry_remove_service.lua]
        F[registry_set_metadata.lua]
        G[discovery_get_instances.lua]
        H[discovery_get_instance.lua]
        I[discovery_get_instance_ttl.lua]
        J[instance_count_stat.lua]
        K[service_stat.lua]
        L[service_topology_add.lua]
        M[service_topology_get.lua]
    end

    subgraph Config Lua Scripts["配置 Lua 脚本"]
        N[config_set.lua]
        O[config_remove.lua]
        P[config_rollback.lua]
    end

    style A fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style B fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style C fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style D fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style E fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style F fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style G fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style H fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style I fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style J fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style K fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style L fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style M fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style N fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style O fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style P fill:#2d333b,stroke:#6d5dfc,color:#e6edf3

本地缓存提升性能

ConsistencyRedisServiceDiscovery 装饰器维护一个内存中的 ConcurrentHashMap 来存储服务实例,通过 Pub/Sub 事件订阅保持最新状态。这意味着一旦首次 getInstances() 调用从 Redis 获取数据,后续调用将从本地缓存提供服务,直到变更事件到达。这种模式为负载均衡器等读取密集型工作负载提供亚微秒级延迟,同时通过事件驱动失效保持一致性。

交叉引用

参考

基于 Apache License 2.0 许可发布。