不在gopath目录下能使用godoc吗_使用grpc-gateway反向代理http请求到grpc服务(ubuntu 18.04)...

如果不是因为grpc-gateway是由Go语言编写的,大家不会搜索到这里,因为grpc服务并不支持http协议。对于初次使用grpc gateway方向代理的人来说,参考github给出的步骤是难以测试和理解grpc-gateway是如何将grpc服务映射成RESTFul API的。话不多少,首先介绍准备过程,然后介绍方向代理基本原理,以及在这个过程中遇到的问题和解决办法。

  1. 安装和配置最新版本的Go

首先在GO官网下载最新版的binary, 然后配置环境变量(以安装到/usr/local为例)

export PATH=$PATH:/usr/local/go/bin

export GOROOT=/usr/local/go

export GOPATH=$HOME/go

export PATH=$PATH:$GOROOT/bin

export PATH=$PATH:$GOPATH/bin

这里有两个环境变量容易混淆,一个是GOROOT,一个是GOPATH,前面一个是指向go安装跟目录的,后一个用于制定包管理器从何处查找模块的,在linux环境中与当前用户的目录关联。

2. grpc-gateway 依赖于GOPATH 环境中指向的模块位置,查找以下必须的模块:

0265e658425fd0f5f7ee39f661b1b3db.png
GOPATH目录下面包含三个文件夹(一般在通过 go get -u -d 安装模块的时候会自动添加前面两个,如果没有src目录,记得创建一个)

go通过自带命令下载模块到对应的目录,第一个模块是google.golang.org/grpc

使用go get -u - google.golang.org/grpc 会报告以下错误:

