首先需要下载proto包:
go get -u github.com/golang/protobuf/proto
一、安装
go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway
go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger
go get -u github.com/golang/protobuf/protoc-gen-go
分别安装gateway、swagger、还有就是protoc中go语言的编译器。
二、编写.proto文件
syntax = "proto3"; // proto版本
package demo; // 指定包名,默认go中包名也是这个
option go_package = ".;demo"; // 指定go包名
// 定义Demo服务
service Demo {
// 定义Confession方法
rpc TestSay(SayRequest) returns (SayResponse) {
}
}
// 请求
message SayRequest {
string name = 1;
}
// 响应
message SayResponse {
string result = 1;
}
这个是最简单的也就仅仅使用的grpc基本的功能,如果要用grpc gateway的话,在这个基础上面改一下
syntax = "proto3"; // proto版本
package demo; // 指定包名,默认go中包名也是这个
import "google/api/annotations.proto";
option go_package = ".;demo"; // 指定go包名
// 定义Demo服务
service Demo {
// 定义Get方法
//其实重点就是定义这个玩意,可以定义get,post,put,delete等method,主要是用于外部调用
rpc GetTestSay (SayRequest) returns (SayResponse){
option (google.api.http) ={
get: "/v1/demo/{name}"
};
}
//定义Post方法
rpc PostTestSay (SayRequest) returns (SayResponse){
option (google.api.http) ={
post: "/v1/demo"
body: "*"
};
}
}
// 请求
message SayRequest {
string name = 1;
}
// 响应
message SayResponse {
string result = 1;
}
三、生成.pb文件
首先生成demo.pb.go文件。
protoc --go_out=plugins=grpc:. demo.proto
然后给我报出来这么一个玩意
2020/12/21 16:18:53 WARNING: Deprecated use of 'go_package' option without a full import path in "google/protobuf/descriptor.proto", please specify:
option go_package = "google/protobuf;types";
A future release of protoc-gen-go will require the import path be specified.
See https://developers.google.com/protocol-buffers/docs/reference/go-generated#package for more information.
大体的意思就是说之后要使用的时候需要指定路径,直接忽略,后续升级版本的时候再说。原谅我的不细致。。。
然后在你的demo.proto所在的目录下就会生成一个demo.pb.go文件。
之后咱们开始生成demo.pb.gw.go文件
protoc --grpc-gateway_out=logtostderr=true:. demo.proto
也是在demo.proto这个文件所在的目录下执行上面的这个命令行
然后在这个文件夹下面会有一个demo.pb.gw.go文件出现。
四、编写代码
上面已经把准备的工作做好了,那之后我们就开始写服务端和客户端的代码
服务端分为两个部分:
grpc服务端代码:
type server struct {
}
func (s *server) GetTestSay(ctx context.Context,res *demo.SayRequest) (resp *demo.SayResponse,err error) {
return &demo.SayResponse{
Result: "hello " + res.Name}, nil
}
func (s *server) PostTestSay(ctx context.Context,res *demo.SayRequest) (*demo.SayResponse, error) {
return &demo.SayResponse{
Result: "hello " + res.Name}, nil
}
func main() {
// 监听本地端口
lis, err := net.Listen("tcp", "127.0.0.1:9100")
if err != nil {
fmt.Printf("监听端口失败: %s", err)
return
}
// 创建gRPC服务器
s := grpc.NewServer()
// 注册服务
demo.RegisterDemoServer(s, &server{
})
reflection.Register(s)
err = s.Serve(lis)
if err != nil {
fmt.Printf("开启服务失败: %s", err)
return
}
}
http服务端代码:
func main() {
//1.定义一个context
ctx:=context.Background()
ctx,cancel:=context.WithCancel(ctx)
defer cancel()
//grpc服务地址
endpoint := "127.0.0.1:9100"
mux := runtime.NewServeMux()
opts := []grpc.DialOption{
grpc.WithInsecure()}
//Http转grpc
err:= demo.RegisterDemoHandlerFromEndpoint(ctx,mux,endpoint,opts)
if err != nil {
grpclog.Fatalf("Register handler err:%vn", err)
}
grpclog.Println("HTTP Listen on 9000")
http.ListenAndServe("127.0.0.1:9000", mux)
}
由上面代码可以看出来,其实http请求也是将http的方式转化为grpc的方式,只不过grpc已经为我们提供了更加方便的http的调用方式。
客户端代码:
func main(){
// 连接服务器
conn, err := grpc.Dial("127.0.0.1:9100", grpc.WithInsecure())
if err != nil {
fmt.Printf("连接服务端失败: %s", err)
return
}
defer conn.Close()
// 新建一个客户端
c := demo.NewDemoClient(conn)
// 调用服务端函数
r, err := c.GetTestSay(context.Background(), &demo.SayRequest{
Name: "Grpc"})
if err != nil {
fmt.Printf("调用服务端代码失败: %s", err)
return
}
fmt.Printf("调用成功: %s", r.Result)
}
其实这个客户端直接走的是grpc,而http调用的方式就需要我们使用postman来调用了。http调用的监听端口为9000,host是:127.0.0.1,然后我们回到demo.proto这个文件,在最开始定义的时候我们在方法里面添加了不同请求方式对应的url,直接拼接就好了。
具体的调用url为:127.0.0.1:9000/v1/demo/{Http}
项目的代码结构为:
补充一点:
其实可以将http的方式和grpc的方式放在一起:
var(
endpoint = "127.0.0.1:9000"
)
type server struct {
}
func (s *server) GetTestSay(ctx context.Context,res *demo.SayRequest) (resp *demo.SayResponse,err error) {
return &demo.SayResponse{
Result: "hello " + res.Name}, nil
}
func (s *server) PostTestSay(ctx context.Context,res *demo.SayRequest) (*demo.SayResponse, error) {
return &demo.SayResponse{
Result: "hello " + res.Name}, nil
}
func run() error {
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
defer cancel()
mux := runtime.NewServeMux()
opts := []grpc.DialOption{
grpc.WithInsecure()}
err := demo.RegisterDemoHandlerFromEndpoint(ctx, mux, endpoint, opts)
if err != nil {
return err
}
return http.ListenAndServe("127.0.0.1:9100", mux)
}
func main() {
lis, err := net.Listen("tcp", endpoint)
if err != nil {
log.Fatal("failed to listen: %v", err)
}
s := grpc.NewServer()
demo.RegisterDemoServer(s, &server{
})
go s.Serve(lis)
defer glog.Flush()
if err := run(); err != nil {
glog.Fatal(err)
}
}
其中grpc的端口是9000,http端口为9100。这样无论是grpc还是http都会走这块的。
既然说到grpc了,那咱们在下一节就再说一下路由以及swagger