Skip to content

Spring Cloud Discovery Starter ​

CoSky Spring Cloud Discovery Starter 提供了 CoSky 基于 Redis 的服务注册中心与 Spring Cloud Discovery 模型之间的无缝集成。它同时提供阻塞式(DiscoveryClient)和响应式(ReactiveDiscoveryClient)实现、带心跳续期的自动服务注册以及加权负载均衡器 -- 所有这些都不需要运行独立的发现服务器。服务在启动时将自己注册到 Redis,通过 Redis 查询互相发现,并在关闭时自动注销。

一览 ​

组件职责关键文件源码
CoSkyDiscoveryAutoConfiguration装配核心发现 Bean(服务发现、拓扑、事件监听器、负载均衡器)CoSkyDiscoveryAutoConfiguration.ktcosky-spring-cloud-starter-discovery/.../CoSkyDiscoveryAutoConfiguration.kt:47
CoSkyDiscoveryClient阻塞式 DiscoveryClient 适配器CoSkyDiscoveryClient.ktcosky-spring-cloud-starter-discovery/.../CoSkyDiscoveryClient.kt:24
CoSkyReactiveDiscoveryClient响应式 ReactiveDiscoveryClient 适配器CoSkyReactiveDiscoveryClient.ktcosky-spring-cloud-starter-discovery/.../CoSkyReactiveDiscoveryClient.kt:26
CoSkyServiceRegistry通过 Redis 注册和注销实例CoSkyServiceRegistry.ktcosky-spring-cloud-starter-discovery/.../CoSkyServiceRegistry.kt:25
CoSkyRegistration持有用于注册的服务实例元数据CoSkyRegistration.ktcosky-spring-cloud-starter-discovery/.../CoSkyRegistration.kt:26
CoSkyAutoServiceRegistrationWeb 应用自动注册(从内嵌服务器获取端口)CoSkyAutoServiceRegistration.ktcosky-spring-cloud-starter-discovery/.../CoSkyAutoServiceRegistration.kt:25
CoSkyAutoServiceRegistrationOfNoneWeb非 Web 应用自动注册(使用 PID 作为端口)CoSkyAutoServiceRegistrationOfNoneWeb.ktcosky-spring-cloud-starter-discovery/.../CoSkyAutoServiceRegistrationOfNoneWeb.kt:31

配置属性 ​

发现属性 ​

由 CoSkyDiscoveryProperties.kt:24 绑定,前缀为 spring.cloud.cosky.discovery。

属性默认值描述
spring.cloud.cosky.discovery.enabledtrue启用或禁用 CoSky 发现启动器。
spring.cloud.cosky.discovery.order0组合链中发现客户端的顺序。
spring.cloud.cosky.discovery.timeout2s阻塞式发现操作的超时时间。

注册属性 ​

由 CoSkyRegistryProperties.kt:27 绑定,前缀为 spring.cloud.cosky.discovery.registry。

属性默认值描述
spring.cloud.cosky.discovery.registry.service-id${spring.application.name}用于注册的服务名称。回退为应用程序名称。
spring.cloud.cosky.discovery.registry.schemahttp协议方案(http 或 https)。
spring.cloud.cosky.discovery.registry.host自动检测主机 IP 地址。如果为空,通过 InetUtils 自动检测。
spring.cloud.cosky.discovery.registry.port0服务端口。对于 Web 应用,从内嵌服务器自动检测。
spring.cloud.cosky.discovery.registry.weight1用于加权负载均衡的实例权重。
spring.cloud.cosky.discovery.registry.is-ephemeraltrue实例是否为临时实例(需要心跳续期)。
spring.cloud.cosky.discovery.registry.ttl60s实例的存活时间;必须在过期前续期。
spring.cloud.cosky.discovery.registry.timeout2s阻塞式注册操作的超时时间。
spring.cloud.cosky.discovery.registry.initial-statusUP初始实例状态(UP 或 OUT_OF_SERVICE)。
spring.cloud.service-registry.auto-registration.enabledtrue启用或禁用自动注册。

