gRPC gateway - Http Restful API gRPC 协议转换

news2025/1/10 11:08:26

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)
   }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/21666.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

mybatis # $

总结: # 你传入的变量类型会被保留 $ 本质就是拼接 不会考虑拼串的 $ 情况下 参数是整数 跟参数是字符串 字符串情况: 缺少’’ 相当于字符串拼接 ”select * from t_user where username “ “张三” ”select * from t_user where username 张三&…

java 容器

java 容器 数组 数组的扩容问题 ArrayList 的默认初始化容量为0,首次添加元素时,创建容量为(10 || 添加集合大小) ,以后每次扩容的话,为当前容量的1.5倍 public ArrayList() {/*初始化容量大小为0private static final Object…

CEAC之《计算机应用助理工程师》2

👨‍💻个人主页:微微的猪食小窝 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 本文由 微微的猪食小窝 原创 收录于专栏 【CEAC证书】 1组合框的常用属性有 ____________ 。 A、Index B、Text C、Caption D、ListCountA,B,D2在…

ES6 入门教程 16 Reflect 16.2 静态方法 16.3 实例:使用 Proxy 实现观察者模式

ES6 入门教程 ECMAScript 6 入门 作者:阮一峰 本文仅用于学习记录,不存在任何商业用途,如侵删 文章目录ES6 入门教程16 Reflect16.2 静态方法16.2.1 Reflect.get(target, name, receiver)16.2.2 Reflect.set(target, name, value, receiver)1…

数据结构之:数组

数组初体验之数组中重复的数字 数组 : 有限个 相同类型的变量 组成的有序集合 int[] arr; int arr[]; // 静态初始化 String[] strArr {"和平精英","王者荣耀","开心消消乐","欢乐斗地主"}; // 动态初始化 String[] strAr…

自学 TypeScript 第三天 使用webpack打包 TS 代码

安装: 首先第一步,我们要初始化我们项目,在目录下输入 npm init 接下来,我们的安装几个工具 npm i -D webpack webpack-cli typescript ts-loader -D 意思是 开发依赖,也就是我们现在所安装的依赖都是开发依赖&am…

知乎 日报

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录前言评论界面首页cell的小标题的文字显示下拉刷新前言 这周完成了评论内容,改了一些小bug。收藏界面正在加油,FMDB库目前不是很理解 评论界面…

【C++初阶】三、类和对象(中)

在上一篇类和对象中,我们初步了解了类和对象的基本概念,知道了什么是类,接下来一起看看类和对象的具体细节和实现吧。(以日期类举例) 文章目录类和对象【中】1.类的6个默认成员函数2. 构造函数2.1 构造函数定义2.2 构造…

SpringBoot SpringBoot 开发实用篇 4 数据层解决方案 4.16 SpringBoot 整合 ES 客户端操作

SpringBoot 【黑马程序员SpringBoot2全套视频教程,springboot零基础到项目实战(spring boot2完整版)】 SpringBoot 开发实用篇 文章目录SpringBootSpringBoot 开发实用篇4 数据层解决方案4.16 SpringBoot 整合 ES 客户端操作4.16.1 环境准备…

[附源码]java毕业设计停车场收费管理系统

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

DP入门(一)

前言:由于作者经常卡力扣周赛最后一题的dp,因此决定痛改前非,从头做人,争取下次能做出最后一道dp ak周赛!呜呜呜加油~~ 因此 这个系列的文章不会教 dp ,只会讲刷题思路,目前的计划是先更 lc 的题…

[Spring Cloud] RestTemplate跨进程调用

✨✨个人主页:沫洺的主页 📚📚系列专栏: 📖 JavaWeb专栏📖 JavaSE专栏 📖 Java基础专栏📖vue3专栏 📖MyBatis专栏📖Spring专栏📖SpringMVC专栏📖SpringBoot专…

【k8s】5、资源管理命令-声明式

文章目录一、 yaml和json介绍1、yuml语言介绍2、k8s支持的文件格式3、yaml和json的主要区别二、声明式对象管理1、命令式对象配置2、声明式对象配置3、声明式对象管理命令介绍三、编写资源配置清单1、 编写yaml文件2、 启动并查看资源3、创建service服务对外提供访问测试4、创建…

MySQL介绍

MySQL数据库最初是由瑞典MySQL AB公司开发,2008年1月16号被Sun公司收购。2009年,SUN又被Oracle收购。MySQL是目前IT行业最流行的开放源代码的数据库管理系统,同时它也是一个支持多线程高并发多用户的关系型数据库管理系统。MySQL之所以受到业…

基于51单片机的舞蹈机器人四路步进电机控制仿真

资料编号:091 下面是相关功能视频演示: 91-基于51单片机的舞蹈机器人四路步进电机控制仿真(源码仿真全套资料)功能介绍:通过51单片机控制4个步进电机旋转,模拟出机器人的四肢动作,全套资料齐全…

ES6 入门教程 17 Promise 对象 17.2 基本用法

ES6 入门教程 ECMAScript 6 入门 作者:阮一峰 本文仅用于学习记录,不存在任何商业用途,如侵删 文章目录ES6 入门教程17 Promise 对象17.2 基本用法17 Promise 对象 17.2 基本用法 ES6 规定,Promise对象是一个构造函数&#xff0…

【Java高级】一篇文章带你搞懂线程

目录 | 线程概述 | 线程创建 方式一:继承 Thread 类 方式二:实现 Runnable 接口 一些小细节 方式三:实现 Callable 接口(JDK1.8) | 线程生命周期 生命周期概述 [获取线程信息] 方法 set/getName current [运…

ArcGIS绘制北半球俯视投影地图

做全球碳水循环,植被变化,极端气候相关研究的同学都知道。北半球是核心,因为北半球的核心区域(东亚湿润区,中亚干旱半干旱,青藏高原,阿拉伯半岛,非洲北部沙漠以及美国西部等等核心区): 对于北半球的展示一般采用下面的图: 那么该如何做呢? 熟悉地图学的同学都知道…

Dubbo-聊聊Dubbo协议

前言 Dubbo源码阅读分享系列文章,欢迎大家关注点赞 SPI实现部分 Dubbo-SPI机制 Dubbo-Adaptive实现原理 Dubbo-Activate实现原理 Dubbo SPI-Wrapper 注册中心 Dubbo-聊聊注册中心的设计 Dubbo-时间轮设计 通信 Dubbo-聊聊通信模块设计 什么是协议 在网络交…

【FreeRTOS】FreeRTOS删除任务vTaskDelete()

使用说明: 任务中。小时 (任务句柄_t xTask); INCLUDE_vTaskDelete必须定义为1,才能使用此函数。有关更多信息,请参见RTOS配置文档。 从RTOS内核管理中删除任务。正在删除的任务将从所有就绪、阻止、暂停和事件列表中删…