trpc-group / trpc-cpp

A pluggable, high-performance RPC framework written in cpp
Other
270 stars 78 forks source link

【腾讯犀牛鸟计划】为tRPC-Cpp实现一个简单协议的编码器 #139

Closed weimch closed 1 month ago

weimch commented 2 months ago

issue介绍

tRPC从设计就支持多种协议,目前tRPC-Cpp已经支持了额外的http/grpc协议,任何人只要扩展codec模块,即可非常快速地实现对三方协议的对接。

你需要开发一个简单协议(这个协议非常简单,不能用在实际生产当中),并用腾讯文档输出一份报告到issue回复里,报告需要包含你实现的关键代码、日志输出以及你在实践过程中的心得体会,最后提交相关变更代码到你个人的repo以及编译运行指引到issue回复里,方便我们测试验证你的确完成了issue。

具体地,你可以按照下面的步骤完成此issue 1、请求协议定义:4bytes(协议包总长度)+1byte(方法名长度)+方法名+业务PB序列化后的请求包;响应协议定义:4byte(协议包总长度)+1byte(错误码)+业务PB序列化后的响应包。 2、你需要参考 trpc/codec/trpc 来实现ServerCodec、ClientCodec、ProtocolChecker、Protocol,并完成注册。 3、你可以复用examples/helloworld的代码来做测试,使用pb option扩展别名的方式,定义接口的方法名,以方便你协议进行接口方法的打解包,以及路由到正确的接口实现里。 4、你需要为helloworld的pb里新增一个接口,包含已有一个接口,两个接口都需能正常地调用成功,并打印你的pb请求响应消息内容,以及协议定义的响应码。

参考资料

1、使用pb option扩展别名:https://github.com/trpc-group/trpc-cpp/blob/main/docs/zh/bazel_or_cmake.md#%E4%BD%BF%E7%94%A8option%E6%89%A9%E5%B1%95alias%E5%88%AB%E5%90%8D

其他说明

本issue为2024犀牛鸟开源人才培养活动专属issue,仅供在校大学生参与领取 【认领issue】在研学基地"issue营地"对应issue行的M~R列抢滩报名,即视为认领成功。 【完成issue】已认领issue的同学,请同步在本issue评论区回复“已成功领取本issue”; 如7天内无提交任何进展包括不限于comment \ commit \ Pull Request,则将视为同学主动放弃issue,组委会将释放issue给下一位等候者。

PoloDyBala commented 2 months ago

已成功领取本issue

PoloDyBala commented 2 months ago

已成功领取本issue

【腾讯文档】TRPC(updating) https://docs.qq.com/aio/DQlZTUU5oVkZkQXhK

weimch commented 2 months ago

如果完成了,可以提供用于测试验证你的确完成的github repo,并附上编译运行指引

PoloDyBala commented 2 months ago

如果完成了,可以提供用于测试验证你的确完成的github repo,并附上编译运行指引

收到

raojunacc commented 1 month ago

已领取

PoloDyBala commented 1 month ago

已领取

此issue已认领

PoloDyBala commented 1 month ago

如果完成了,可以提供用于测试验证你的确完成的github repo,并附上编译运行指引

已完成:https://github.com/PoloDyBala/trpc-cpp/blob/self-protocol/examples/features/demo/readme.md

weimch commented 1 month ago

虽然你的例子能够跑通,但实现是有下面问题的:

  1. client.cc是用户发起调用的代码,用户只需要设置请求内容,然后调用接口即可发起调用。而协议可以承载请求的内容,也就是说,填充协议的细节不应该放在client.cc里,应该在ClientCodec的FillRequest里,拿到请求的大小进行填充(你可以参考trpc_client_codec.cc)。
  2. 服务端并没有方法分发的逻辑,而是直接调了service的方法处理接口。
  3. 作为一个插件,业务是不需要关心用了哪种协议的,同样的代码,只需要在yaml里换不同的协议字段,即可成功访问,这一点在实现里是没有体现到的。