自动配置链 ​

发现启动器使用分层自动配置方法。CoSkyDiscoveryAutoConfiguration 装配核心基础设施 Bean(基于 Redis 的服务发现、事件监听器、负载均衡器)。然后,根据应用程序类型,由 CoSkyDiscoveryClientConfiguration 或 CoSkyReactiveDiscoveryClientConfiguration 提供相应的 Spring Cloud 发现客户端。最后,CoSkyAutoServiceRegistrationAutoConfiguration 处理服务注册。

mermaid
flowchart TB
    subgraph Core["核心发现基础设施"]
        direction TB
        A["CoSkyAutoConfiguration<br>(Redis 连接)"]
        A --> B["CoSkyDiscoveryAutoConfiguration"]
    end

    subgraph Clients["发现客户端"]
        direction TB
        B --> C["CoSkyDiscoveryClientConfiguration<br>(阻塞式)"]
        B --> D["CoSkyReactiveDiscoveryClientConfiguration<br>(响应式)"]
    end

    subgraph Registration["服务注册"]
        direction TB
        B --> E["CoSkyAutoServiceRegistrationAutoConfiguration"]
        E --> F["CoSkyAutoServiceRegistration<br>(Web 应用)"]
        E --> G["CoSkyAutoServiceRegistrationOfNoneWeb<br>(非 Web 应用)"]
    end

    B --> H["BinaryWeightRandomLoadBalancer"]

    style Core fill:#161b22,stroke:#30363d,color:#e6edf3
    style Clients fill:#161b22,stroke:#30363d,color:#e6edf3
    style Registration fill:#161b22,stroke:#30363d,color:#e6edf3
    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

发现客户端 ​

CoSky 提供两种发现客户端实现,将 CoSky ServiceDiscovery API 适配为 Spring Cloud 接口。

阻塞式:CoSkyDiscoveryClient ​

CoSkyDiscoveryClient 实现了 Spring 的 DiscoveryClient。它委托给响应式 ServiceDiscovery 并使用配置的超时时间进行阻塞(CoSkyDiscoveryClient.kt:36)。来自 CoSky 的每个 ServiceInstance 都被包装在 CoSkyServiceInstance 适配器中。

响应式:CoSkyReactiveDiscoveryClient ​

CoSkyReactiveDiscoveryClient 实现了 ReactiveDiscoveryClient,直接返回 Flux<ServiceInstance> 而不阻塞(CoSkyReactiveDiscoveryClient.kt:32)。

CoSkyServiceInstance 适配器 ​

CoSkyServiceInstance 是一个简单的数据类,包装 CoSky 的 ServiceInstance 并将其适配为 Spring Cloud 的 ServiceInstance 接口,映射 instanceId、serviceId、host、port、isSecure、uri、metadata 和 scheme(CoSkyServiceInstance.kt:23)。

服务发现查询流程 ​

mermaid
sequenceDiagram
    autonumber
    participant App as 应用代码
    participant DC as DiscoveryClient
    participant SD as ServiceDiscovery
    participant Redis as Redis

    App->>DC: getInstances("order-service")
    DC->>SD: getInstances(serviceId)
    SD->>Redis: SMEMBERS + HMGET
    Redis-->>SD: 实例数据
    SD-->>DC: List~ServiceInstance~
    DC->>DC: 包装为 CoSkyServiceInstance
    DC-->>App: List~ServiceInstance~

    App->>DC: getServices()
    DC->>SD: getServices()
    SD->>Redis: SMEMBERS 服务键
    Redis-->>SD: 服务名称
    SD-->>DC: List~String~
    DC-->>App: List~String~

服务注册 ​

CoSkyServiceRegistry ​

