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 unary 和 async stream 的方法支持中间件,stream 和 callback 暂不支持。
使用方法如下:
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)
}第一个打印的上下文,可以看到包含了 path、method、request,这些都是在调用服务端前补充的,可以对其进行修改。
其中method包含了调用的类型和请求的metadata和options参数。
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' }
}第二个打印上下文,额外增加了peer、status、metadata 和 response,都是执行后添加的,都可以修改,最终响应的是修改好的结果。
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