tRPC-Go in Action
业务服务开发步骤
- 每个服务单独创建一个 git,如:
git.woa.com/trpc-go/helloworld
- 初始化 go mod 文件:
go mod init git.woa.com/trpc-go/helloworld
- 编写服务协议文件,如:
helloworld.proto
, 协议规范如下:package
分成三级trpc.app.server
,app
是一个业务项目分类,server
是具体的进程服务名- 必须指定
option go_package
,表明协议的git
地址 - 定义
service rpc
方法,一个server
可以有多个service
,一般都是一个server
一个service
syntax = "proto3";
package trpc.test.helloworld;
option go_package="git.woa.com/trpcprotocol/test/helloworld";
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string msg = 1;
}
message HelloReply {
string msg = 1;
}
- 通过命令行生成服务模型:
trpc create --protofile=helloworld.proto
(首先需要先安装 trpc 工具)
#!/bin/bash
# 只生成协议代码
trpc create --protofile=qqchatsvr.proto --rpconly
- 可以在
trpc_go.yaml
的 server service 中额外添加 HTTP RPC 服务:
- name: trpc.test.helloworld.Greeter # service 的名字服务路由名称
ip: 127.0.0.1 # 服务监听 ip 地址
port: 8080 # 服务监听端口
network: tcp # 网络监听类型 tcp udp
protocol: http # 应用层协议 trpc http
timeout: 1000 # 请求最长处理时间 单位 毫秒
- 开发具体业务逻辑
- 开发完成,开始编译,根目录执行:
go build
- 执行单元测试:
go test -v
- 启动服务:
./helloworld &
- 自测 trpc 协议:
trpc-cli -func "/trpc.test.helloworld.Greeter/SayHello" -target "ip://127.0.0.1:8000" -body '{"msg":"hello"}' -v
- 自测 http 协议:
curl -X POST -d '{"msg":"hello"}' -H "Content-Type:application/json" http://127.0.0.1:8080/trpc.test.helloworld.Greeter/SayHello
注意:trpc-cli 工具支持很多参数,使用时注意指定。
func 为 pb 协议定义的 /package.service/method,如上面的 helloworld.proto,则为 /trpc.test.helloworld.Greeter/SayHello,千万注意:不是 yaml 里面配置的 service。
target 为被调服务的目标地址,格式为 selectorname://servicename,这里只是本地自测,没有接入名字服务,直接指定 ip:port 寻址,使用 ip selector 就可以了,格式是 ip://${ip}:${port},如 ip://127.0.0.1:8000。
body 为请求包体数据的 json 结构字符串,内部 json 字段要跟 pb 定义的字段完全一致,注意大小写不要写错。
tRPC-Go 拦截器功能及实现 (filter 过滤器)
tRPC-Go 框架的拦截器,也称之为过滤器。tRPC 框架利用拦截器的机制,将接口请求相关的特定逻辑组件化,插件化,从而同具体的业务逻辑解除耦合,达到复用的目的。例如监控拦截器,分布式追踪拦截器,日志拦截器,鉴权拦截器等。
触发时机:拦截器可以拦截到接口的请求和响应,并对请求,响应,上下文进行处理(用通俗的语言阐述也就是,可以在请求接收前做一些事情,请求处理后做一些事情),因此,拦截器从功能上说是分为两个部分:前置(业务逻辑处理前)和 后置(业务逻辑处理后)。
顺序性:拦截器是有明确的顺序性,根据拦截器的注册顺序依次执行前置部分逻辑,并逆序执行拦截器的后置部分。
自定义一个拦截器
定义处理逻辑函数
func ServerFilter() filter.ServerFilter {
return func(ctx context.Context, req interface{}, handler filter.ServerHandleFunc) (rsp interface{}, err error) {
// 前置逻辑
log.Debug("ServerFilter before")
begin := time.Now()
rsp, err = handler(ctx, req)
// 后置逻辑
log.Debug("ServerFilter post")
cost := time.Since(begin) // 业务逻辑处理后计算耗时
log.Debugf("cost: %v", cost)
// 必须返回 next 的 rsp 和 err,要格外注意不要被自己的逻辑的 rsp 和 err 覆盖
return rsp, err
}
}
func ClientFilter() filter.ClientFilter {
return func(ctx context.Context, req, rsp interface{}, handler filter.HandleFunc) (err error) {
// 前置逻辑
log.Debug("ClientFilter before")
begin := time.Now()
err = handler(ctx, req, rsp)
// 后置逻辑
log.Debug("ClientFilter post")
cost := time.Since(begin)
log.Debugf("cost: %v", cost)
return err
}
}
注册到框架中
filter1 := ServerFilter()
filter2 := ClientFilter()
filter.Register("name", filter1, filter2) // 拦截器名字自己随便定义,供后续配置文件使用,必须放在 trpc.NewServer() 之前
配置文件开启使用
server:
filter: # 对所有 service 全部生效
- name1 # 上面第三步注册到框架中的 server 拦截器名字
service:
- name: trpc.app.server.service
filter: # 只对当前 service 生效
- name2
client:
...
filter:
...
- name
完整拦截器代码示例
// Package metirc is a tRPC filter used to report service metrics to monitor
package metric
import (
"context"
"time"
"git.code.oa.com/trpc-go/trpc-go/filter"
"git.code.oa.com/trpc-go/trpc-go/log"
)
func init() {
filter.Register("metric", ServerFilter(), ClientFilter())
}
func ServerFilter() filter.ServerFilter {
return func(ctx context.Context, req interface{}, handler filter.ServerHandleFunc) (rsp interface{}, err error) {
// 前置逻辑
log.Debug("ServerFilter before")
begin := time.Now()
rsp, err = handler(ctx, req)
// 后置逻辑
log.Debug("ServerFilter post")
cost := time.Since(begin) // 业务逻辑处理后计算耗时
log.Debugf("cost: %v", cost)
// 必须返回 next 的 rsp 和 err,要格外注意不要被自己的逻辑的 rsp 和 err 覆盖
return rsp, err
}
}
func ClientFilter() filter.ClientFilter {
return func(ctx context.Context, req, rsp interface{}, handler filter.HandleFunc) (err error) {
// 前置逻辑
log.Debug("ClientFilter before")
begin := time.Now()
err = handler(ctx, req, rsp)
// 后置逻辑
log.Debug("ClientFilter post")
cost := time.Since(begin)
log.Debugf("cost: %v", cost)
return err
}
}