CoSkyServiceRegistry 实现了 Spring 的 ServiceRegistry<CoSkyRegistration> 接口。在 register() 时,它委托给 CoSky ServiceRegistry 将实例持久化到 Redis,然后启动心跳 RenewInstanceService(CoSkyServiceRegistry.kt:30)。在 deregister() 时,它仅通过 CoSky ServiceRegistry 移除实例——心跳会继续运行(CoSkyServiceRegistry.kt:38)。续期服务由 close() 方法停止(CoSkyServiceRegistry.kt:45)。

CoSkyRegistration ​

CoSkyRegistration 实现了 Spring 的 Registration 接口。它携带注册所需的 serviceId、scheme、host、port、weight、isEphemeral 和 metadata(CoSkyRegistration.kt:26)。asServiceInstance() 方法将其转换为适合注册调用的 CoSky ServiceInstance。

Web 应用自动注册 ​

CoSkyAutoServiceRegistration 继承 AbstractAutoServiceRegistration。当内嵌 Web 服务器启动时,它捕获动态分配的端口并调用 register()(CoSkyAutoServiceRegistration.kt:48)。它还将实例存储在 ServiceInstanceContext 中供下游使用。

非 Web 应用自动注册 ​

CoSkyAutoServiceRegistrationOfNoneWeb 处理非 Web 应用程序(如 gRPC 服务、CLI 工具)。它监听 ApplicationStartedEvent,如果应用程序上下文不是 WebServerApplicationContext,则使用进程 ID(PID)作为端口注册服务(CoSkyAutoServiceRegistrationOfNoneWeb.kt:52)。即使没有 HTTP 端口,这也提供了一个有意义的标识符。

服务注册流程 ​

mermaid
sequenceDiagram
    autonumber
    participant Boot as Spring Boot
    participant ASR as CoSkyAutoServiceRegistration
    participant Reg as CoSkyRegistration
    participant SR as CoSkyServiceRegistry
    participant Redis as Redis
    participant Renew as RenewInstanceService

    Boot->>ASR: WebServerInitializedEvent
    ASR->>Reg: getPort() - 捕获动态端口
    ASR->>ASR: ServiceInstanceContext.serviceInstance = ...
    ASR->>SR: register(registration)
    SR->>Reg: asServiceInstance()
    SR->>Redis: 注册实例 (HMSET)
    Redis-->>SR: true
    SR->>Renew: start() - 开始心跳
    Renew->>Redis: 定期续期 (TTL 刷新)

服务注销流程 ​

mermaid
sequenceDiagram
    autonumber
    participant Boot as Spring Boot
    participant SR as CoSkyServiceRegistry
    participant Reg as CoSkyRegistration
    participant Redis as Redis
    participant Renew as RenewInstanceService

    Boot->>SR: close() / @PreDestroy
    SR->>Renew: stop() - 结束心跳
    SR->>Reg: asServiceInstance()
    SR->>Redis: 注销实例
    Redis-->>SR: true

一致性层 ​

发现启动器使用 ConsistencyRedisServiceDiscovery 作为主要的 ServiceDiscovery Bean(CoSkyDiscoveryAutoConfiguration.kt:81)。此装饰器通过订阅以下事件,用本地一致性保证包装 RedisServiceDiscovery:

这种事件驱动的方式确保本地服务缓存被及时失效和刷新,而不依赖轮询。

负载均衡器集成 ​

CoSky 提供了自定义的 BinaryWeightRandomLoadBalancer(CoSkyDiscoveryAutoConfiguration.kt:109),支持每实例权重。它使用在累积权重数组上的二分搜索算法,实现 O(log n) 的实例选择。负载均衡器继承 AbstractLoadBalancer,并在 InstanceEventListenerContainer 报告实例列表变更时重建其选择器。

类图 ​