下面是我给的一些改造指引,你可以参考这些指引看能否对代码进行一些调整

  1. 实际上在helloworld的例子,只需要新增注册Codec插件的代码以及改变yaml里配置的protocol字段,其他代码无需改变,即可接入到自定义协议插件。
  2. 关于协议字段方法名,方法名是在trpc.pb.cc(通过trpc_proto_library生成的trpc桩代码)进行填充的。对于客户端来说,在发起调用时(proxy->SayeHello调用),是使用你填充的别名。对于服务端来说,在服务注册时(比如TrpcApp的Intialize里RegisterService(service_name, std::make_shared());),会使用桩代码中的别名注册,因此受到请求后,能根据请求协议的方法名方法到对于的方法handler。
  3. 2中说的方法名填充,服务注册方法名,根据方法名分发到对于的RPC接口,是不需要你关注的,你只需要关注协议填充以解 析。具体来说,在客户端,你需要在FillRequest里将client.cc里传入的pb对象序列化,并设置协议里业务数据的大小字段,关于方法字段设置,你需要实现protocol的两个接口(SetFuncName)框架就会在桩代码里把方法名自动设到protocol里;在服务端,你只需要在ZeroCopyDecode解析协议后,框架能通过Protocol的GetFuncName/GetNonContiguousProtocolBody分别拿到方法名,以及业务的pb序列化后的数据(pb反序列化你依然不需要关注,框架会自动做的)即可(你需要重载这两个方法)。

你可以按上面的指引,结合trpc_server_codec.cc/trpc_client_codec.cc先改善下代码,如果有不懂的地方,欢迎随时咨询

PoloDyBala commented 1 month ago

虽然你的例子能够跑通,但实现是有下面问题的:

  1. client.cc是用户发起调用的代码,用户只需要设置请求内容,然后调用接口即可发起调用。而协议可以承载请求的内容,也就是说,填充协议的细节不应该放在client.cc里,应该在ClientCodec的FillRequest里,拿到请求的大小进行填充(你可以参考trpc_client_codec.cc)。
  2. 服务端并没有方法分发的逻辑,而是直接调了service的方法处理接口。
  3. 作为一个插件,业务是不需要关心用了哪种协议的,同样的代码,只需要在yaml里换不同的协议字段,即可成功访问,这一点在实现里是没有体现到的。

下面是我给的一些改造指引,你可以参考这些指引看能否对代码进行一些调整

  1. 实际上在helloworld的例子,只需要新增注册Codec插件的代码以及改变yaml里配置的protocol字段,其他代码无需改变,即可接入到自定义协议插件。
  2. 关于协议字段方法名,方法名是在trpc.pb.cc(通过trpc_proto_library生成的trpc桩代码)进行填充的。对于客户端来说,在发起调用时(proxy->SayeHello调用),是使用你填充的别名。对于服务端来说,在服务注册时(比如TrpcApp的Intialize里RegisterService(service_name, std::make_shared());),会使用桩代码中的别名注册,因此受到请求后,能根据请求协议的方法名方法到对于的方法handler。
  3. 2中说的方法名填充,服务注册方法名,根据方法名分发到对于的RPC接口,是不需要你关注的,你只需要关注协议填充以解 析。具体来说,在客户端,你需要在FillRequest里将client.cc里传入的pb对象序列化,并设置协议里业务数据的大小字段,关于方法字段设置,你需要实现protocol的两个接口(SetFuncName)框架就会在桩代码里把方法名自动设到protocol里;在服务端,你只需要在ZeroCopyDecode解析协议后,框架能通过Protocol的GetFuncName/GetNonContiguousProtocolBody分别拿到方法名,以及业务的pb序列化后的数据(pb反序列化你依然不需要关注,框架会自动做的)即可(你需要重载这两个方法)。

Thank you for your review. I will make the necessary modifications as soon as possible.

weimch commented 1 month ago

