Node.js+TypeScript gRPC微服务网关实现问题咨询
关于ts-proto生成的fromJSON方法
你的判断完全正确,这个方法的作用就是把符合proto字段规则的普通JS对象/JSON请求体,转换为带完整类型约束的proto消息实例,对应的反向转换方法是toJSON,用来把proto实例序列化为普通JSON对象返回给调用方。
你写的const product = Product.fromJSON(req.body)逻辑本身没问题,但要注意你的AddProductRequest定义是嵌套了一层product字段的,实际构造请求参数的时候不要漏了这层结构。
Express网关是否需要同时作为gRPC客户端
这是gRPC微服务架构下API网关的标准角色,完全合理:
- 网关对外暴露HTTP接口,承接客户端的JSON格式请求
- 对内网关就是所有下游微服务的gRPC客户端,负责完成协议转换:JSON转proto发起RPC调用,拿到微服务返回的proto结果后再转成JSON返回给客户端
不需要额外拆分服务,这个架构是通用实践。
Express网关调用商品微服务的具体实现
你需要在网关项目里安装@grpc/grpc-js依赖,复用已经用ts-proto编译好的proto文件,按两步走:
第一步:初始化gRPC客户端连接
在网关项目中新建客户端初始化文件,比如src/grpc/clients.ts:
import * as grpc from '@grpc/grpc-js'; // 替换为你自己项目中ts-proto编译输出的服务定义路径 import { CoreServiceClient } from '../proto/generated/core'; // 连接本地启动的商品服务,地址端口和你商品服务绑定的8081保持一致 export const productClient = new CoreServiceClient( 'localhost:8081', grpc.credentials.createInsecure() );
第二步:在Express路由中发起RPC调用
注意构造参数时匹配AddProductRequest的结构,路由代码示例:
import { Router } from 'express'; import { productClient } from '../grpc/clients'; // 替换为你自己项目的编译产物路径 import { Product } from '../proto/generated/product'; const router = Router(); router.post('/AddProduct', (req, res) => { try { // 构造AddProductRequest要求的参数结构 const rpcParams = { product: Product.fromJSON(req.body) }; // 发起RPC调用,注意ts-proto生成的客户端方法默认是小驼峰命名 productClient.addProduct(rpcParams, (err, resp) => { if (err) { res.status(500).json({ error: err.message }); return; } // 将返回的proto对象转为普通JSON返回 res.json({ data: resp.product }); }); } catch (e) { res.status(400).json({ error: '请求参数格式不符合要求' }); } }); export default router;
有个容易踩的坑:proto里定义的RPC方法名是大驼峰AddProduct,ts-proto生成的客户端方法默认转成小驼峰addProduct,写的时候注意大小写,不然会报方法不存在的错误。
Node.js/TypeScript对JSON转proto的支持
完全支持,不需要额外找工具。ts-proto生成的fromJSON/toJSON方法已经覆盖了所有proto类型的转换逻辑,包括嵌套消息、枚举、时间戳等特殊类型,转换能力和Java、Go生态的proto JSON序列化能力完全对齐,直接用就可以满足你的需求。
补充提醒
你贴的商品服务代码目前只完成了服务启动和端口绑定,还没有把CoreService的具体实现注册到gRPC服务中,启动后客户端调用会报「方法未实现」的错误,记得补全服务注册逻辑。本地开发用insecure明文连接没问题,上线时记得替换为SSL凭证加密RPC流量。
内容的提问来源于stack exchange,提问作者Arnob