mermaid
classDiagram
    class CoSkyDiscoveryProperties {
        +enabled: Boolean = true
        +order: Int = 0
        +timeout: Duration = 2s
    }

    class CoSkyRegistryProperties {
        +serviceId: String
        +schema: String = "http"
        +host: String
        +port: Int = 0
        +weight: Int = 1
        +isEphemeral: Boolean = true
        +ttl: Duration = 60s
        +timeout: Duration = 2s
        +initialStatus: String = "UP"
    }

    class CoSkyDiscoveryAutoConfiguration {
        +redisServiceDiscovery() RedisServiceDiscovery
        +redisServiceTopology() RedisServiceTopology
        +consistencyRedisServiceDiscovery()
        +coSkyLoadBalancer()
    }

    class CoSkyDiscoveryClient {
        -serviceDiscovery: ServiceDiscovery
        -coSkyDiscoveryProperties: CoSkyDiscoveryProperties
        +getInstances(serviceId) List~ServiceInstance~
        +getServices() List~String~
    }

    class CoSkyReactiveDiscoveryClient {
        -serviceDiscovery: ServiceDiscovery
        +getInstances(serviceId) Flux~ServiceInstance~
        +getServices() Flux~String~
    }

    class CoSkyServiceInstance {
        +delegate: ServiceInstance
        +getInstanceId() String
        +getServiceId() String
        +getHost() String
        +getPort() Int
    }

    class CoSkyServiceRegistry {
        -serviceRegistry: ServiceRegistry
        -renewInstanceService: RenewInstanceService
        +register(registration)
        +deregister(registration)
        +setStatus(registration, status)
        +close()
    }

    class CoSkyRegistration {
        +serviceId: String
        +scheme: String
        +host: String
        +port: Int
        +weight: Int
        +isEphemeral: Boolean
        +asServiceInstance() ServiceInstance
    }

    class CoSkyAutoServiceRegistration {
        +register()
        +getRegistration() CoSkyRegistration
    }

    class CoSkyAutoServiceRegistrationOfNoneWeb {
        +onApplicationEvent(event)
        +destroy()
    }

    CoSkyDiscoveryAutoConfiguration --> CoSkyDiscoveryProperties : 配置
    CoSkyDiscoveryClient --> CoSkyDiscoveryProperties : 读取
    CoSkyDiscoveryClient --> CoSkyServiceInstance : 包装
    CoSkyReactiveDiscoveryClient --> CoSkyServiceInstance : 包装
    CoSkyServiceRegistry --> CoSkyRegistration : 接受
    CoSkyAutoServiceRegistration --> CoSkyServiceRegistry : 委托
    CoSkyAutoServiceRegistration --> CoSkyRegistration : 提供
    CoSkyAutoServiceRegistrationOfNoneWeb --> CoSkyServiceRegistry : 委托

    style CoSkyDiscoveryProperties fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style CoSkyRegistryProperties fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style CoSkyDiscoveryAutoConfiguration fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style CoSkyDiscoveryClient fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style CoSkyReactiveDiscoveryClient fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style CoSkyServiceInstance fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style CoSkyServiceRegistry fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style CoSkyRegistration fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style CoSkyAutoServiceRegistration fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style CoSkyAutoServiceRegistrationOfNoneWeb fill:#2d333b,stroke:#6d5dfc,color:#e6edf3

完整 YAML 配置示例 ​

yaml
spring:
  application:
    name: order-service
  cloud:
    cosky:
      namespace: production
      discovery:
        enabled: true
        order: 0
        timeout: 2s
        registry:
          service-id: order-service        # 默认为 spring.application.name
          schema: http
          host: ""                          # 通过 InetUtils 自动检测
          port: 0                           # Web 应用自动检测
          weight: 1
          is-ephemeral: true
          ttl: 60s
          timeout: 2s
          initial-status: UP
          metadata:
            version: "2.0.0"
            region: "us-east-1"
    service-registry:
      auto-registration:
        enabled: true

使用以上配置,order-service 将:

  1. 在启动时将自己注册到 production 命名空间下的 Redis 中。
  2. 启动心跳续期以保持实例存活(TTL = 60s)。
  3. 可被其他服务通过阻塞式或响应式发现客户端发现。
  4. 支持权重为 1 的加权负载均衡。

相关页面 ​

参考 ​

基于 Apache License 2.0 许可发布。