0.3.30 • Published 5 months ago

@dvsantd/polaris v0.3.30

Weekly downloads
-
License
MIT
Repository
-
Last release
5 months ago

Polaris SDK

build status standard-readme compliant

Polaris(北极星)是名字服务协同团队合力研发的服务治理组件,具备服务注册、健康检查、服务发现、服务路由、负载均衡、故障节点熔断、动态权重调整与服务限流等功能。

Polaris Node.js SDK 采用微内核(Microkernel) + 插件(Plugins) 设计,可支持替换 11 种不同类型插件。

项目(包括依赖部分) 100% 由 TypeScript(JavaScript) 编写,不含任何 C/C++ 代码。

目录

安装

tnpm

$ tnpm install @dvsantd/polaris

yarn

$ yarn add @dvsantd/polaris --registry=https://mirrors.dvsantd.com/npm/

例子

查询被调服务特定地址:

import { Consumer } from "@dvsantd/polaris";
const consumer = new Consumer();
const response = await (consumer.select("namespace", "service"));
if (response) {
  const { instance: { host, port }} = response;
  /**
   * 通过 host, port 进行调用,
   * 并调用 `update` 上报调用结果
   */
  response.update(success, cost, code);
}

使用

运行模式

模块支持 ServerAgent(暂未上线) 两种运行模式。

模式优势权衡
Server(default)不需要部署 Agent可替换不同类型插件拥有更多配置更好的性能不能跨进程统计调用状态
Agent逻辑简单支持跨进程统计调用状态需要部署 Agent可配置项较少

我应该如何选择运行模式?

在绝大多数情况下,直接使用默认的 Server 模式即可。

由于模块只统计当前进程内的调用状态,如果服务调用量极少而又启动了 多进程 负载均衡时, 存在无法及时对实例进行调整(如屏蔽熔断)的可能,此时如果服务关心调用成功率,可以使用 Agent 模式。

具体切换方式可详见 名字服务插件

公共类型

为了便于描述(表达)北极星组件的调用与返回结构,模块定义了如下几种数据类型:

实例

实例 Instance 类型描述了服务节点详细信息,信息由静态与动态成员组成:

  • 静态部分:
    • id: 唯一 ID
    • vpc_id: 腾讯云 VPC Id
    • host: IP 或域名
    • port: 端口号
    • protocol: 协议信息
    • staticWeight: 静态权重值, 取值 ∈ 0-1000
    • metadata: 元数据信息
    • priority: 优先级
    • version: 版本号
    • logicSet: 逻辑区域
    • location: 地理位置
  • 动态部分:
    • dynamicWeight: 动态权重值
    • status: 当前状态

两个实例对象在进行比较时,直接比较 Instance.id 是否相同。也就是说,相同实例的 id 必须相同

实例状态

实例状态 InstanceStatus 描述了实例当前所处的状态:

  • Normal: 正常
  • HalfOpen: 半打开,各周期只选出极少次,负责探活
  • HalfClose: 半关闭,不在任何模块中被选出,但计算调用结果
  • Fused: 熔断,不在任何模块中被选出

各状态间迁移关系,可查看插件节 - 实例状态迁移

位置

位置 Location 描述了一个特定的(地理)位置信息,其中包含:

  • region
  • zone
  • campus(idc)

两个位置在比较时,按照范围由小至大进行匹配,campus --> zone --> region

一般用于就近调用等要求匹配位置的场景。

元数据

元数据 Metadata 以 {key: string: string} 形式存储特定对象(如实例对象)的描述信息。

一般用于规则路由等需进行对象筛选的场景。

Consumer API

import { Consumer } from "@dvsantd/polaris"

供服务调用方使用。

通过提供的主调(可选)与被调服务信息,按规则选出被调服务的一个特定实例或返回所有被调服务实例。

new Consumer(service, plugins, options)

构造 Consumer 对象,用于获取被调方特定实例。

  • service: 主调方服务信息或服务名(可选)
  • plugins: 插件列表(可选)
  • options: 配置参数(可选)

请注意:不要每次调用都构造一个 Consumer 对象,这样不仅会造成性能损耗,同时会导致无法按预期逻辑处理。如果你不得不这样做,请在使用完 Consumer 对象后调用 dispose() 接口将其释放。

select(...)

获取被调服务中的一个特定实例,并返回上报对象。

const response = await consumer.select(namespace, service, metadata, args);

const response = await consumer.select(service, metadata, args);
  • namespace: 被调服务名字空间(可选)
  • service: 被调服务名
  • metadata: 主调方服务元数据(可选)
  • args: 本次调用附加参数(可选),在特定负载均衡(如一致性哈希)插件中使用。

