Skip to Content
🎉 gRPCity 3.0 is released. Read more →
文档高级功能Client Middleware

Client Middleware

客户端中间件在每次出站 RPC 前后包一层。日志、追踪、监控指标、鉴权、入参/响应整形、重试与退避——这些不该写在调用现场的横切逻辑,都应该放在这里。

洋葱模型

gRPCity 用 Koa 风格的洋葱模型中间件:每个函数自己决定何时调 next()next() 之前与之后的代码会把下层逻辑包在中间。

a → b → c 顺序注册三个中间件,调用轨迹如下:

enter a enter b enter c rpc-method() exit c exit b exit a

用法

gRPCity Client 提供了clients.use()的方法用于添加中间件。

当前版本只有 async unaryasync stream 的方法支持中间件,streamcallback 暂不支持。

使用方法如下:

const clients = await loader.initClients() // 逐个添加 clients.use(f1) clients.use(f2) clients.use(f3) // 传入多个参数添加 clients.use(f1, f2, f3) // 使用数组添加 clients.use([f1, f2, f3])

上下文

const middleware = async (ctx, next) => { console.log(ctx) await next() console.log(ctx) }

第一个打印的上下文,可以看到包含了 pathmethodrequest,这些都是在调用服务端前补充的,可以对其进行修改。 其中method包含了调用的类型和请求的metadataoptions参数。

Terminal
{ path: '/helloworld.Greeter/SayGreet', method: { requestStream: false, responseStream: false, metadata: Metadata { internalRepr: [Map], options: {} }, options: { deadline: 2024-01-16T16:22:11.836Z } }, request: { name: 'greeter' } }

第二个打印上下文,额外增加了peerstatusmetadataresponse,都是执行后添加的,都可以修改,最终响应的是修改好的结果。

Terminal
{ path: '/helloworld.Greeter/SayGreet', method: { requestStream: false, responseStream: false, metadata: Metadata { internalRepr: [Map], options: {} }, options: { deadline: 2024-01-16T16:22:11.836Z } }, request: { name: 'greeter' }, metadata: Metadata { internalRepr: Map(9) { 'content-type' => [Array], 'x-cache-control' => [Array], 'x-business-id' => [Array], 'x-timestamp-client' => [Array], 'x-client-hostname' => [Array], 'x-service-path' => [Array], 'user-agent' => [Array], 'x-timestamp-server' => [Array], 'date' => [Array] }, options: {} }, response: { message: 'hello, greeter' }, status: { code: 0, details: 'OK', metadata: Metadata { internalRepr: Map(0) {}, options: {} } }, peer: '::1:9099' }

例子

以调用日志打印作为例子:

log.js
const log = async (ctx, next) => { const startTime = Date.now() await next() const responseTimeMs = Date.now() - startTime console.log({ path: ctx.path, peer: ctx.peer, responseTimeMs }) } } export default log

添加中间件

client.js
import log from './log.js' // other... clients.use(log)

至此,我们完成了客户端中间件的编写与使用。

Last updated on