package google.golang.org/grpc: unrecognized import path "google.golang.org/grpc"(https fetch: Get https://google.golang.org/grpc?go-get=1: dial tcp 216.239.37.1:443: i/o timeout)

主要的问题是被强隔离,需要使用以下方式进行处理:

git clone https://github.com/grpc/grpc-go.git $GOPATH/src/http://google.golang.org/grpc

其它需要库也做这样的处理

git clone https://github.com/golang/net.git $GOPATH/src/http://golang.org/x/net

git clone https://github.com/golang/text.git $GOPATH/src/http://golang.org/x/text

第二个比较重要的模块是google.golang.org/protobuf/

git clone https://github.com/protocolbuffers/protobuf-go.git $GOPATH/src/google.golang.org/protobuf/

第三个比较重要的模块是http://github.com/grpc-ecosystem/grpc-gateway

git clone grpc-ecosystem/grpc-gateway $GOPATH/src/github.com/grpc-ecosystem

其它可以在国内自由访问的使用以下命令

go get -u -d github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway

go get -u -d github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger

go get -u -d github.com/golang/protobuf/protoc-gen-go

3. grpc-gateway代理的原理与实践例子

d30f8f0e6316623d303784bac2a1eef2.png

github上的原图,我的理解是grpc-gateway的代理注册http服务端口,并将请求按照protobuf翻译和转发成gprc服务所依赖的形式,这样grpc服务就可以使用不同语言编写功能了。profile-service.proto 定义了grpc服务接受的消息的类型传递的格式和类型。 可能理解不是准确,但是现在这么认为也没大问题。

先申明下,不是有意剽窃别人的代码,这里只是讲解如何验证上面的过程。(具体参考:https://qiita.com/ryu3/items/b2882d4f45c7f8485030)

首先在本地机器上创建一个proto的目录(还是以/usr/local为例)

创建一个service.proto

syntax = "proto3";
package example;

import "google/api/annotations.proto";

service HelloWorldService {
  rpc SayHello (HelloRequest) returns (HelloReply) {
    option (google.api.http) = {
      get: "/v1/example/sayhello/{name}"
    };
  }

  rpc GetUser(GetUserRequest) returns (User) {
    option (google.api.http) = {
        get: "/v1/example/users/{id}"
    };
  }

  rpc CreateUser(CreateUserRequest) returns (User) {
      option (google.api.http) = {
          post: "/v1/example/users"
          body: "*"
      };
  }
}

message HelloRequest {protoc   --proto_path=.:$GOPATH/src  -I. -Igrpc/third_party/googleapis/google/api --go_out=plugins=grpc:. proto/service.proto

  string name = 1;
}

message HelloReply {
  string message = 1;
}

message GetUserRequest {
  string id = 1;
}

message CreateUserRequest {
  string name = 1;
}

message User {
  string id = 1;
  string name = 2;
}

定义了三个rpc方法,第一个SayHello方法接受一个名字请求,返回一个HelloReply消息。

第二个GetUser方法接受一个参数为id的GetUserRequest 请求,返回一个User用户信息

第三个CreateUser方法根据用户创建请求创建用户,并返回一个User用户消息

由于在grpc代理和grpc服务中都需要使用改消息进行通信,我们先为grpc服务生成stub代码

我的理解是grpc服务仍然接受rpc protobuf传递的消息,然后进行逻辑处理(稍后在介绍)。

在/usr/local目录下执行下面的命令:(如果protoc没安装的话,先去安装)

protoc --proto_path=.:$GOPATH/src -I. -Igrpc/third_party/googleapis/google/api --go_out=plugins=grpc:. proto/service.proto

这里额proto_path告诉protoc在何处寻找依赖包,第二个-I.表示输入文件的当前目录,第三个参数不确定是干什么的,最后一个go_out指定根据语言生成grpc类文件,最后指定要转换的proto文件,得到一个service.pb.go文件

然后,在/usr/local目录下执行下面的命令:

protoc --proto_path=.:$GOPATH/src -I. -Igrpc/third_party/googleapis/google/api --grpc-gateway_out=logtostderr=true:. proto/service.proto

得到一个service.pb.gw.go文件

然后在创建一个在greeter_gateway/main.go中的go应用,具体代码是:

package main
import (
"flag"
"fmt"
"net/http"
"http://github.com/golang/glog"
"http://golang.org/x/net/context"
"http://github.com/grpc-ecosystem/grpc-gateway/runtime"
"http://google.golang.org/grpc"gw "../proto"
)
func run() error {
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
defer cancel()
mux := runtime.NewServeMux()
opts := []grpc.DialOption{grpc.WithInsecure()}
endpoint := fmt.Sprintf("localhost:5001")
err := gw.RegisterHelloWorldServiceHandlerFromEndpoint(ctx, mux, endpoint, opts)
if err != nil {
return err
}
return http.ListenAndServe(":5000", mux)
}
func main() {
flag.Parse()
defer glog.Flush()
if err := run(); err != nil {
glog.Fatal(err)
}
}

看标有黑色显示的代码,我们知道gateway使用上一步生成的go类,在本地机器的5000号端口监听http服务请求,并将该请求的消息转发给grpc服务。

最后,看一下grpc服务,在/usr/local/下创建一个greeter_server/main.go程序

package main

import (
    "context"
    "log"
    "net"

    "google.golang.org/grpc"
    pb "../proto"
)

const (
    port = ":5001"
)

// server is used to implement HelloWorldServer.
type server struct{}

// SayHello implements HelloWorldServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
    log.Printf("Received: %v", in.Name)
    return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}

// GetUser
func (s *server) GetUser(ctx context.Context, in *pb.GetUserRequest) (*pb.User, error) {
    log.Printf("Received: %v", in.Id)
    return &pb.User{
        Id: in.Id,
        Name: "SampleUser"}, nil
}

// CreateUser
func (s *server) CreateUser(ctx context.Context, in *pb.CreateUserRequest) (*pb.User, error) {
    log.Printf("Received: %v", in.Name)
    return &pb.User{
        Id: "123",
        Name: in.Name}, nil
}

func main() {
    lis, err := net.Listen("tcp", port)
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }
    s := grpc.NewServer()
    pb.RegisterHelloWorldServiceServer(s, &server{})
    if err := s.Serve(lis); err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
}

它实现了之前proto文件中定义的三个方法的处理逻辑,在内部使用的tcp 5001号端口。

在两个tty终端中运行

go run greeter_gateway/main.go

go run greeter_server/main.go

在另一个tty中进行http请求测试:

d9df6c9ef430c5191ea809389bbe832d.png

三个http请求对应的结果

curl -X GET http://localhost:5000/v1/example/sayhello/nakata
{"message":"Hello nakata"}

curl -X GET http://localhost:5000/v1/example/users/10
{"id":"10","name":"SampleUser"}

curl -X POST http://localhost:5000/v1/example/users -d '{"name":"nakata"}'
{"id":"123","name":"nakata"}

最后看一下我们的greeter_server成功接收到http传过来的消息

603e1a3fc64eb48389f2eac6c980f826.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值