fong - 纯 typescript 的 node gRPC 微服务框架 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
liux466713
V2EX    Node.js

fong - 纯 typescript 的 node gRPC 微服务框架

  •  
  •   liux466713 2019-05-13 11:21:37 +08:00 3891 次点击
    这是一个创建于 2368 天前的主题,其中的信息可能已经有所发展或是发生改变。

    简介

    fong: A service framework of node gRPC.
    github: https://github.com/xiaozhongliu/fong
    fong 是一个完全用 typescript 编写的 node gRPC 框架, 可以基于它很方便地编写 gRPC 微服务应用. 一般是用来编写 service 层应用, 以供 bff 层或前端层等调用.

    优点

    1.纯 typescript 编写, typescript 的好处不用多说了. 并且用户使用这个框架框架时, 查看定义都是 ts 源码, 用户使用框架感受不到 type definition 文件.
    2.效仿egg.js的『约定优于配置』原则, 按照统一的约定进行应用开发, 项目风格一致, 开发模式简单, 上手速度极快. 如果用过 egg, 就会发现一切都是那么熟悉.

    对比

    目前能找到的开源 node gRPC 框架很少, 跟其中 star 稍微多点的 mali 简单对比一下:
    (去 github 看吧, V2EX 还不支持 table 啊)

    |对比方面 |mali |fong | |:--- |:--- |:--- | |项目风格约定 | |√ | |定义查看跳转 |definition |源代码 | |编写语言 |Javascript |typescript | |proto 文件加载 |仅能加载一个 |按目录加载多个| |代码生成 | |√ | |中间件 |√ |√ | |配置 | |√ | |日志 | |√ | |controller 加载| |√ | |service 加载 | |即将支持, 目前可以自己 import 即可 | |util 加载 | |即将支持, 目前可以自己 import 即可 | |入参校验 | |即将支持 | |插件机制 | |打算支持 | |更多功能 | |TBD |

    示例

    示例项目

    github: https://github.com/xiaozhongliu/ts-rpc-seed

    运行服务

    使用 vscode 的话直接进 F5 调试 typescript.
    或者:

    npm start 

    测试请求

    ts-node tester # 或者: npm run tsc node dist/tester.js 

    使用

    目录约定

    不同类型文件只要按以下目录放到相应的文件夹即可自动加载.

    root ├── proto | └── greeter.proto ├── config | ├── config.default.ts | ├── config.dev.ts | ├── config.test.ts | ├── config.stage.ts | └── config.prod.ts ├── midware | └── logger.ts ├── controller | └── greeter.ts ├── service | └── sample.ts ├── util | └── sample.ts └── typings | ├── enum.ts | └── indexed.d.ts ├── log | ├── common.20190512.log | ├── common.20190513.log | ├── request.20190512.log | └── request.20190513.log ├── app ├── packagen ├── tsconfign └── tslintn 

    入口文件

    import App from 'fong' new App().start() 

    配置示例

    默认配置 config.default.ts 与环境配置 config.<NODE_ENV>.ts 是必须的, 运行时会合并.
    配置可从 ctx.config 和 app.config 获取.

    import { AppInfo, Config } from 'fong' export default (appInfo: AppInfo): COnfig=> { return { // basic PORT: 50051, // log COMMON_LOG_PATH: `${appInfo.rootPath}/log/common`, REQUEST_LOG_PATH: `${appInfo.rootPath}/log/request`, } } 

    中间件示例

    注: req 没有放到 ctx, 是为了方便在 controller 中支持强类型.

    import { Context } from 'fong' import 'dayjs/locale/zh-cn' import dayjs from 'dayjs' dayjs.locale('zh-cn') export default async (ctx: Context, req: object, next: Function) => { const start = dayjs() await next() const end = dayjs() ctx.logger.request({ '@duration': end.diff(start, 'millisecond'), controller: `${ctx.controller}.${ctx.action}`, metedata: JSON.stringify(ctx.metadata), request: JSON.stringify(req), response: JSON.stringify(ctx.response), }) } 

    controller 示例

    import { Controller, Context } from 'fong' import HelloReply from '../typings/greeter/HelloReply' export default class GreeterController extends Controller { async sayHello(ctx: Context, req: HelloRequest): Promise<HelloReply> { return new HelloReply( `Hello ${req.name}`, ) } async sayGoodbye(ctx: Context, req: HelloRequest): Promise<HelloReply> { return new HelloReply( `Goodbye ${req.name}`, ) } } 

    日志

    日志文件:
    请求日志: ./log/request.<yyyyMMdd>.log
    其他日志: ./log/common.<yyyyMMdd>.log

    请求日志示例:

    { "@env": "dev", "@region": "unknown", "@timestamp": "2019-05-12T22:23:53.181Z", "@duration": 5, "controller": "Greeter.sayHello", "metedata": "{\"user-agent\":\"grpc-node/1.20.3 grpc-c/7.0.0 (osx; chttp2; godric)\"}", "request": "{\"name\":\"world\"}", "response": "{\"message\":\"Hello world\"}" } 

    代码生成

    代码生成器还未单独封包, 现在放在示例应用的 codegen 目录下.

    使用方法:
    1.定义好契约 proto, 确保格式化了内容.

    2.运行代码生成逻辑:

    ts-node codegen 

    这样就会生成 controller 及相关请求 /响应的 interface/class, 未来会支持更多类型的文件的生成.

    3.从./codegen/dist 目录将生成的 controller 文件移入./controller 文件夹并开始编写方法内部逻辑.

    定义查看跳转

    Peek Definition 直接指向源码.

    近期计划

    service 加载

    service 文件放到 service 文件夹即可自动加载. 通过 ctx.<service>使用.

    util 加载

    util 文件放到 util 文件夹即可自动加载. 通过 ctx.util.<function>使用.

    入参校验

    把在这里用的参数校验中间件搬过来, 用 class-validator 和 class-transformer 实现校验, 支持自动生成.

    应用内的 request model 将会类似:

    import { IsOptional, Length, Min, Max IsBoolean } from 'class-validator' export default class IndexRequest { @Length(4, 8) @IsOptional() foo: string @Min(5) @Max(10) @IsOptional() bar: number @IsBoolean() @IsOptional() baz: boolean } 

    框架内的 validate midware 将会类似:

    import { Context } from 'egg' import { validate } from 'class-validator' import { plainToClass } from 'class-transformer' import HomeIndexRequest from '../request/home/IndexRequest' import HomeValidateRequest from '../request/home/ValidateRequest' const typeMap = new Map([ ['Home.index', HomeIndexRequest], ['Home.validate', HomeValidateRequest], ]) export default async (ctx: Context, next: Function) => { const type = typeMap.get(ctx.routerName) const target = plainToClass(type, ctx.query) const errors = await validate(target) if (!errors.length) return next() ctx.body = { success: false, message: errors.map(error => ({ field: error.property, prompt: error.constraints, })), } } 
    目前尚无回复
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3517 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 32ms UTC 04:21 PVG 12:21 LAX 20:21 JFK 23:21
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86