response.instance 即为选出的被调服务的特定实例。

请保留 select(...) 接口返回的 response 对象,以便在调用完成后进行结果上报:

response.update(success, cost, code);
  • success: 是否调用成功
  • cost: 调用耗时(可选,默认为 0,单位为毫秒)
  • code: 返回码(可选)

请注意:无论是否调用成功,在调用完成后一定要上报调用结果,否则模块将无法对实例进行动态调整。

list(...)

获取被调方全部服务实例。

const instances = await consumer.list(namespace, service);

const instances = await consumer.list(service);
  • namespace: 被调服务名字空间(可选)
  • service: 被调服务名

instances 即为被调方全部服务实例。

update(...)

强制刷新缓存。

const hasUpdated = await consumer.update(namespace, service, type);
  • namespace: 被调服务名字空间
  • service: 被调服务名
  • type: 数据存储类别,为 RegistryCategory 枚举

如存在更新,则调用结果为 true

请留意:一般情况下,请同时更新 RegistryCategory.InstanceRegistryCategory.Rule

dispose()

释放掉 Consumer 对象,在内部会释放掉相关的缓存和 socket 连接。

请注意:调用 dispose() 后,再调用 Consumer 对象的其他方法会抛出异常。

Provider API

供服务提供方使用。

提供服务注册(注销)、心跳上报等能力。

new Provider(plugins)

构造 Provider 对象,用于获取被调方特定实例。

  • plugins: 插件列表(可选)

请注意:不要每次调用都构造一个 Provider 对象,这样会造成性能损耗。

服务注册

const response = await provider.register(namespace, service, token, instance, options);
  • namespace: 命名空间
  • service: 服务名
  • token: 服务 Token 用来鉴权
  • instance: 待注册的实例
  • options: 注册选项(可选)

可通过 response 对注册的服务进行操作:

  • id: 获取注册的实例 id
  • unregister(): 服务注销
  • heartbeat(): 心跳上报

服务注销

const success = await provider.unregister(id, token);
  • id: 实例 ID
  • token: 服务 Token 用来鉴权

const success = await provider.unregister(namespace, service, host, port, token);
  • namespace: 命名空间
  • service: 服务名
  • host: 节点 IP 或者域名
  • port: 节点端口号
  • token: 服务 Token 用来鉴权

可通过 success 判断注册是否成功。

心跳上报

const success = await provider.heartbeat(id, token);
  • id: 实例唯一 ID
  • token: 服务 Token 用来鉴权

const success = await provider.heartbeat(namespace, service, host, port, token)
  • namespace: 命名空间
  • service: 服务名
  • host: 节点 IP 或者域名
  • port: 节点端口号
  • token: 服务 Token 用来鉴权

可通过 success 判断注册是否成功。

Limiter API

提供流量控制(整形)能力。

new Limiter(plugins, options)

构造 Limiter 对象。

  • plugins: 插件列表(可选)
  • options: 配置参数(可选)

请注意:不要每次调用都构造一个 Limiter 对象,这样不仅会造成性能损耗,同时会导致无法按预期逻辑处理。

配额申请

const response = await limiter.acquire(namespace, service, amount, cluster, labels, id)
  • namespace: 命名空间
  • service: 服务名
  • amount: 申请配额的数量
  • cluster: 集群名(可选)
  • labels: 标签集合(可选)
  • id: 上次调用返回 ID(对应 response.id),用于二次获取配额时提升性能(可选)

response.quotas 即为申请的配额列表,而配额是否申请成功需等待其状态变更:

response.quotas[i].then((release) => { /** 配额申请成功 */ }, (err) => { /** 配额申请失败 */ });

当针对并发数进行限流时,在配额使用完成后,需调用 release() 方法释放对于配额的占用。

插件

列表

使用插件

所有的内置插件被定义为实现了特定插件接口的 class缺省情况下,new Consumer() 时构造函数内部会使用默认参数实例化一系列插件实例。如果需要替换掉默认的插件或其实例化的参数,可以在 new Consumer() 时直接传入特定插件的实例。部分插件支持调用时参数,可以在调用 select() 方法时传入。

以负载均衡(一致性哈希)为例,其初始化和传参方法如下:

import { Consumer, HashRingLoadBalancer, plugins } from  "@dvsantd/polaris";

const consumer = new Consumer({
  [plugins.PluginType.LoadBalancer]: new HashRingLoadBalancer({
    algorithm: 'md5'
  })
});

(async () => {
  await consumer.select("namespace", "service", metadata, {
    [plugins.PluginType.LoadBalancer]: 'group/project'
  })
})();

详情请看 插件

贡献

我们十分期待您的贡献,更多详情请参考 CONTRIBUTING.md