按建议调整有遇到问题吗?

PoloDyBala commented 1 month ago

按建议调整有遇到问题吗?

老师你好,按照您的修改了一版。目前没有修改helloworld示例的代码(只增加注册自定义插件的逻辑)。 但是在FillRequest(将传入的pb对象序列化)之后, 框架通过GetNonContiguousProtocolBody()得到序列化的请求信息。然后框架解码请求信息的时候会报错:pb deserialize failed。从而导致没有成功调用RPC服务,response信息就没拿到。 https://github.com/PoloDyBala/trpc-cpp/tree/new-protocol/examples/helloworld 总的来说就是在DemoRequestProtocol::ZeroCopyDecode --- TrpcServerCodec::ZeroCopyEncode步骤之间,因为pb deserialize failed的原因导致没有成功调用RPC方法。

weimch commented 1 month ago

按建议调整有遇到问题吗?

老师你好,按照您的修改了一版。目前没有修改helloworld示例的代码(只增加注册自定义插件的逻辑)。 但是在FillRequest(将传入的pb对象序列化)之后, 框架通过GetNonContiguousProtocolBody()得到序列化的请求信息。然后框架解码请求信息的时候会报错:pb deserialize failed。从而导致没有成功调用RPC服务,response信息就没拿到。 https://github.com/PoloDyBala/trpc-cpp/tree/new-protocol/examples/helloworld 总的来说就是在DemoRequestProtocol::ZeroCopyDecode --- TrpcServerCodec::ZeroCopyEncode步骤之间,因为pb deserialize failed的原因导致没有成功调用RPC方法。

是服务端报错?

PoloDyBala commented 1 month ago

按建议调整有遇到问题吗?

老师你好,按照您的修改了一版。目前没有修改helloworld示例的代码(只增加注册自定义插件的逻辑)。 但是在FillRequest(将传入的pb对象序列化)之后, 框架通过GetNonContiguousProtocolBody()得到序列化的请求信息。然后框架解码请求信息的时候会报错:pb deserialize failed。从而导致没有成功调用RPC服务,response信息就没拿到。 https://github.com/PoloDyBala/trpc-cpp/tree/new-protocol/examples/helloworld 总的来说就是在DemoRequestProtocol::ZeroCopyDecode --- TrpcServerCodec::ZeroCopyEncode步骤之间,因为pb deserialize failed的原因导致没有成功调用RPC方法。

是服务端报错? 是的 image

image

weimch commented 1 month ago

按建议调整有遇到问题吗?

老师你好,按照您的修改了一版。目前没有修改helloworld示例的代码(只增加注册自定义插件的逻辑)。 但是在FillRequest(将传入的pb对象序列化)之后, 框架通过GetNonContiguousProtocolBody()得到序列化的请求信息。然后框架解码请求信息的时候会报错:pb deserialize failed。从而导致没有成功调用RPC服务,response信息就没拿到。 https://github.com/PoloDyBala/trpc-cpp/tree/new-protocol/examples/helloworld 总的来说就是在DemoRequestProtocol::ZeroCopyDecode --- TrpcServerCodec::ZeroCopyEncode步骤之间,因为pb deserialize failed的原因导致没有成功调用RPC方法。

是服务端报错? 是的 image

image

你把客户端序列化后,服务端反序列化前的二进制数据打出来(用十六进制打印出来),对比下看看是哪里有问题

PoloDyBala commented 1 month ago

你把客户端序列化后,服务端反序列化前的二进制数据打出来(用十六进制打印出来),对比下看看是哪里有问题 问题已修复,现在可以接入helloworld的例子,只需要注册插件以及对应yaml中的配置。前面报错的原因在于Decode中的buff忘记Skip,导致数据切分错误。 https://github.com/PoloDyBala/trpc-cpp/tree/new-protocol/examples/helloworld

weimch commented 1 month ago

验证测试通用,该issue由 @PoloDyBala 完成。