@iocore/micro-ws-agent
v1.1.9
Published
Default Monorepo template
Readme
@iocore/micro-ws-agent
IoCore 微服务 WebSocket Agent (客户端/服务端)。
该模块允许你构建一个 IoCore 微服务,该服务通过 WebSocket 与注册中心 (@iocore/micro-ws-registry) 通信以实现服务发现,并能处理来自其他 Agent 或客户端的请求。
支持两种主要的服务模式:
- WebSocket Service (
Service): 处理纯粹的 WebSocket 消息,适用于 RPC 风格的调用。 - HTTP-like Controller (
Controller): 模拟 HTTP 请求/响应模式,通过 WebSocket 传输,支持中间件和类似 Koa 的上下文 (Context)。
安装
npm install @iocore/micro-ws-agent @iocore/logger @iocore/micro-ws @iocore/component detect-port retry --save
# or
yarn add @iocore/micro-ws-agent @iocore/logger @iocore/micro-ws @iocore/component detect-port retry依赖
@iocore/logger@iocore/micro-ws@iocore/componentdetect-port(用于自动检测可用端口)retry(用于注册中心重连)
配置
通过环境变量 IOCORE_MICRO_WEBSOCKET_AGENT_CONFIGS 配置 Agent。该变量应包含一个 JSON 字符串。
export type IOCORE_MICRO_WEBSOCKET_AGENT_CONFIGS = {
registry: string; // 注册中心地址 (例如 'ws://localhost:8427')
namespace: string; // 当前服务的命名空间 (例如 'user-service')
port?: number; // 可选,指定服务监听的端口,若不指定或端口被占用则自动选择可用端口
};示例 .env 文件:
IOCORE_MICRO_WEBSOCKET_AGENT_CONFIGS='{"registry":"ws://localhost:8427","namespace":"user-service","port":9001}'使用
通常,Agent 会作为一个独立的 IoCore 应用启动,并定义自己的服务或控制器。
import { Application } from '@iocore/component';
import MicroWebSocketAgent, {
Service,
Controller,
ControllerRequest,
ControllerResponse,
Context,
Next,
Middleware,
} from '@iocore/micro-ws-agent';
// --- 定义 WebSocket Service ---
class MyWsService extends Service {
exec(name: string) {
// 可以通过 this.fetch 调用其他服务
// const result = await this.fetch('other-service/someApi', [arg1]);
return `Hello from WebSocket Service, ${name}!`;
}
}
// --- 定义 HTTP-like Controller 和 Middleware ---
class MyMiddleware extends Middleware {
@Middleware.InComing.Head('x-request-id') // 从 Header 注入
private requestId: string;
async use(ctx: Context, next: Next) {
console.log('Middleware Start, Request ID:', this.requestId);
ctx.state.middlewareData = 'Data from middleware'; // 向下传递数据
await next();
console.log('Middleware End');
}
}
@Controller.Middleware(MyMiddleware) // 应用中间件
class MyController extends Controller {
@Controller.InComing.Path('id') // 从 URL 路径参数注入
private userId: string;
@Controller.InComing.Query('sort') // 从 URL 查询参数注入
private sortBy: string;
@Controller.InComing.Body // 注入请求体
private requestBody: { message: string };
@Controller.InComing.State('middlewareData') // 从 ctx.state 注入 (由上游中间件设置)
private dataFromMiddleware: string;
async response(): Promise<ControllerResponse> {
console.log('Controller Executing...');
console.log('User ID:', this.userId);
console.log('Sort By:', this.sortBy);
console.log('Request Body:', this.requestBody);
console.log('Data from Middleware:', this.dataFromMiddleware);
// 可以调用其他服务
// const wsResult = await this.fetch('my-ws-service/exec', ['controller']);
return {
body: {
success: true,
data: `Processed user ${this.userId}, sorted by ${this.sortBy}. Message: ${this.requestBody?.message}`,
// wsResult,
},
headers: {
'X-Custom-Header': 'Value from controller',
},
// 设置 cookie 等...
};
}
}
// --- 启动 Agent 应用 ---
@Application.Inject(MicroWebSocketAgent)
class AgentApp extends Application {
@Application.Inject(MicroWebSocketAgent)
private agent: MicroWebSocketAgent;
public async main() {
// 绑定 WebSocket Service
this.agent.wsBinding('/my-ws-service', MyWsService);
// 绑定 HTTP-like Controller
this.agent.httpBinding('/users/:id', MyController);
console.log(`Agent '${this.agent.props.namespace}' ready.`);
// 示例:Agent 内部调用自己的服务
try {
const result = await this.agent.fetch('user-service/my-ws-service', ['internal call']);
console.log('Internal WS call result:', result);
const httpResult = await this.agent.fetch('user-service/users/123?sort=desc', [
{
headers: { 'x-request-id': 'abc-123' },
query: { sort: 'desc' }, // 查询参数也可以在这里提供
params: { id: '123' }, // 路径参数也可以在这里提供
cookie: { session: 'xyz' },
body: { message: 'Hello from internal fetch' },
} as ControllerRequest,
]);
console.log('Internal HTTP call result:', httpResult);
} catch (error) {
console.error('Internal fetch failed:', error);
}
}
}
Application.start(AgentApp);MicroWebSocketAgent 类
constructor(): 解析环境变量配置。initialize(): Promise<void>: 启动 WebSocket 服务器,自动选择端口,连接到注册中心并注册命名空间。IoCore 会自动调用。terminate(): void: 关闭服务器,断开与注册中心的连接。IoCore 会自动调用。props: IOCORE_MICRO_WEBSOCKET_AGENT_CONFIGS: 加载的配置。registry: Channel | undefined: 到注册中心的 WebSocket 通道。where(namespace: string): Promise<Channel>: 通过注册中心查找指定命名空间的服务地址,并建立连接(或返回已有的连接)。createFetcher<R = any>(protocol: string, url: string, props: any[] = [], timeout?: number): Promise<R>: 通过命名空间调用其他服务。protocol通常是 'ws' 或 'http'(模拟)。URL 格式如ws://other-service/api/path或http://other-service/users/:id。- 对于
ws协议,props是直接传递给目标Service的exec方法的参数列表。 - 对于
http协议,props应该是一个包含单个ControllerRequest对象的数组,模拟 HTTP 请求的各个部分。
- 对于
fetch<R = any>(url: string, props: any[] = [], timeout?: number): Promise<R>:createFetcher的快捷方式,默认协议为ws。wsBinding<T extends Service>(url: string, clazz: INewAble<T>): void: 将一个 URL 路径绑定到一个Service类。当收到该路径的ws协议请求时,会自动实例化该类并调用其exec方法。httpBinding<T extends Controller>(url: string, clazz: INewAble<T>): Promise<void>: 将一个 URL 路径(支持参数,如/users/:id)绑定到一个Controller类。当收到该路径的http协议请求时,会执行关联的中间件链和 Controller 的response方法,模拟 HTTP 处理流程。
Service (抽象类)
继承自 BaseContext。
abstract exec(...args: any[]): unknown | Promise<unknown>: 实现此方法来处理ws请求。参数 (args) 来自fetch调用。channel: Channel(注入): 当前请求的 WebSocket 通道。agent: MicroWebSocketAgent(注入): Agent 实例。fetch<R = any>(...): 调用其他服务。
Controller (抽象类)
继承自 Router。
abstract response(): ControllerResponse | Promise<ControllerResponse>: 实现此方法来生成模拟的 HTTP 响应。static Middleware(...args: IMiddleware[]): ClassDecorator: 类装饰器,用于关联中间件到 Controller。channel: Channel(注入)agent: MicroWebSocketAgent(注入)fetch<R = any>(...): 调用其他服务。@Controller.InComing...: 属性装饰器,用于从请求上下文注入数据 (见下方Router)。
Middleware (抽象类)
继承自 Router。
abstract use(ctx: Context, next: Next): Promise<unknown>: 实现此方法来定义中间件逻辑。调用next()将控制权传递给下一个中间件或 Controller。static Dependencies(...args: IMiddleware[]): ClassDecorator: 类装饰器,用于声明当前中间件依赖的其他中间件。Agent 会自动处理依赖顺序。channel: Channel(注入)agent: MicroWebSocketAgent(注入)fetch<R = any>(...): 调用其他服务。@Middleware.InComing...: 属性装饰器,用于从请求上下文注入数据 (见下方Router)。
Router (基类,Controller 和 Middleware 继承)
static readonly InComing: 包含一组属性装饰器,用于将Context中的数据注入到Controller或Middleware的属性中。Head(key?: string): 从ctx.headers注入。Query(key?: string): 从ctx.query注入。Path(key?: string): 从ctx.params(URL 路径参数) 注入。Cookie(key?: string): 从ctx.cookie注入。Body(): 注入整个ctx.body。State(key?: string): 从ctx.state(中间件共享数据) 注入。- 所有装饰器都支持可选的回调函数进行数据转换/校验。
Context
类似 Koa 的上下文对象,在 HTTP 模拟处理流程中使用。
headers: Record<string, string | string[]>query: Record<string, string | string[]>params: Record<string, string>cookie: Record<string, string>body: anystate: Record<string, any>: 用于在中间件之间传递数据。
贡献
欢迎提交 Pull Request。对于重大更改,请先开一个 Issue 来讨论您想要更改的内容。
