上一篇文章 中我们快速搭建了一个 http API 服务,并且我们可以看到,对外提供了 URL query 和 application/json
两种服务模式。那么实际上,我们到底实现了什么、并且能够做些什么?读者可能还是没有直观的感受,因此必要先来简单 review 一下。就让我们先放下敲代码的小手,一起看看刚刚写出来的都是些什么玩意儿吧。
系列文章
- 腾讯 tRPC-Go 教学——(1)搭建服务
- 腾讯 tRPC-Go 教学——(2)trpc HTTP 能力
- 腾讯 tRPC-Go 教学——(3)微服务间调用
- 腾讯 tRPC-Go 教学——(4)tRPC 组件生态和使用
- 腾讯 tRPC-Go 教学——(5)filter、context 和日志组件
- 腾讯 tRPC-Go 教学——(6)服务发现
- 腾讯 tRPC-Go 教学——(7)服务配置和指标上报
- …… 还会有更多,敬请期待 ……
先说说内部版和开源版的 tRPC
首先要说明的是,腾讯内部使用的 tRPC 与开源版的 tRPC,虽然并不是 100% 相同,但大部份的代码和基本的功能是基本一致。官方对开源版的 PR 比较谨慎,在我提出的 PR 中维护者也提及了这一点,这为的就是尽量保持内部与外部版本的尽量一致性。
笔者虽然是腾讯员工,但并不是 tRPC 团队的开发者,而只是内部版和开源版双边的使用者。撰此系列文章,我的资料主要来源于以下这些:
- 对开源版的代码阅读
- 内部版和开源版逻辑一致的、可脱敏的资料
- 内部版本的一些使用经验和我们团队的经验
- 个人喜好和观点(当然我的个人观点多少也是会影响第 3 条的团队决策的哈哈)
所以,也还请读者不要将笔者视作 tRPC 官方,就当作是一名普通程序员就行了~~
从 proto 桩代码说起
业务代码与 trpc 服务的绑定
我们看看例子中的 service:
service HelloWorld {
rpc Hello(HelloRequest) returns (HelloResponse); // @alias=/demo/Hello
}
平平无奇的一个 service,经过 trpc 工具编译后,生成了 echo.pb.go
和 echo.trpc.go
两个文件。前者和使用 gRPC 的 proto-gen-go 工具生成的差别不大,我们就不用讲了。我们看看后面那个。
echo.trpc.go
文件中,trpc 工具生成了一个服务端接口:
type HelloWorldService interface {
Hello(ctx context.Context, req *HelloRequest) (*HelloResponse, error) // @alias=/demo/Hello
}
// ......
func RegisterHelloWorldService(s server.Service, svr HelloWorldService) {
if err := s.Register(&HelloWorldServer_ServiceDesc, svr); err != nil {
panic(fmt.Sprintf("HelloWorld register error:%v", err))
}
}
很好理解,RegisterHelloWorldService
函数将 trpc 服务和我们的具体业务实现绑定在了一起,启动服务就对接上了业务逻辑。这跟 gRPC 的思路是一致的。
alias 关键字的作用
继续往下看,可以留意到下面这段代码:
var HelloWorldServer_ServiceDesc = server.ServiceDesc{
ServiceName: "demo.simplest.HelloWorld",
HandlerType: ((*HelloWorldService)(nil)),
Methods: []server.Method{
{
Name: "/demo/Hello",
Func: HelloWorldService_Hello_Handler,
},
{
Name: "/demo.simplest.HelloWorld/Hello",
Func: HelloWorldService_Hello_Handler,
},
},
}
这里其实就是前文 @alias
的作用了。显然,trpc 默认是使用 package/method
的格式定义一个接口方法的路径;而 alias 的作用则是在这基础上再额外注册了一个路径。上面的这两个路径,都可以直接通过 http 访问到。
trpc_go.yaml 配置
上文提到,trpc 服务启动需要搭配一个 yaml 配置文件。tRPC 的文档会告诉你默认使用与工作目录同级的 trpc_go.yaml 文件,但实际上考虑到在 Kubernetes 中挂载的需要,我们往往会将配置文件独立在一个目录下,而可执行文件在另一个目录下,再配合日志(也需要挂载,方便日志采集),这就构成了这样的一个结构:
工作目录
bin/
- 可执行文件,包括服务程序和一些启动脚本conf/
- 服务配置文件,主要就是 trpc_go.yamllog/
- 日志文件。这个我后文再讲
由于配置文件与可执行文件或工作目录不在同一个路径下,因此我们启动服务的时候经常需要 -conf
参数制定配置文件的路径。
接下来,我们看一下配置文件中的内容:
server:
service:
- name: demo.simplest.HelloWorld
nic: eth0
# ip: 127.0.0.1
port: 8000
network: tcp
protocol: http
timeout: 1800
可以看明白的是:我们注册了一个叫做 demo.simplest.HelloWorld
的服务,监听端口 8000,服务工作在 tcp 协议上,应用层采用 http 协议,超时时间是 1800 毫秒。这几个参数我们都是需要好好说道说道的。
name
trpc 的文档中,并没有详细说明这个 name 应该取什么值。一般而言,这个 name
值等于你 proto 中定义的 package 名 + 服务名。
不过你不用记这个规则。实际上我们直接以生成的 trpc.go 文件为准。我们打开之前我们生成的 echo.trpc.go
,搜索 Register
,很快可以找打下面的代码段:
// RegisterHelloWorldService registers service.
func RegisterHelloWorldService(s server.Service, svr HelloWorldService) {
if err := s.Register(&HelloWorldServer_ServiceDesc, svr); err != nil {
panic(fmt.Sprintf("HelloWorld register error:%v", err))
}
}
找到 s.Register
的第一个参数 HelloWorldServer_ServiceDesc
的定义,对,就是前面我们讲 alias 关键字的时候看到的那个结构体中的 ServiceName
字段,字段的值就是我们应该填在配置的 name
字段中的值。
此外, trpc 的官方文档会告诉你,如果当前进程仅定义了一个 service(这是绝大部分微服务的情况),那么实际上这个 service name 字段定义成什么都没关系,因为 trpc 会自动寻找这唯一的 service 配置,并且自动适配它。尽管如此,笔者依然不建议你因为有了这一条 feature,就随便写,咱们还是按规范的好。后面这个规范在作为客户端的时候也需要遵从,等用到了笔者再提吧。
nic、ip 和 port
ip
就是表示服务应该监听在哪一个 ip 上;而 nic
是 network interface card 的缩写,表示监听哪一个网卡。在生产环境中,应该是 nic
参数用得比较多,而在开发 / 测试的时候,当服务并不是部署在 Kubernetes 上的时候,ip 则提供了更大的自由度。
port
很好理解,表示监听的端口
network 和 protocol
网络类型参数就是 tcp
和 udp
两类。两者能够支持的应用层协议不同。除非是极为极致的性能和高并发需求,否则我们一般还是固定使用 tcp
。至于 protocol
参数,一般就是在 http
和 trpc
这两者之间选。当然也支持 grpc
,读者可以试一下,笔者没有实际用过。
在前面的例子中,我们部署了一个 HTTP 服务,因此这里我们填写的是 http
。如果填写 trpc
和 grpc
,那么无需修改任何业务逻辑, 框架会自动字改为配置所指定的服务协议。
timeout
毫秒级的超时时间。这个参数会影响在 context
中的时间,如果配置了超时时间,trpc 框架在调用业务逻辑的时候,会给 context 加上这个 timeout。
tRPC 的 HTTP 服务模式
上一篇文章我们分别是用了两种模式来调用 Hello
方法,通过这个例子我们可以知道,trpc 服务框架会自动适配前端不同的调用方式、解析数据并调用业务逻辑。这一点对我们来说是非常舒服的,这让开发者们不用去关心业务无关的东西,一切都交给协议和框架解决。
我们对外提供的 HTTP 服务格式,常见的是以下三种:
application/json
: 前端可以使用 GET 或 POST 方式,在 body 中放置 JSON 数据作为入参application/proto
: 与前面一样,不同的是 body 中是 protobuf 编码后的字节流- 如果没有指定 body 格式,那么 trpc 也会尝试从 url query 参数中寻找协议所需的参数
总而言之,如果是定在最前面的 HTTP API,最好跟前端同学约定,一般情况下就使用 POST application/json
把,毕竟 URL query 遇到复杂数据类型比较麻烦,而 proto 前端不太处理了。
下一步
本文,我们简单介绍了上一篇文章中提到的 hello world 服务的各种参数。至此,我们开启了一个最简单的服务,对前端提供了最简单的响应。
然而,当我引入文中的这些概念之后,聪明的读者们肯定有更多的问题。而这些功能,也只不过是 tRPC-Go 众多功能的冰山一角上的一粒冰片。
接下来,我会带领大家开始拉起一个真正的微服务,构建一个完整的系统。我也会列出各种 trpc 服务仓库的目录组织形式,这个目录组织形式也方便对微服务进行单体化构建。与 trpc 官方给出的建议、和 trpc 工具自动生成的不同,这也就是为什么我不使用 trpc 工具的默认行为。
同时,trpc 的周边服务生态也是必不可少的一环,必然需要一并讲述。
此外,我也会详细说明我是如何在 tRPC-Go 中践行 微服务+单体 架构的,比起之前我文章中干巴巴的描述,上代码会来得更清楚。
本文章采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。
原作者: amc,原文发布于腾讯云开发者社区,也是本人的博客。欢迎转载,但请注明出处。
原文标题:《手把手 tRPC-Go 教学——(2)trpc HTTP 能力》
发布日期:2024-01-16
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。