dubbo-js
dubbo-js copied to clipboard
dubbo3 - stub模块
目标
- 研究IDL机制,通过编译生成nodejs的rpc服务的签名 - 包含调用方client和服务提供方service
- 实现服务的service repository的代理调用能力,client的服务调用代理能力
- 和invoke紧密结合,实现调用对接
TODO
同步下目前进展: 已实现通过引入protoc获取pb文件“ast”( protoc --plugin=./protoc-scene-dubbojs_proto --ts_proto_out=. ./simple.proto);然后单元测试通过ast文件进行处理; 具体任务如下:
- [x] prototype json ast 提取逻辑
- [x] prototype 用例测试
- [ ] 封装ast操作,包含领域对象File、Method、Service、Message、其他
- [ ] 定义stub的服务接口标准
- [ ] Ast 代码生成对接
- [ ] stubClient 代码格式对接,确定示例;
- [ ] stubClient 底层逻辑处理对接;
接下来需讨论以下示例生成node client代码标准: ··· syntax = "proto3";
option java_multiple_files = true; option java_package = "org.apache.dubbo.demo"; option java_outer_classname = "DemoServiceProto"; option objc_class_prefix = "DEMOSRV";
package demoservice;
// The demo service definition. service DemoService { rpc SayHello (HelloRequest) returns (HelloReply) {} }
// The request message containing the user's name. message HelloRequest { string name = 1; }
// The response message containing the greetings message HelloReply { string message = 1; }
···
东哥,你的repo 我直接拉么?
grpc 生成的client stub service的 代码逻辑:
// The greeting service definition.
var GreeterService = exports.GreeterService = {
// Sends a greeting
sayHello: {
path: '/helloworld.Greeter/SayHello',
requestStream: false,
responseStream: false,
requestType: helloworld_pb.HelloRequest,
responseType: helloworld_pb.HelloReply,
requestSerialize: serialize_helloworld_HelloRequest,
requestDeserialize: deserialize_helloworld_HelloRequest,
responseSerialize: serialize_helloworld_HelloReply,
responseDeserialize: deserialize_helloworld_HelloReply,
},
};
exports.GreeterClient = grpc.makeGenericClientConstructor(GreeterService);
针对dubbo-js设计: 我建议这块的设计仍然可以参考dubbo-js/dubbo2分支的proxyservice的设计,对于client方invoke调用来说核心的是两点,
- 方便获取服务调用的metadata和payload
- path, request/response stream flag, req/res type schema
- 开发的体验,通过typescript类型的动态推导,实现真正的本地方法调用的体验
- 代码的提示,静态的检查等
还有序列话模块的部分,我们是不是应该设计出一个独立的encode和decode的能力,不跟着具体的生成的rpc的stub代码走
- 交给invoke在请求之前进行调用
- 这样的好处,生成的stub service比较干净简单 代码量也非常的少
- stub service 简单意味着将来我们特性改进如果升级比较简单一些
- 序列化模块根据参数重的req/res的type schema进行序列化和反序列化
其他同学 补充讨论 ~~~
针对上面思考,我初步的想法是,
service DemoService {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
对应的client stub service
// generate request type
export interface HelloRequest {
name:string
}
// generate response type
export interface HelloReply {
message: string
}
// generate service interface
export interface IHelloService {
sayHello(helloRequest: HelloRequest): Promise<HelloReply>
}
// generate dubbo client stub service
export const HelloService = dubbo.newClientStubService<IHelloService>({
name: 'HelloService',
methods: {
sayHello(helloRequest) {
return {
args: [HelloRequest],
//... maybe other information
}
},
})
dubboClient.addStubServices({helloSerivce: HelloService})
对应的server部分
// generate request type
export interface HelloRequest {
name:string
}
// generate response type
export interface HelloReply {
message: string
}
export default abstract class HelloService {
path: string = 'HelloService'
// sub class implements this abstract method
abstract sayHello(req: HelloRequest): Promise<HelloReply>
}
export class HelloService extends AbstractHelloService {
async sayHello(req: HelloRequest): Promise<HelloReply>: Promise<HelloReply> {
return {message: `hello from server and receive from ${req.name}}
}
}
dubboServer.addServices({HelloService})
因为 protobuf 比较出名了,所以我在想是不是可以基于现有的开源库来实现,比如 ts-proto
类似 ts-proto(可能需要注意下开源协议)这样我们是否可以直接使用,或者基于它的一些工作,比如直接使用它的前端解析器部分
@hsiaosiyuan0 目前的生成流程也借鉴了ts-proto等库,如:pb文件解析(也是基于官方protoc)及fileDescript类型定义(google提供),其他一些基础方法在coding时可以参考;
两种形态确实要讨论下:
- 在ts-proto库写 generate扩展,以生成dubbojs stub;
- 单独封装生成命令;
只要拿到了ast,其实就解决大部分问题了,剩下的只是根据我们的诉求生成dubbo-client/dubbo-server的stub service 方法上可以借鉴,但是我们还是要梳理我们需要什么,如果我们不需要那么重的实现逻辑,也可以自己实现。
现在的重点也要思考生成代码的样子。
杨晓东 在 2022年11月21日 星期一 14:32:50 (+08:00) 写道:
@hsiaosiyuan0 目前的生成流程也借鉴了ts-proto等库,如:pb文件解析(也是基于官方protoc)及fileDescript类型定义(google提供),其他一些基础方法在coding时可以参考;
两种形态确实要讨论下:
在ts-proto库写 generate扩展,以生成dubbojs stub; 单独封装生成命令;
— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you authored the thread.
@creasy2010 我们要把dubbo-client/dubbo-server的stub service 接口设计出来,这样大家就好协同了,模块内部的实现不影响大家的并行协作,我和invoker模块 冯伟同学整体过了下 他这边要做的事情,我们可以根据我们上面的stub service定义 先开展起来,过程中有需要完善的再调整。
@hufeng 目前是按照示例的内容在推进;
@creasy2010 东哥,这个地方,
// generate request type
export interface HelloRequest {
name:string
}
// generate response type
export interface HelloReply {
message: string
}
// generate service interface
export interface IHelloService {
sayHello(helloRequest: HelloRequest): Promise<HelloReply>
}
// generate dubbo client stub service
export const HelloService = dubbo.newClientStubService<IHelloService>({
name: 'HelloService',
methods: {
sayHello(helloRequest) {
return {
args: [HelloRequest],
//... maybe other information
}
},
})
dubboClient.addStubServices({helloSerivce: HelloService})
这个地方我的想法 对newClientStubService方法设计,需要考虑点:
- 对于invoke来说,他需要的是数据结构本身,其实这个api类似于dubbojs2中的proxyService啥也没有做就是返回这个数据结构
- 之前设计proxyService的逻辑是类型约束,如果是类型约束也可以不需要函数来做,直接一个interface也行
- 为了类型的推导我们覆盖了 dubbo.newClientStubService<IHelloService> 返回值类型,这样不好,导致代码内部拿不到原始类型
这个方法可以约束类型,但不应该覆盖返回类型,返回类型单独export出来
// generate request type
export interface HelloRequest {
name:string
}
// generate response type
export interface HelloReply {
message: string
}
// generate service interface
export interface IHelloService {
sayHello(helloRequest: HelloRequest): Promise<HelloReply>
}
// generate dubbo client stub service
export const HelloService = dubbo.newClientStubService({
name: 'HelloService',
methods: {
sayHello(helloRequest) {
return {
args: [HelloRequest],
//... maybe other information
}
},
})
dubboClient.addStubServices({helloSerivce: HelloService})