gRPC gateway - Http Restful API & gRPC 协议转换

13 篇文章 1 订阅

gRPC gateway - http restful

gRPC gateway 介绍

gRPC-Gateway 是protocalBufffer的编译插件,根据protobuf的服务定义自动生成反向代理服务器,并将Restful Http API 转化为 gRPC通信协议。反向代理服务器根据 google.api.http 注解生成。

gRPC底层是使用HTTP2 协议进行数据通信,固定为HTTP POST请求,通过gateway 方向代理可以将目前通用的http1.1转换为HTTP2协议 ,并且根据自身需要构建出符合业务场景的Http Restful Api.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9HNXxQ0g-1668943700569)(https://github.com/grpc-ecosystem/grpc-gateway/raw/master/docs/assets/images/architecture_introduction_diagram.svg)]

创建项目

初始化项目

# 1. 创建项目
mkdir grpc-gateway
# 2. 项目初始化
cd grpc-gateway
go mod init example/grpc-gateway/greetings

下载 google 依赖

# 1. 创建proto文件夹
mkdir -p proto/google/api
# 2. 下载google依赖 放置 api 文件夹
cd proto/google/api
wget https://github.com/googleapis/googleapis/tree/master/google/api/http.proto
wget https://github.com/googleapis/googleapis/tree/master/google/api/annotations.proto

创建protobuf文件

在proto文件夹下,创建example.proto

syntax = "proto3";
// go_package example/grpc-gateway/greetings 跟模块初始化名称一致方便引用
// proto 为文件目录
option go_package = "example/grpc-gateway/greetings/proto";

package proto;
import "google/api/annotations.proto";

// The greeting service definition.
service HttpGreeter {
  // Sends a greeting
  rpc SayHello (HttpHelloRequest) returns (HttpHelloReply) {
    option (google.api.http) = {
      post: "/post_hello",
      body: "*"
    };
  }
  rpc SayGetHello (HttpHelloRequest) returns (HttpHelloReply) {
    option (google.api.http) = {
      get: "/get_hello"
    };
  }
}
// The request message containing the user's name.
message HttpHelloRequest {
  string name = 1;
}
// The response message containing the greetings
message HttpHelloReply {
  string message = 1;
}

protobuf编译

  • 安装gateway依赖包

    go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway@v2
    
  • 编译脚本

       #在项目跟目录下执行
       protoc -I=proto \
       --go_out=proto --go_opt=paths=source_relative \
       --go-grpc_out=proto --go-grpc_opt=paths=source_relative \
       --grpc-gateway_out=proto --grpc-gateway_opt=paths=source_relative \
       example.proto
    

    参数说明:

    • -I=proto - 相对目录,结合上下文,表明在proto文件夹下执行example文件夹
    • –go_out=proto - go文件的生成目录
    • –go-grpc_out=proto - grpc文件的生成目录
    • –grpc-gateway_out=proto - 反向代理服务器(grpc gateway)的生成目录

    最后使用脚本,下载相关依赖: go mod tidy

  • 完整项目结构如下

    ├── go.mod
    ├── go.sum
    ├── proto
    │   ├── example.pb.go
    │   ├── example.pb.gw.go
    │   ├── example.proto
    │   ├── example_grpc.pb.go
    │   └── google
    │       └── api
    │           ├── annotations.proto
    │           └── http.proto
    ├── proxy
    │   ├── grpc
    │   │   └── server.go
    │   └── http
    │       └── server.go
    └── server
        └── grpcServer.go
    

服务端代码

package main

import (
   "context"
   "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
   "google.golang.org/grpc"
   "google.golang.org/grpc/credentials/insecure"
   pb "google.golang.org/grpc/examples/httpget/proto"
   "log"
   "net"
   "net/http"
)

type server struct {
   pb.UnimplementedHttpGreeterServer
}

func NewServer() *server {
   return &server{}
}

func (s *server) SayHello(ctx context.Context, in *pb.HttpHelloRequest) (*pb.HttpHelloReply, error) {
   return &pb.HttpHelloReply{Message: in.Name + " world, post HttpRequest"}, nil
}

func (s *server) SayGetHello(ctx context.Context, in *pb.HttpHelloRequest) (*pb.HttpHelloReply, error) {
   return &pb.HttpHelloReply{Message: in.Name + " world, get HttpRequest"}, nil
}

func main() {
   lis, err := net.Listen("tcp", ":9999")
   if err != nil {
      log.Fatalln("Failed to listen:", err)
   }
   s := grpc.NewServer()
   pb.RegisterHttpGreeterServer(s, &server{})
   log.Println("Serving gRPC on port 9999")
   go func() {
      log.Fatalln(s.Serve(lis))
   }()

   // 构建grpc服务上下文 
   conn, err := grpc.DialContext(
      context.Background(),
      "0.0.0.0:9999",
      grpc.WithBlock(),
      grpc.WithTransportCredentials(insecure.NewCredentials()),
   )
   if err != nil {
      log.Fatalln("Failed to dial server:", err)
   }

   gwmux := runtime.NewServeMux()
   // 注册Greeter
   err = pb.RegisterHttpGreeterHandler(context.Background(), gwmux, conn)
   if err != nil {
      log.Fatalln("Failed to register gateway:", err)
   }

   gwServer := &http.Server{
      Addr:    ":8090",
      Handler: gwmux,
   }
   // 8090端口提供gRPC-Gateway服务
   log.Println("Serving gRPC-Gateway on http://0.0.0.0:8090")
   log.Fatalln(gwServer.ListenAndServe())
}

出现代码报错,请执行:go mod tidy

结果测试

HTTP POST

curl -v POST -k http://localhost:8090/post_hello -d '{"name": " hello"}'

在这里插入图片描述

HTTP GET

curl -v http://localhost:8090/get_hello/kobe

在这里插入图片描述

服务代码分离

之前为了测试方便,服务端代码将gRPC代码、gateway代码结合在一起,这在真实的项目中是不可取的,理由是:任何一方出现系统问题都会导致整个服务不可用。分开后的代码如下:

Grpc 服务

package main

import (
   "context"
   pb "example/grpc-gateway/greetings/proto"
   "google.golang.org/grpc"
   "log"
   "net"
)

const (
   port = ":9999"
)
type server struct {
   pb.UnimplementedHttpGreeterServer
}
func NewServer() *server {
   return &server{}
}
func (s *server) SayHello(ctx context.Context, in *pb.HttpHelloRequest) (*pb.HttpHelloReply, error) {
   return &pb.HttpHelloReply{Message: in.Name + " world, post HttpRequest"}, nil
}
func (s *server) SayGetHello(ctx context.Context, in *pb.HttpHelloRequest) (*pb.HttpHelloReply, error) {
   return &pb.HttpHelloReply{Message: in.Name + " world, get HttpRequest"}, nil
}

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

   pb.RegisterHttpGreeterServer(s, &server{})
   if err := s.Serve(lis); err != nil {
      log.Fatalf("failed to serve: %v", err)
   }
}

