文章目录
前言
由于rpc通信效率相比http效率高。大部分公司使用go开发的服务内部之间调用使用的rpc进行调用,但是暴露给外面前端调用的话,最好使用http协议。这里我们可以开发个类似中间代理的服务(grpc gateway),负责将http协议转换成后台使用的rpc协议,负责将后台rpc协议产生的数据转换成前端容易识别的http协议内容
最终项目目录结构
一、搭建gRPC-Gateway环境
1、我使用的Intellij idea,集成go安装环境
安装go插件,使idea支持go开发
2、idea配置go开发环境
2.1配置GOROOT
2.2配置GOPATH,默认使用全局的GOPATH
为了使用全局的GOPATH,我们还需要配置系统环境变量
cd ~
vim .zshrc
go相关的环境变量内容
.zshrc
export CLICOLOR=1
export LSCOLORS=ExGxFxdaCxDaDahbadeche
export GO_HOME=/Users/chenchanghui/sdk/go1.16.7
export GOPATH_HOME=/Users/chenchanghui/go
export PATH=$GO_HOME/bin:$GOPATH_HOME/bin:$PATH
//系统变量生效
source .zshrc
//检查
go version
echo $GOPATH_HOME
3安装gRPC-Gateway环境
执行如下shell脚本命令
install-grpc-gateway.sh
#!/bin/bash
echo "1. downloading grpc-gateway release package v1.16.0"
rm -rf $GOPATH/src/github.com/grpc-ecosystem/grpc-gateway
mkdir -p $GOPATH/src/github.com/grpc-ecosystem
cd $GOPATH/src/github.com/grpc-ecosystem
wget https://github.com/grpc-ecosystem/grpc-gateway/archive/v1.16.0.zip -O grpc-gateway.zip
unzip grpc-gateway.zip
rm -f grpc-gateway.zip
mv grpc-gateway-1.16.0 grpc-gateway
echo "2. building grpc gateway"
cd $GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway
go build
go install
echo "3. building swagger"
cd $GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger
go build
go install
echo "4. downloading annonation files"
cd ~
wget https://github.com/protocolbuffers/protobuf/archive/v3.14.0.zip -O annonations.zip
unzip annonations.zip
rm -f annonations.zip
cp -r protobuf-3.14.0/src/google/ $GOPATH/src/
rm -rf protobuf-3.14.0
echo "finish"
执行安装
chmod +x install-grpc-gateway.sh
./install-grpc-gateway.sh
检查,GOPATH/src目录下有如下两个目录,证明安装成功
二、生成源码
1.编写proto文件
helloworld.proto
// 协议类型
syntax = "proto3";
// 包名
package helloworld;
option go_package = "cch.com/hello";
import "google/api/annotations.proto";
// 定义的服务名
service Greeter {
// 具体的远程服务方法
rpc SayHello (HelloRequest) returns (HelloReply) {
option (google.api.http) = {
post: "/helloworld"
body: "*"
};
}
}
// SayHello方法的入参,只有一个字符串字段
message HelloRequest {
string name = 1;
}
// SayHello方法的返回值,只有一个字符串字段
message HelloReply {
string message = 1;
}
2、生成grpc源码
在helloworld.proto同级目录下执行如下命令,不然会报错找不到对应文件
protoc -I. \
-I$GOPATH/src \
-I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
--go_out=plugins=grpc:. \
helloworld.proto
3、生成gRPC-Gateway源码
protoc -I. \
-I$GOPATH/src \
-I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
--grpc-gateway_out=logtostderr=true:. \
helloworld.proto
三、服务端代码开发
server.go
package main
import (
"context"
"google.golang.org/grpc"
pb "grpc-gateway-demo/proto/cch.com/hello"
"log"
"net"
)
const (
port = ":50051"
)
// 定义结构体,在调用注册api的时候作为入参,
// 该结构体会带上SayHello方法,里面是业务代码
// 这样远程调用时就执行了业务代码了
type server struct {
// pb.go中自动生成的,是个空结构体
pb.UnimplementedGreeterServer
}
// 业务代码在此写,客户端远程调用SayHello时,
// 会执行这里的代码
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
// 打印请求参数
log.Printf("Received: %v", in.GetName())
// 实例化结构体HelloReply,作为返回值
return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
}
func main() {
// 要监听的协议和端口
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
// 实例化gRPC server结构体
s := grpc.NewServer()
// 服务注册
pb.RegisterGreeterServer(s, &server{})
log.Println("开始监听,等待远程调用...")
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
gateway.go
package main
import (
"flag"
"fmt"
gw "grpc-gateway-demo/proto/cch.com/hello"
"net/http"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"golang.org/x/net/context"
"google.golang.org/grpc"
)
var (
echoEndpoint = flag.String("echo_endpoint", "localhost:50051", "endpoint of YourService")
)
func run() error {
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
defer cancel()
mux := runtime.NewServeMux()
opts := []grpc.DialOption{grpc.WithInsecure()}
err := gw.RegisterGreeterHandlerFromEndpoint(ctx, mux, *echoEndpoint, opts)
if err != nil {
return err
}
return http.ListenAndServe(":9090", mux)
}
func main() {
if err := run(); err != nil {
fmt.Print(err.Error())
}
}
四、测试
分别启动service.go和gateway.go
cd server
go run server.go
cd gateway
go run gateway.go
测试接口成功
curl \
-X POST \
-d '{"name": "will"}' \
localhost:9090/helloworld