微服务想必大家都不陌生了。刚接触到golang
,那么在golang
中怎么使用微服务呢。这里使用gRRC
框架写了一个简单的例子。
环境要求
- go>=1.13; set GO111MODULE=on
- 安装 protoc
- 安装 Protoc plugin-go
示例代码 iris-grpc-example
项目结构
iris-grpc-example
│ .gitignore
│ go.mod
│ go.sum
│ README.md
│
├─proto
│ README.md
│ servers.pb.go
│ servers.proto
│
└─servers
main.go
services.go
通过目录可以看到这里使用了go mod
,当前golang
版本 1.13
。
proto
使用Protobuf
定义了通信的IDL,可以理解为rpc
中接口定义。
protocol buf
可扩展的序列化数据结构,在通信协议中使用的比较广泛。比json
更快,更小。比xml
更简洁。
servers.proto
syntax = "proto3";
package proto;
message Id {
int32 id=1;
}
message Name {
string name=1;
}
message Age {
int32 age=1;
}
// 用户变量
message User {
int32 id=1;
string name=2;
int32 age=3;
}
// 用户参数
message UserParams{
Name name=1;
Age age=2;
}
// 声明那些方法可以使用rpc
service ServiceSearch{
rpc SaveUser(UserParams) returns (Id){}
rpc UserInfo(Id) returns (User){}
}
简单说明
syntax = "proto3";
声明了proto
语法版本。
package
声明包名
message Id {
int32 id=1;
}
接口中使用的变量声明 变量名称:Id
,类型:int32
,等号后面表示字段编号为 1
service ServiceSearch{
rpc SaveUser(UserParams) returns (Id){}
rpc UserInfo(Id) returns (User){}
}
声明了两个函数 SaveUser(),UserInfo()
是使用RPC
协议,接收的参数与返回参数分别为什么。
servers.go
该文件是使用servers.proto
编译生成的
。在完成servers.proto
之后 在proto
目录下执行
protoc --go_out=plugins=grpc:. *.proto
- --go_out 指定生成go文件,这里会使用到
Protoc plugin-go
- plugins 参数告诉生成RPC代码,并指定了框架为
grpc
编译命令执行完之后,就会生成servers.go
。而我们在go
的模块中实际使用的代码也就是这个文件。
如果有兴趣的同学可以看看里面的代码,主要就是一些参数定义【我们在proto中所定义的
】,还有一个接口的声明。
......
// ServiceSearchServer is the server API for ServiceSearch service.
type ServiceSearchServer interface {
SaveUser(context.Context, *UserParams) (*Id, error)
UserInfo(context.Context, *Id) (*User, error)
}
// UnimplementedServiceSearchServer can be embedded to have forward compatible implementations.
type UnimplementedServiceSearchServer struct {
}
func (*UnimplementedServiceSearchServer) SaveUser(ctx context.Context, req *UserParams) (*Id, error) {
return nil, status.Errorf(codes.Unimplemented, "method SaveUser not implemented")
}
func (*UnimplementedServiceSearchServer) UserInfo(ctx context.Context, req *Id) (*User, error) {
return nil, status.Errorf(codes.Unimplemented, "method UserInfo not implemented")
}
func RegisterServiceSearchServer(s *grpc.Server, srv ServiceSearchServer) {
s.RegisterService(&_ServiceSearch_serviceDesc, srv)
}
......
可以看到最后有一个 RegisterServiceSearchServer
注册服务的方法,接受一个grpc.Server
与一个ServiceSearchServer
的接口。而我们在servers/services.go
中,主要就是实现ServiceSearchServer
这个接口,并通过RegisterServiceSearchServer
将接口实现的函数注册rpc
服务中。
servers
rpc 接口的实现与调用
services.go 接口的实现
package main
import (
"context"
"fmt"
"google.golang.org/grpc"
"iris-grpc-example/proto"
"log"
"math/rand"
"net"
)
type ServiceSearch struct{}
func main() {
listen, err := net.Listen("tcp", "127.0.0.1:9527")
if err != nil {
log.Fatalf("tcp listen failed:%v", err)
}
server := grpc.NewServer()
fmt.Println("services start success")
proto.RegisterServiceSearchServer(server, &ServiceSearch{})
server.Serve(listen)
}
//保存用户
func (Service *ServiceSearch) SaveUser(ctx context.Context, params *proto.UserParams) (*proto.Id, error) {
id := rand.Int31n(10) //随机生成id 模式保存成功
res := &proto.Id{Id: id}
fmt.Printf("username:%s,age:%d\r\n", params.Name, params.Age)
return res, nil
}
func (Service *ServiceSearch) UserInfo(ctx context.Context, id *proto.Id) (*proto.User, error) {
res := &proto.User{Id:id.GetId(),Name:"test",Age:31}
return res, nil
}
- 实现
ServiceSearchServer
接口,在代码中声明了一个ServiceSearch
来实现了ServiceSearchServer
接口。 -
SaveUser
,实现了proto
中定义的SaveUser
方法,需要注意的是这里需要返回两个参,数第一个是我们预先定好的参数,第二个为定义的错误信息。 -
main
函数声明当前服务的ip以及端口,并创建了一个grpc server
然后通过proto.RegisterServiceSearchServer(server, &ServiceSearch{})
将ServiceSearch
注册到grpc
中
main.go 接口的调用
package main
import (
"context"
"github.com/kataras/iris/v12"
"google.golang.org/grpc"
"iris-grpc-example/proto"
"log"
)
var client proto.ServiceSearchClient
func main() {
app := iris.New()
app.Logger().SetLevel("debug") //debug
app.Handle("GET", "/testSaveUser", saveUser)
app.Handle("GET", "/testUserInfo", userInfo)
app.Run(iris.Addr("127.0.0.1:8080"))
}
func saveUser(ctx iris.Context) {
params := proto.UserParams{}
params.Age = &proto.Age{Age: 31}
params.Name = &proto.Name{Name: "test"}
res, err := client.SaveUser(context.Background(), ¶ms)
if err != nil {
log.Fatalf("client.SaveUser err: %v", err)
}
ctx.JSON(res)
}
func userInfo(ctx iris.Context) {
res, err := client.UserInfo(context.Background(), &proto.Id{Id: 1})
if err != nil {
log.Fatalf("client.userInfo err: %v", err)
}
ctx.JSON(res)
}
func init() {
connect, err := grpc.Dial("127.0.0.1:9527", grpc.WithInsecure())
if err != nil {
log.Fatalln(err)
}
client = proto.NewServiceSearchClient(connect)
}
这里使用了iris
作为了一个client
。与传统http
的区别主要是在
func init() {
connect, err := grpc.Dial("127.0.0.1:9527", grpc.WithInsecure())
if err != nil {
log.Fatalln(err)
}
client = proto.NewServiceSearchClient(connect)
}
这里创建了一个rpc
的client
。在使用的时候我们只需要调用services
里面已经写好的函数即可。
测试
\iris-grpc-example>cd servers
开启服务:go run services.go
开启Client:go run main.go
浏览器访问
http://127.0.0.1:8080/testSaveUser
{
"id": 1
}
http://127.0.0.1:8080/testUserInfo
{
"id": 1,
"name": "test",
"age": 31
}
期待一起交流