Http 服务

package main

import (
   "flag"
   "net/http"

   pb "example/grpc-gateway/greetings/proto"
   "github.com/golang/glog"
   "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
   "golang.org/x/net/context"
   "google.golang.org/grpc"
)

var (
   tasklistEndpoint = flag.String("tasklist_endpoint", "localhost:9999", "endpoint of YourService")
)

func run() error {
   ctx := context.Background()
   ctx, cancel := context.WithCancel(ctx)
   defer cancel()

   mux := runtime.NewServeMux()
   opts := []grpc.DialOption{grpc.WithInsecure()}
   //分离后的代码 使用的api不一致,仅此而已
   err := pb.RegisterHttpGreeterHandlerFromEndpoint(ctx, mux, *tasklistEndpoint, opts)
   if err != nil {
      return err
   }
   return http.ListenAndServe(":8090", mux)
}

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

   if err := run(); err != nil {
      glog.Fatal(err)
   }
}
  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java + gRPC + grpc-gateway 的实践主要分为以下几个步骤: 1. 定义 proto 文件 在 proto 文件中定义需要调用的服务以及方法,同时指定请求和响应的数据类型。例如: ``` syntax = "proto3"; package example; service ExampleService { rpc ExampleMethod (ExampleRequest) returns (ExampleResponse) {} } message ExampleRequest { string example_field = 1; } message ExampleResponse { string example_field = 1; } ``` 2. 使用 protoc 编译 proto 文件 使用 protoc 编译 proto 文件,生成 Java 代码。例如: ``` protoc --java_out=./src/main/java ./example.proto ``` 3. 实现 gRPC 服务 在 Java 代码中实现定义的 gRPC 服务,例如: ``` public class ExampleServiceImpl extends ExampleServiceGrpc.ExampleServiceImplBase { @Override public void exampleMethod(ExampleRequest request, StreamObserver<ExampleResponse> responseObserver) { // 实现具体逻辑 ExampleResponse response = ExampleResponse.newBuilder().setExampleField("example").build(); responseObserver.onNext(response); responseObserver.onCompleted(); } } ``` 4. 启动 gRPC 服务器 使用 gRPC 提供的 ServerBuilder 构建 gRPC 服务器,并启动服务器。例如: ``` Server server = ServerBuilder.forPort(8080).addService(new ExampleServiceImpl()).build(); server.start(); ``` 5. 集成 grpc-gateway 使用 grpc-gateway 可以将 gRPC 服务转换HTTP/JSON API。在 proto 文件中添加以下内容: ``` import "google/api/annotations.proto"; service ExampleService { rpc ExampleMethod (ExampleRequest) returns (ExampleResponse) { option (google.api.http) = { post: "/example" body: "*" }; } } ``` 在 Java 代码中添加以下内容: ``` Server httpServer = ServerBuilder.forPort(8081).addService(new ExampleServiceImpl()).build(); httpServer.start(); String grpcServerUrl = "localhost:8080"; String httpServerUrl = "localhost:8081"; ProxyServerConfig proxyConfig = new ProxyServerConfig(grpcServerUrl, httpServerUrl, "/example"); HttpProxyServer httpProxyServer = new HttpProxyServer(proxyConfig); httpProxyServer.start(); ``` 6. 测试 使用 HTTP/JSON API 调用 gRPC 服务,例如: ``` POST http://localhost:8081/example Content-Type: application/json { "example_field": "example" } ``` 以上就是 Java + gRPC + grpc-gateway 的实践步骤。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值