grpc优点不再多叙述,但如何对外提供Restful接口的,而又不希望重复开发相同的功能可以使用grpc-gateway将grpc转换为对外的Restful API。
安装
go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway
go get -u github.com/micro/protobuf/{proto,protoc-gen-go}
如果只是使用,其实处理起来很简单在原有grpc的.proto文件中添加相应的import文件和http配置声明即可。
import文件
import "google/api/annotations.proto";
http配置声明
service Bookinfo {
// get book information
rpc Getall (GetallRequest) returns (GetallResponse) {
option (google.api.http) = {
post: "/bookinfo/getall"
body: "*"
};
};
}
大概说明
bookinfo.proto文件
syntax = "proto3";
package bookinfo;
import "google/api/annotations.proto";
option go_package = "bookinfopb";
// The book service makes it possible to get or set book's detail information
service Bookinfo {
// get book information
rpc Getall (GetallRequest) returns (GetallResponse) {
option (google.api.http) = {
post: "/bookinfo/getall"
body: "*"
};
};
// add books information
rpc Add (AddRequest) returns (AddResponse){
option (google.api.http) = {
post: "/bookinfo/add"
body: "*"
};
};
}
message GetallRequest {
// ID of user
string userid = 1;
}
message GetallResponse {
// Book info list
repeated BookInfo bookinfolist = 1;
}
message BookInfo {
// Name of book
string book_name = 1;
// Author
string author = 2;
// Chapters name
repeated ChapterInfo chapters_info = 3;
}
message ChapterInfo {
// Chapter number
sint32 chapter_num = 1;
// Chapter name
string chapter_name = 2;
// Words cound
sint32 words_count = 3;
}
message AddRequest {
// Books information
repeated BookInfo books_info = 1;
}
message AddResponse {
repeated sint32 field = 1;
}
定义请求接口名,参数和返回结果
Getall接口的参数是string类型的userid。返回结果是书籍详情(BookInfo)的数组(通过repeated标识)
生成grpc服务的stub文件
protoc -I/usr/local/include -I. \
-I$(GOPATH)/src \
-I$(GOPATH)/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
--go_out=plugins=grpc:. bookinfo-srv/proto/bookinfo/bookinfo.proto
生成bookinfo.pb.go文件
生成grpc-gateway的stub文件
protoc -I/usr/local/include -I. \
-I $(GOPATH)/src \
-I $(GOPATH)/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
--grpc-gateway_out=logtostderr=true:. bookinfo-srv/proto/bookinfo/bookinfo.proto
生成bookinfo.pb.gw.go文件
grpc功能的实现
- service.go
package main
import (
pb "grpcT1/bookinfo-srv/proto/bookinfo"
bkinfoprocess "grpcT1/bookinfo-srv/process"
"net"
"log"
"google.golang.org/grpc"
)
const (
PORT = ":50051"
)
func main() {
listener, err := net.Listen("tcp", PORT)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
log.Printf("listen on: %s\n", PORT)
server := grpc.NewServer()
pb.RegisterBookinfoServer(server, bkinfoprocess.NewBookinfo())
if err := server.Serve(listener); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
其中RegisterBookinfoServer方法是在bookinfo.pb.go中定义和实现的
- process/bookinfo.go 定义的接口方法的具体逻辑实现
package bookinfoprocess
import (
bookinfo "grpcT1/bookinfo-srv/proto/bookinfo"
"context"
"fmt"
)
type bookinfosrvc struct {}
func NewBookinfo() bookinfo.BookinfoServer {
return &bookinfosrvc{}
}
func (s *bookinfosrvc) Getall(ctx context.Context, p *bookinfo.GetallRequest) (*bookinfo.GetallResponse, error) {
res := &bookinfo.GetallResponse{}
fmt.Println("bookinfo.getall")
cpsInfo := make([]*bookinfo.ChapterInfo,4)
cpsInfo[0] = &bookinfo.ChapterInfo{ChapterNum:1, ChapterName:"序言", WordsCount:3259}
cpsInfo[1] = &bookinfo.ChapterInfo{ChapterNum:2, ChapterName:"布尔", WordsCount:4559}
cpsInfo[2] = &bookinfo.ChapterInfo{ChapterNum:3, ChapterName:"字符", WordsCount:7559}
cpsInfo[3] = &bookinfo.ChapterInfo{ChapterNum:4, ChapterName:"函数", WordsCount:7859}
bkInfo := &bookinfo.BookInfo{BookName:"Ugly language", Author:"Bill",
ChaptersInfo:cpsInfo}
res.Bookinfolist = append(res.Bookinfolist, bkInfo)
return res, nil
}
func (s *bookinfosrvc) Add(ctx context.Context, p *bookinfo.AddRequest) (*bookinfo.AddResponse, error) {
return nil, nil
}
gateway方式对外提供http接口
- gateway/main.go
package main
import (
"flag"
"net/http"
"github.com/golang/glog"
"golang.org/x/net/context"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"google.golang.org/grpc"
gw "grpcT1/bookinfo-srv/proto/bookinfo"
)
var (
echoEndpoint = flag.String("getall_endpoint", "rpcserver:50051", "endpoint of BookInfoService")
)
func run() error {
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
defer cancel()
mux := runtime.NewServeMux()
opts := []grpc.DialOption{grpc.WithInsecure()}
err := gw.RegisterBookinfoHandlerFromEndpoint(ctx, mux, *echoEndpoint, opts)
if err != nil {
return err
}
return http.ListenAndServe(":8080", mux)
}
func main() {
flag.Parse()
defer glog.Flush()
if err := run(); err != nil {
glog.Fatal(err)
}
}
其中RegisterBookinfoHandlerFromEndpoint方法在bookinfo.pb.gw.go中定义和实现
grpc请求
- client/main.go
package main
import (
pb "grpcT1/bookinfo-srv/proto/bookinfo"
"google.golang.org/grpc"
"log"
"context"
)
const (
ADDRESS = "rpcserver:50051"
)
func main() {
// 连接到 gRPC 服务器
conn, err := grpc.Dial(ADDRESS, grpc.WithInsecure())
if err != nil {
log.Fatalf("connect error: %v", err)
}
defer conn.Close()
// 初始化 gRPC 客户端
client := pb.NewBookinfoClient(conn)
resp, err := client.Getall(context.Background(), &pb.GetallRequest{Userid:"tt"})
if err != nil {
log.Fatalf("Getall error: %v", err)
}
length := len(resp.Bookinfolist)
for i:=0;i<length;i++ {
log.Printf("bookName:%s\n",resp.Bookinfolist[i].BookName)
log.Printf("author:%s\n", resp.Bookinfolist[i].Author)
chaptersCount := len(resp.Bookinfolist[i].ChaptersInfo)
for j:=0;j<chaptersCount;j++ {
log.Printf("---Chapter.Num:%d\n", resp.Bookinfolist[i].ChaptersInfo[j].ChapterNum)
log.Printf("---Chapter.Name:%s\n", resp.Bookinfolist[i].ChaptersInfo[j].ChapterName)
log.Printf("---Chapter.WordsCount:%d\n", resp.Bookinfolist[i].ChaptersInfo[j].WordsCount)
log.Println("")
}
}
}
client调用grpc接口的实现。NewBookinfoClient方法在bookinfo.pb.go中定义和实现
http请求测试
curl -X POST -k http://localhost:8080/bookinfo/getall -d '{"userid":"tt"}'
详细代码可以从这取得 https://github.com/BinWang-sh...
根目录下有makefile可生成stub和docker镜像
Docker目录下有docker-compose文件执行docker-compose up可以直接run
有疑问加站长微信联系(非本文作者)