前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >腾讯 tRPC-Go 教学——(2)trpc HTTP 能力

腾讯 tRPC-Go 教学——(2)trpc HTTP 能力

原创
作者头像
amc
修改2024-05-01 13:25:41
6180
修改2024-05-01 13:25:41
举报
文章被收录于专栏:后台全栈之路后台全栈之路

上一篇文章 中我们快速搭建了一个 http API 服务,并且我们可以看到,对外提供了 URL query 和 application/json 两种服务模式。那么实际上,我们到底实现了什么、并且能够做些什么?读者可能还是没有直观的感受,因此必要先来简单 review 一下。就让我们先放下敲代码的小手,一起看看刚刚写出来的都是些什么玩意儿吧。

系列文章


先说说内部版和开源版的 tRPC

首先要说明的是,腾讯内部使用的 tRPC 与开源版的 tRPC,虽然并不是 100% 相同,但大部份的代码和基本的功能是基本一致。官方对开源版的 PR 比较谨慎,在我提出的 PR 中维护者也提及了这一点,这为的就是尽量保持内部与外部版本的尽量一致性。

笔者虽然是腾讯员工,但并不是 tRPC 团队的开发者,而只是内部版和开源版双边的使用者。撰此系列文章,我的资料主要来源于以下这些:

  1. 对开源版的代码阅读
  2. 内部版和开源版逻辑一致的、可脱敏的资料
  3. 内部版本的一些使用经验和我们团队的经验
  4. 个人喜好和观点(当然我的个人观点多少也是会影响第 3 条的团队决策的哈哈)

所以,也还请读者不要将笔者视作 tRPC 官方,就当作是一名普通程序员就行了~~


从 proto 桩代码说起

业务代码与 trpc 服务的绑定

我们看看例子中的 service:

代码语言:proto
复制
service HelloWorld {
  rpc Hello(HelloRequest) returns (HelloResponse); // @alias=/demo/Hello
}

平平无奇的一个 service,经过 trpc 工具编译后,生成了 echo.pb.goecho.trpc.go 两个文件。前者和使用 gRPC 的 proto-gen-go 工具生成的差别不大,我们就不用讲了。我们看看后面那个。

echo.trpc.go 文件中,trpc 工具生成了一个服务端接口:

代码语言:go
复制
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 关键字的作用

继续往下看,可以留意到下面这段代码:

代码语言:go
复制
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.yaml
    • log/ - 日志文件。这个我后文再讲

由于配置文件与可执行文件或工作目录不在同一个路径下,因此我们启动服务的时候经常需要 -conf 参数制定配置文件的路径。

接下来,我们看一下配置文件中的内容:

代码语言:yaml
复制
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,很快可以找打下面的代码段:

代码语言:go
复制
// 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

网络类型参数就是 tcpudp 两类。两者能够支持的应用层协议不同。除非是极为极致的性能和高并发需求,否则我们一般还是固定使用 tcp。至于 protocol 参数,一般就是在 httptrpc 这两者之间选。当然也支持 grpc,读者可以试一下,笔者没有实际用过。

在前面的例子中,我们部署了一个 HTTP 服务,因此这里我们填写的是 http。如果填写 trpcgrpc,那么无需修改任何业务逻辑, 框架会自动字改为配置所指定的服务协议。

timeout

毫秒级的超时时间。这个参数会影响在 context 中的时间,如果配置了超时时间,trpc 框架在调用业务逻辑的时候,会给 context 加上这个 timeout。


tRPC 的 HTTP 服务模式

上一篇文章我们分别是用了两种模式来调用 Hello 方法,通过这个例子我们可以知道,trpc 服务框架会自动适配前端不同的调用方式、解析数据并调用业务逻辑。这一点对我们来说是非常舒服的,这让开发者们不用去关心业务无关的东西,一切都交给协议和框架解决。

我们对外提供的 HTTP 服务格式,常见的是以下三种:

  1. application/json: 前端可以使用 GET 或 POST 方式,在 body 中放置 JSON 数据作为入参
  2. application/proto: 与前面一样,不同的是 body 中是 protobuf 编码后的字节流
  3. 如果没有指定 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

原文链接:htthttps://cloud.tencent.com/developer/article/2379587

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 系列文章
  • 先说说内部版和开源版的 tRPC
  • 从 proto 桩代码说起
    • 业务代码与 trpc 服务的绑定
      • alias 关键字的作用
      • trpc_go.yaml 配置
        • name
          • nic、ip 和 port
            • network 和 protocol
              • timeout
              • tRPC 的 HTTP 服务模式
              • 下一步
              相关产品与服务
              API 网关
              腾讯云 API 网关(API Gateway)是腾讯云推出的一种 API 托管服务,能提供 API 的完整生命周期管理,包括创建、维护、发布、运行、下线等。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档