grpc-gateway:grpc轉換爲http協議對外提供服務

我所在公司的項目是採用基於Restful的微服務架構,隨着微服務之間的溝通愈來愈頻繁,就但願能夠作成用rpc來作內部的通信,對外依然用Restful。因而就想到了google的grpc。前端

使用grpc的優勢不少,二進制的數據能夠加快傳輸速度,基於http2的多路複用能夠減小服務之間的鏈接次數,和函數同樣的調用方式也有效的提高了開發效率。java

不過使用grpc也會面臨一個問題,咱們的微服務對外必定是要提供Restful接口的,若是內部調用使用grpc,在某些狀況下要同時提供一個功能的兩套API接口,這樣就不只下降了開發效率,也增長了調試的複雜度。因而就想着有沒有一個轉換機制,讓Restful和gprc能夠相互轉化。git

在網上看到一個解決方案,https://github.com/grpc-ecosystem/grpc-gateway,簡單的說就是有一個網關服務器負責轉化和代理轉發。github

以下圖:
imagegolang

安裝

首先要安裝ProtocolBuffers 3.0及以上版本。json

mkdir tmp
cd tmp
git clone https://github.com/google/protobuf
cd protobuf
./autogen.sh
./configure
make
make check
sudo make install

而後使用go get獲取grpc-gateway。api

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

這裏最好把編譯生成的二進制文件的目錄放在$PATH中,能夠把$GOPATH/bin放入$PATH中。服務器

示例

本示例是基於個人上一篇博客《google的grpc在glang中的使用》中的示例,若是有必要請先了解上一篇博客。架構

示例代碼獲取地址:https://github.com/andyidea/go-examplecurl

代碼文件結構以下

└── src
    └── grpc-helloworld-gateway
        ├── gateway
        │   └── main.go
        ├── greeter_server
        │   └── main.go
        └── helloworld
            ├── helloworld.pb.go
            ├── helloworld.pb.gw.go
            └── helloworld.proto

咱們仍是先看一下協議文件。helloworld.proto有一些變更,引入了google官方的api相關的擴展,爲grpc的http轉換提供了支持。

具體改動以下:

syntax = "proto3";

option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";

package helloworld;

import "google/api/annotations.proto";

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {
        option (google.api.http) = {
        post: "/v1/example/echo"
        body: "*"
    };
  }
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

和以前的proto文件比較,新的文件增了

import "google/api/annotations.proto";

option (google.api.http) = {
        post: "/v1/example/echo"
        body: "*"

這裏增長了對http的擴展配置。

而後編譯proto文件,生成對應的go文件

cd src/grpc-helloworld-gateway

protoc -I/usr/local/include -I. \
-I$GOPATH/src \
-I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
--go_out=Mgoogle/api/annotations.proto=github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis/google/api,plugins=grpc:. \
helloworld/helloworld.proto

這裏生成了helloworld/helloworld.pb.go文件。

helloworld.pb.go是server服務須要的,下一步咱們須要使用protoc生成gateway須要的go文件。

cd src/grpc-helloworld-gateway

protoc -I/usr/local/include -I. \
-I$GOPATH/src  -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
--swagger_out=logtostderr=true:. \
helloworld/helloworld.proto

這裏生成了helloworld/helloworld.pb.gw.go文件。這個文件就是gateway用來的協議文件,用來作grpc和http的協議轉換。

協議文件處理完畢,就須要寫gateway代碼了。

gateway代碼以下:

package main

import (
    "flag"
    "net/http"

    "github.com/golang/glog"
    "github.com/grpc-ecosystem/grpc-gateway/runtime"
    "golang.org/x/net/context"
    "google.golang.org/grpc"

    gw "grpc-helloworld-gateway/helloworld"
)

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(":8080", mux)
}

func main() {
    flag.Parse()
    defer glog.Flush()

    if err := run(); err != nil {
        glog.Fatal(err)
    }
}

首先echoEndpoint存儲了須要鏈接的server信息,而後將這些信息和新建的server用gw.go中的RegisterGreeterHandlerFromEndpoint進行一個註冊和綁定,這時低層就會鏈接echoEndpoint提供的遠程server地址,這樣gateway就做爲客戶端和遠程server創建了鏈接,以後用http啓動新建的server,gateway就做爲服務器端對外提供http的服務了。

代碼到此就完成了,咱們測試一下。

先啓動greeter_server服務,再啓動gateway,這時gatway鏈接上greeter_server後,對外創建http的監聽。

而後咱們用curl發送http請求

curl -X POST -k http://localhost:8080/v1/example/echo -d '{"name": " world"}'

{"message":"Hello  world"}

流程以下:curl用post向gateway發送請求,gateway做爲proxy將請求轉化一下經過grpc轉發給greeter_server,greeter_server經過grpc返回結果,gateway收到結果後,轉化成json返回給前端。

這樣,就經過grpc-gateway完成了從http json到內部grpc的轉化過程。

相關文章
相關標籤/搜索