「gRPC」 Gateway的实现
一、gateway
网关(Gateway)又称网间连接器、协议转换)器。网关在网络层以上实现网络互连,是复杂的网络互连设备,仅用于两个高层协议不同的网络互连。网关既可以用于广域网互连,也可以用于局域网互连。 网关是一种充当转换重任的计算机系统或设备。使用在不同的通信协议、数据格式或语言,甚至体系结构完全不同的两种系统之间,网关是一个翻译器。与网桥只是简单地传达信息不同,网关对收到的信息要重新打包,以适应目的系统的需求。同层–应用层。
简单的说就是:从一个房间走到另一个房间,必然要经过一扇门。同样,从一个网络向另一个网络发送信息,也必须经过一道“关口”,这道关口就是网关
grpc-gateway 能够将我们写好的grpc服务转换为外部可以访问的http服务,这样一来我们就对外暴露grpc服务的接口,当然grpc内部依然需要使用grpc通讯。
二、gateway的实现
我们来模拟行程获取的过程,内部使用grpc将服务写好,使用grpc向外暴露http接口
首先我们需要知道:
- 起点
- 终点
- 距离
- 费用
- 起点终点坐标
- 路径坐标
下面我们来编写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()方法不会退出
}
编写客户端:
- 这里也可以通过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)
}
- 通过浏览器:
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 协议》,转载必须注明作者和本文链接