「gRPC」 Gateway的实现

一、gateway

网关(Gateway)又称网间连接器、协议转换)器。网关在网络层以上实现网络互连,是复杂的网络互连设备,仅用于两个高层协议不同的网络互连。网关既可以用于广域网互连,也可以用于局域网互连。 网关是一种充当转换重任的计算机系统或设备。使用在不同的通信协议、数据格式或语言,甚至体系结构完全不同的两种系统之间,网关是一个翻译器。与网桥只是简单地传达信息不同,网关对收到的信息要重新打包,以适应目的系统的需求。同层–应用层。

简单的说就是:从一个房间走到另一个房间,必然要经过一扇门。同样,从一个网络向另一个网络发送信息,也必须经过一道“关口”,这道关口就是网关

grpc-gateway 能够将我们写好的grpc服务转换为外部可以访问的http服务,这样一来我们就对外暴露grpc服务的接口,当然grpc内部依然需要使用grpc通讯。

二、gateway的实现

我们来模拟行程获取的过程,内部使用grpc将服务写好,使用grpc向外暴露http接口

首先我们需要知道:

  1. 起点
  2. 终点
  3. 距离
  4. 费用
  5. 起点终点坐标
  6. 路径坐标

下面我们来编写trip.proto

syntax = "proto3";  //语法使用proto3
package coolcar;
option go_package = "coolcar/proto/gen/go/;trippb";

message Location{
    double latitude = 1;
    double longitude = 2;
}

enum TripStatus{
    TS_NOT_SPECIFID = 0;
    NOT_STARTED = 1;
    IN_PROGRESS = 2;
    FINISHED = 3;
    PAID = 4;
}

message Trip{
    string statar = 1;  //数字表示标记码,不是赋值
    Location statar_pos = 5;
    repeated Location path_locations = 7;
    string end = 2;
    Location end_pos = 6;
    int32 duration_sec = 3;
    int32 fee_cent = 4;
    TripStatus status = 8;

}

message GetTripRequest{
    string id = 1;
}

message GetTripResponse{
    string id = 1;
    Trip trip = 2;
}

service TripService{
    rpc GetTrip (GetTripRequest) returns (GetTripResponse);
}

在编译前我们需要写一个trip.yaml文件用来配置对外http服务向外暴露端口

type: google.api.Service
config_version: 3

http:
  rules:
  - selector: coolcar.TripService.GetTrip
    get: /trip/{id}

然后我们编写一个trip.sh文件用来执行我们的编译命令吧

protoc -I=. --go_out=plugins=grpc,paths=source_relative:gen/go trip.proto
protoc -I=. --grpc-gateway_out=paths=source_relative,grpc_api_configuration=trip.yaml:gen/go trip.proto

编译过后在gen/go下会生成trip.pb.go文件和trip.pb.gw.go文件

具体代码在:gRPC Gateway的实现

相应的客户端和服务端代码就已经为我们生成了

我们只需要实现服务端的接口:

// TripServiceServer is the server API for TripService service.
type TripServiceServer interface {
    GetTrip(context.Context, *GetTripRequest) (*GetTripResponse, error)
}

接口的实现:

import (
    "context"
    trippb "coolcar/proto/gen/go"
)

//type TripServiceServer interface {
//    GetTrip(context.Context, *GetTripRequest) (*GetTripResponse, error)
// }

type Service struct{}

func (*Service) GetTrip(con context.Context, req *trippb.GetTripRequest) (*trippb.GetTripResponse, error) {
    return &trippb.GetTripResponse{
    //客户请求什么Id,服务端返回什么Id
        Id: req.Id,
        Trip: &trippb.Trip{
            Statar:      "北京"
            End:         "上海",
            DurationSec: 3600,
            FeeCent:     1000,
            StatarPos: &trippb.Location{
                Latitude:  30,
                Longitude: 120,
            },
            EndPos: &trippb.Location{
                Latitude:  40,
                Longitude: 125,
            },
            PathLocations: []*trippb.Location{
                {
                    Latitude:  34,
                    Longitude: 123,
                },
                {
                    Latitude:  38,
                    Longitude: 124,
                },
            },
            Status: trippb.TripStatus_FINISHED,
        },
    }, nil
}

现在我们来实现获取行程的整个过程:

编写service端和gateway

package main

import (
    "context"
    trippb "coolcar/proto/gen/go"
    trip "coolcar/tripservice"
    "fmt"
    "log"
    "net"
    "net/http"

    "github.com/grpc-ecosystem/grpc-gateway/runtime"
    "google.golang.org/grpc"
)

//gateway的实现
func startGRPCGatway() {
    c := context.Background()          //生成没具体内容的上下文
    c, cancel := context.WithCancel(c) //该方法将具有cancel的能力
    defer cancel()

    //runtime.WithMarshalerOption()将Status: trippb.TripStatus_FINISHED,改为status:3
    mux := runtime.NewServeMux(runtime.WithMarshalerOption(
        runtime.MIMEWildcard, &runtime.JSONPb{
            EnumsAsInts: true, //status
            OrigName:    true, //命名
        },
    ))

    err := trippb.RegisterTripServiceHandlerFromEndpoint(
        c, //通过context去连接, 注册在runtime.NewServeMux()上面
        mux,
        ":8081",  //连接内部grpc服务端口
        []grpc.DialOption{grpc.WithInsecure()}, //grpc.WithInsecure()连接方式tcp明文,即不做安全处理
    )
    if err != nil {
        log.Fatalf("断开连接: %v", err)
    }

    //对外暴露http端口
    err = http.ListenAndServe(":8080", mux)
    if err != nil {
        log.Fatalf("连接失败: %v", err)
    }
}


//service 端
func main() {
    fmt.Println("监听开始")
    go startGRPCGatway()
    list, err := net.Listen("tcp", ":8081")
    if err != nil {
        log.Fatalf("监听失败: %v", err)
    }
    s := grpc.NewServer() //NewServer 创建一个未注册服务且尚未开始接受请求的 gRPC 服务器。
    trippb.RegisterTripServiceServer(s, &trip.Service{})
    fmt.Println("监听结束")
    fmt.Println(list)
    log.Fatal(s.Serve(list)) //s.Serve()方法不会退出

}

编写客户端:

  1. 这里也可以通过grpc进行拨号:
package main

import (
    "context"
    trippb "coolcar/proto/gen/go"
    "fmt"
    "log"

    "google.golang.org/grpc"
)

func main() {
  //连接gateway
    con, err := grpc.Dial("localhost:8080")
    if err != nil {
        log.Fatalf("连接失败: %v", err)
    }

    tsClient := trippb.NewTripServiceClient(con)
    res, err := tsClient.GetTrip(context.Background(), &trippb.GetTripRequest{
        Id: "trips01",
    })
    if err != nil {
        log.Fatalf("未获取到trips: %v", err)
    }
    fmt.Println(res)
}
  1. 通过浏览器:http://localhost:8080/trip?Id=trips01

这样整个流程就完了

返回结果:

id:"trips01" trip:{statar:"北京" statar_pos:{latitude:30 longitude:120} path_locations:{latitude:34 longitude:123} path_locations:{latitude:38 longitude:124} end:"上海" end_pos:{latitude:40 longitude:125} duration_sec:3600 fee_cent:1000 status:FINISHED}
本作品采用《CC 协议》,转载必须注明作者和本文链接
刻意学习
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
未填写
文章
118
粉丝
89
喜欢
173
收藏
246
排名:365
访问:2.6 万
私信
所有博文
社区赞助商