Skip to content

服务发现

服务发现是 sora 框架中服务自动注册和发现的核心机制。它允许服务实例在启动时注册自己的信息,在关闭时自动注销,其他服务可以动态感知这些变化。

工作原理

┌──────────┐  注册   ┌──────────┐  订阅   ┌──────────┐
│ Service A │ ──────▶ │          │ ◀────── │ Provider  │
│ (端口4000) │        │Discovery │         │ (调用方)  │
└──────────┘         │          │         └──────────┘
                     │          │
┌──────────┐  注册   │          │  订阅   ┌──────────┐
│ Service B │ ──────▶ │          │ ◀────── │ Service C │
│ (端口5000) │        │          │         │ (同进程)  │
└──────────┘         └──────────┘         └──────────┘

Discovery 维护四类元数据:

类型说明RxJS Subject
Service服务实例信息(名称、ID、标签)serviceSubject
Endpoint端点信息(协议、地址、权重、标签)listenerSubject
Worker工作单元信息workerSubject
Node节点信息(主机、PID、版本)nodeSubject

其他组件通过订阅这些 Subject 实时感知变化。例如 Provider 订阅 listenerSubject,当目标服务的新端点注册时自动创建连接,端点注销时自动断开。

可替换的底层实现

sora 框架只定义了服务发现的抽象接口,不负责具体实现。核心理念是通过规范化的接口,让开发者能够根据需求自由选择底层方案——etcd、Redis、Zookeeper 等都可以作为实现。切换时只需替换 Discovery 实例,业务代码无需任何改动。

典型的演进路径:

项目初期          单进程部署           RamDiscovery      零依赖,开箱即用


项目成长          多进程集群           ETCDDiscovery     引入 etcd,跨进程发现


按需扩展          自定义实现           YourDiscovery     实现 Discovery 抽象类

切换过程几乎无缝——只需修改创建 Discovery 的那一行代码。

官方实现

RamDiscovery

纯内存实现,所有元数据存储在进程内,仅限同进程内的服务互相发现。

typescript
import { Runtime } from '@sora-soft/framework';
import { RamDiscovery } from '@sora-soft/ram-discovery';

const discovery = new RamDiscovery();
await Runtime.startup(node, discovery);
特性说明
依赖
跨进程不支持
持久化无(进程退出即丢失)
选举简单先到先得
适用场景单元测试、简单项目、本地开发

ETCDDiscovery

基于 etcd 的分布式实现,支持跨进程服务发现和选举。

typescript
import { Runtime } from '@sora-soft/framework';
import { ETCDDiscovery } from '@sora-soft/etcd-discovery';

// etcd 组件需要提前注册
Runtime.registerComponent('etcd', etcdComponent);

const discovery = new ETCDDiscovery({
  etcdComponentName: 'etcd',
  prefix: '/my-app/discovery',
});
await Runtime.startup(node, discovery);
特性说明
依赖etcd 服务
跨进程支持
持久化基于 etcd Lease(进程退出后自动清理)
实时感知基于 etcd Watch
选举分布式选举
适用场景生产集群、多进程部署

配置项(IETCDDiscoveryOptions):

字段类型说明
etcdComponentNamestringEtcdComponent 在 Runtime 中的注册名称
prefixstringetcd 中的键前缀

etcd 键结构:

{prefix}/service/{serviceId}     → IServiceMetaData (JSON)
{prefix}/worker/{workerId}       → IWorkerMetaData (JSON)
{prefix}/endpoint/{endpointId}   → IListenerMetaData (JSON)
{prefix}/node/{nodeId}           → INodeMetaData (JSON)

基于 WTFPL 许可发布