原生的客户端通过etcd链接,要自己写很多代码,不想写的话自己找类库,go-zero中已经帮我们封装好了,仅仅需要加配置即可
本文演示的客户端,依赖上篇文章的服务端环境
新建一个go-zero api项目,当然,rpc项目也是可以的
使用goctl工具新建项目 clijj
goctl api new clijj
cd clijj
go mod tidy
目录结构
├── clijj.api
├── clijj.go
├── etc
│ └── clijj-api.yaml
├── go.mod
├── go.sum
├── internal
│ ├── config
│ │ └── config.go
│ ├── handler
│ │ ├── clijjhandler.go
│ │ └── routes.go
│ ├── logic
│ │ └── clijjlogic.go
│ ├── svc
│ │ └── servicecontext.go
│ └── types
│ └── types.go
├── proto // 这个文件是我自己新建的,并把服务端(上篇文章)的user.proto文件拷贝进来
│ └── user.proto
└── rpc // 这个文件也是我自己新建的,存放我这个项目中,rpc相关的源码
配置添加
etc/clijj.yaml文件添加etcd的地址
Name: clijj-api
Host: 0.0.0.0
Port: 8888
UserRpc: // UserRpc 与下面的config.go加载类对应
Etcd:
Hosts:
- 127.0.0.1:2379
Key: user.rpc
Etcd目前来说只作为服务注册发现用,如要用来做kv数据库使用,自己找类库包去
internal/config/config.go 配置加载文件修改
type Config struct {
rest.RestConf
UserRpc zrpc.RpcClientConf // 新增RPC配置读取
}
通过.proto文件生成客户端/服务端代码
goctl rpc protoc proto/user.proto --go_out=./rpc --go-grpc_out=./rpc --zrpc_out=. --style=goZero
此时,rpc文件夹中,protoc生成的原生代码;根目录下新增了userclient
文件夹,存放了go-zero封装好的gRPC客户端调用方法
目录结构
├── clijj.api
├── clijj.go
├── etc
│ ├── clijj-api.yaml // 上面的goctl命令连rpc服务端代码都给生成了,本项目只做RPC客户端,删了此文件即可
│ └── user.yaml
├── go.mod
├── go.sum
├── internal
│ ├── config
│ │ └── config.go
│ ├── handler
│ │ ├── clijjhandler.go
│ │ └── routes.go
│ ├── logic
│ │ ├── clijjlogic.go
│ │ ├── getUserLogic.go
│ │ ├── getUserNameLogic.go
│ │ └── getUserOauthLogic.go
│ ├── server
│ │ └── userServer.go
│ ├── svc
│ │ └── servicecontext.go
│ └── types
│ └── types.go
├── proto
│ └── user.proto
├── rpc
│ └── user
│ ├── user.pb.go
│ └── user_grpc.pb.go
├── user.go // 上面的goctl命令连rpc服务端代码都给生成了,本项目只做RPC客户端,删了此文件即可
└── userclient // goctl生成的文件夹,存放了gRPC客户端
└── user.go
将生成的go-zero gRPC客户端代码放入项目初始化中
internal/svc/servicecontext.go
type ServiceContext struct {
Config config.Config
UserRpc userclient.User
}
func NewServiceContext(c config.Config) *ServiceContext {
return &ServiceContext{
Config: c,
UserRpc: userclient.NewUser(zrpc.MustNewClient(c.UserRpc)), // 将配置类中rpc配置加载,生成rpc客户端链接具柄,go-zero已经封装好了etcd链接和rpc寻址功能,无需过多操作
}
}
客户端逻辑编码
go-zero的用法不再做过多说明
- 新建一个api接口
clijj.api
type UserId {
UserId int32 `json:"user_id" rules:"required"`
}
type UserInfo {
UserId int32 `json:"user_id"`
Name string `json:"name"`
Gender int32 `json:"gender"`
}
service clijj {
@handler TestRpc
get /test/rpc(UserId) returns(UserInfo)
}
生成代码
goctl api go -api clijj.api -dir . -style GoZero
删除掉项目中没用的代码,例如RPC服务端方面的,目录结构
├── clijj.api
├── clijj.go
├── etc
│ ├── clijj.yaml
├── go.mod
├── go.sum
├── internal
│ ├── config
│ │ └── config.go
│ ├── handler
│ │ ├── Routes.go
│ │ └── TestRpcHandler.go
│ ├── logic
│ │ └── TestRpcLogic.go
│ ├── svc
│ │ └── servicecontext.go
│ └── types
│ └── Types.go
├── proto
│ └── user.proto
├── rpc
│ └── user
│ ├── user.pb.go
│ └── user_grpc.pb.go
└── userclient
└── user.go
- api逻辑编写
internal/logic/TestRpcLogic.go
func (l *TestRpcLogic) TestRpc(req *types.UserId) (resp *types.UserInfo, err error) {
userInfo, _ := l.svcCtx.UserRpc.GetUser(l.ctx, &user.IdRequest{
Id: req.UserId,
})
return &types.UserInfo{
UserId: userInfo.Id,
Name: userInfo.Name,
Gender: userInfo.Gender,
}, nil
}
- main包文件clijj.go内容参考如下
var configFile = flag.String("f", "etc/clijj.yaml", "the config file")
func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
server := rest.MustNewServer(c.RestConf)
defer server.Stop()
ctx := svc.NewServiceContext(c)
handler.RegisterHandlers(server, ctx)
fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
server.Start()
}
启动api项目
go run clijj.go
请求api地址
127.0.0.1:8888/test/rpc
image.png
本项目从未直接配置过RPC的服务地址,仅配置过etcd的地址,因此此调用服务,是通过etcd服务发现来寻找的RPC服务地址
多次请求会发现,返回值中
name
字段会变化
"name": "来自rpc服务器0.0.0.0:8081返回的名字",
"name": "来自rpc服务器0.0.0.0:8082返回的名字",
这是因为etcd服务会择优选择已注册的服务地址
此刻,我们将RPC服务端,8081的服务强行关掉,etcd此时的键值对为
key | value |
---|---|
user.rpc/7587872091812852496 | 10.0.89.75:8082 |
此时再怎么请求127.0.0.1:8888/test/rpc
,返回的name
字段永远都是通过8082节点返回的值了
"name": "来自rpc服务器0.0.0.0:8082返回的名字",