Golang——gRPC gateway网关

前言

        etcd3 API全面升级为gRPC后,同时要提供REST API服务,维护两个版本的服务显然不大合理,所以gRPC-gateway诞生了。通过protobuf的自定义option实现了一个网关。服务端同时开启gRPC和HTTP服务,HTTP服务接收客户端请求后转换为grpc请求数据,获取响应后转为json数据放回给客户端。

 安装gRPC-gateway:go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway

目录结构

        这里用到了google官方Api中的两个proto描述文件,直接拷贝不需要做修改,里面定义了protocol buffer扩展的HTTP option,为grpc的http转换提供了支持。

 Proto文件

annotations.proto:

// ./proto/google/api/annotations.proto
syntax = "proto3";package google.api;option go_package = "google_api";import "http.proto";
import "descriptor.proto";option java_multiple_files = true;
option java_outer_classname = "AnnotationsProto";
option java_package = "com.google.api";extend google.protobuf.MethodOptions {HttpRule http = 72295728;}

http.proto:

// ./proto/google/api/http.proto
syntax = "proto3";package google.api;option go_package = "google_api";option cc_enable_arenas = true;
option java_multiple_files = true;
option java_outer_classname = "HttpProto";
option java_package = "com.google.api";message Http {repeated HttpRule rules = 1;
}message HttpRule {string selector = 1;oneof pattern {string get = 2;string put = 3;string post = 4;string delete = 5;string patch = 6;CustomHttpPattern custom = 8;}string body = 7;repeated HttpRule additional_bindings = 11;
}message CustomHttpPattern {string kind = 1;string path = 2;
}

编写自定义的hello_http.proto:

        这里在SayHello方法定义中增加了http option,POST方法,路由为"/example/echo"。

syntax="proto3";
package hello_http;import "annotations.proto";service HelloHTTP {rpc SayHello(HelloHTTPRequest) returns (HelloHTTPResponse){//http optionoption(google.api.http) = {post:"/example/echo"body:"*"};}}message HelloHTTPRequest{string name = 1;
}message HelloHTTPResponse{string message = 1;
}

 在生成go对应的proto文件时,报了错,仅供参考:

         这个是因为找不到import的proto文件。可以使用-I或--proto_path。protoc命令中的proto_path参数用于指定proto文件的搜索路径。可以设置多个。

         生成对应的*.pb.go文件:

#编译hello_http.proto文件
#D:\gocode\src\grpc_gateway\proto\google\api地址为import的annotations.proto和http.proto文件位置
#D:\gocode\pkg\mod\google.golang.org\protobuf@v1.34.2\src\google\protobuf为annotations.proto文件import的descriptor.proto文件位置
protoc --go_out=plugins=grpc:. -I=. .\hello_http.proto -I=D:\gocode\src\grpc_gateway\proto\google\api -I=D:\gocode\pkg\mod\google.golang.org\protobuf@v1.34.2\src\google\protobuf#编译annotations.proto文件
#D:\gocode\src\grpc_gateway\proto\google\api地址为import的http.proto文件位置
#D:\gocode\pkg\mod\google.golang.org\protobuf@v1.34.2\src\google\protobuf为annotations.proto文件import的descriptor.proto文件位置
protoc --go_out=. -I=.\ .\annotations.proto  -I=D:\gocode\src\grpc_gateway\proto\google\api  -I=D:\gocode\pkg\mod\google.golang.org\protobuf@v1.34.2\src\google\protobuf#编译http.proto文件
protoc --go_out=. -I=.\ .\http.proto#编译hello_http.proro gateway
protoc --grpc-gateway_out=logtostderr=true:. -I=. .\hello_http.proto -I=D:\gocode\src\grpc_gateway\proto\google\api -I=D:\gocode\pkg\mod\google.golang.org\protobuf@v1.34.2\src\google\protobuf

         注意这里需要编译google/api中的两个proto文件,同时在编译hello_http.proto时使用了grpc-gateway编译生成hello_http.pb.gw.go文件,这个文件时用来协议转换的,查看文件可以看到里面生成的http handler,处理proto文件中定义的路由"example/echo"接收POST参数,调用HelloHTTP服务的客户端请求grpc服务并响应结果。

实现服务器

package mainimport ("context""fmt""log""net""sample-app/grpc_gateway/proto/hello_http""google.golang.org/grpc"
)// gRPC服务地址
var addr = "127.0.0.1:8080"type helloService struct{}var HelloService = helloService{}// 实现约定接口
func (h helloService) SayHello(ctx context.Context, in *hello_http.HelloHTTPRequest) (*hello_http.HelloHTTPResponse, error) {resp := new(hello_http.HelloHTTPResponse)resp.Message = fmt.Sprintf("Hello %s\n", in.Name)return resp, nil
}func main() {//监听连接ls, err := net.Listen("tcp", addr)if err != nil {return}//实例化grpc Servers := grpc.NewServer()//组成HelloService服务hello_http.RegisterHelloHTTPServer(s, HelloService)log.Println("Listen on " + addr)s.Serve(ls)
}

实现客户端

package mainimport ("context""fmt""sample-app/grpc_gateway/proto/hello_http""google.golang.org/grpc"
)var addr = "127.0.0.1:8080"func main() {//连接conn, err := grpc.Dial(addr, grpc.WithInsecure())if err != nil {return}//初始化客户端c := hello_http.NewHelloHTTPClient(conn)//发送请求resp, err := c.SayHello(context.Background(), &hello_http.HelloHTTPRequest{Name: "gRPC"})if err != nil {return}fmt.Println(resp.Message)
}

实现http server

package mainimport ("context""fmt""net/http""sample-app/grpc_gateway/proto/hello_http""github.com/grpc-ecosystem/grpc-gateway/runtime""google.golang.org/grpc"
)func main() {//定义上下文ctx, cancel := context.WithCancel(context.Background())defer cancel()endpoint := "127.0.0.1:8080"mux := runtime.NewServeMux()var opts = []grpc.DialOption{grpc.WithInsecure()}//HTTP转grpcerr := hello_http.RegisterHelloHTTPHandlerFromEndpoint(ctx, mux, endpoint, opts)if err != nil {return}fmt.Println("http listen on 8081")http.ListenAndServe(":8081", mux)
}

         就是这么简单。开启了一个http server,收到请求后根据路由转发请求到对应的RPC接口获得结果。grpc-gateway做的事情就是帮我们自动生成了转换过程的实现。

运行结果

1. 启动服务器:

 2. 启动server http

3. 启动客户端

升级版服务端 

         上面的使用方式已经实现了我们最初的需求,grpc-gateway项目中提供的示例也是这种方式,这样后台需要开启两个服务两个端口。其实我们也可以只开启一个服务,同时提供http和gRPC调用方式。

        新建一个项目hello_http2。目录结构:

        proto文件和上面的一样。

        生成私钥和秘钥:Golang——gRPC认证和拦截器-CSDN博客 

  • 服务端代码 
package mainimport ("context""crypto/tls""fmt""io/ioutil""log""net""net/http""sample-app/grpc_gateway/proto/hello_http""strings""github.com/grpc-ecosystem/grpc-gateway/runtime""golang.org/x/net/http2""google.golang.org/grpc""google.golang.org/grpc/credentials"
)type helloService struct{}var HelloService = helloService{}func (h helloService) SayHello(c context.Context, req *hello_http.HelloHTTPRequest) (*hello_http.HelloHTTPResponse, error) {resp := new(hello_http.HelloHTTPResponse)resp.Message = fmt.Sprintf("Hello %s", req.Name)return resp, nil
}func main() {endpoint := "127.0.0.1:8080"//监听连接conn, err := net.Listen("tcp", endpoint)if err != nil {log.Println("listen fail ", err)return}//grpc服务cred, err := credentials.NewServerTLSFromFile("..\\..\\key\\server.pem", "..\\..\\key\\server_private.key")if err != nil {log.Println("credentials fail ", err)return}s := grpc.NewServer(grpc.Creds(cred))hello_http.RegisterHelloHTTPServer(s, HelloService)//gateway 服务ctx := context.Background()//与grpc服务交互时,需要TLS认证dcred, err := credentials.NewClientTLSFromFile("..\\..\\key\\server.pem", "www.wy.com")if err != nil {log.Println("NewClientTLSFromFile fail ", err)return}dopts := []grpc.DialOption{grpc.WithTransportCredentials(dcred)}gwmux := runtime.NewServeMux()//注册http转grpcif err = hello_http.RegisterHelloHTTPHandlerFromEndpoint(ctx, gwmux, endpoint, dopts); err != nil {log.Println("RegisterHelloHTTPHandlerFromEndpoint fail", err)return}//http服务mux := http.NewServeMux()mux.Handle("/", gwmux)srv := &http.Server{Addr:      endpoint,Handler:   grpcHanderFunc(s, mux),TLSConfig: getTLSConfig(),}if err = srv.Serve(tls.NewListener(conn, srv.TLSConfig)); err != nil {log.Println("srv server fail ", err)}
}func getTLSConfig() *tls.Config {cert, _ := ioutil.ReadFile("..\\..\\key\\server.pem")key, _ := ioutil.ReadFile("..\\..\\key\\server_private.key")var demoKeyPair *tls.Certificatepair, err := tls.X509KeyPair(cert, key)if err != nil {log.Println("X509KeyPair fail ", err)return nil}demoKeyPair = &pairreturn &tls.Config{Certificates: []tls.Certificate{*demoKeyPair},NextProtos:   []string{http2.NextProtoTLS}, //http2 TLS支持}
}// grpcHanderFunc return a http.Handler that delegates to grpcServer on incoming gRPC
// connections or otherHandler otherwise. Copied from cockroachdb
func grpcHanderFunc(grpcServer *grpc.Server, otherHandle *http.ServeMux) http.Handler {if otherHandle == nil {return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {grpcServer.ServeHTTP(w, req)})}return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {if req.ProtoMajor == 2 && strings.Contains(req.Header.Get("Content-Type"), "application/grpc") {grpcServer.ServeHTTP(w, req)} else {otherHandle.ServeHTTP(w, req)}})
}

        gRPC服务端接口的实现没有区别,重点在于HTTP服务的实现。gRPC是基于http2实现的,net/http包也实现了http2,所以我们可以开启一个HTTP服务同时服务两个版本的协议,在注册http handler的时候,在方法grpcHandlerFunc中检测请求头信息,决定是直接使用调用gRPC服务还是使用gateway的HTTP服务。net/http中对http2的支持要求开启https,所以这里要求使用http服务。

步骤:

  • 注册开启TLS的grpc服务
  • 注册开启TLS的gateway服务,地址指向grpc服务。
  • 开启HTTP server

运行结果:

 

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

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

相关文章

(微服务项目实战)预付卡系统券模块系统设计

1 技术架构 框架描述版本JDKJava运行环境17SpringBoot基于SpringBoot完成后端代码开发3.2.6DubboApache Dubbo 是一款易用、高性能的 WEB 和 RPC 框架,同时为构建企业级微服务提供服务发现、流量治理、可观测、认证鉴权等能力、工具与最佳实践3.xSpringCloud微服务…

WINUI——Trigger(触发器)使用小结

背景 WINUI不提供原生的Trigger支持,推荐使用VisualStateManager进行操作;然对于从WPF转WINUI的开发人员而言,经常会想用Trigger解决问题,鉴于此社区推出了CommunityToolkit.WinUI.Triggers以支持Trigger的使用。 使用方法 1.项…

Sky Master ULTIMATE Volumetric Skies Clouds Weather

该系统包含行业级优化的体积云、海洋系统、GI 代理,以及用于全局光照的优化 SEGI 和基于物理的天空渲染系统,且带有大气散射。 Sky Manager 可提供自动或按需的日/夜循环以及平滑的天气过渡。 Skybox 模式提供了与 Unity 及其功能(IBLGI、GI、Skybox)的完整集成。 先进的粒…

【YashanDB知识库】PHP使用ODBC驱动无法获取长度为256char以上的数据

【问题分类】驱动使用 【关键字】ODBC、驱动使用、PHP、 【问题描述】PHP使用PDO_ODBC连接yashan数据库,获取数据类型大于或等于varchar(256 char)的数据时出现异常,数据无法正常获取,BLOB等字段也无法正常获取,并且该问题会导致…

【C语言】解决C语言报错:Undefined Reference

文章目录 简介什么是Undefined ReferenceUndefined Reference的常见原因如何检测和调试Undefined Reference解决Undefined Reference的最佳实践详细实例解析示例1:缺少函数定义示例2:函数声明和定义不匹配示例3:未链接必要的库示例4&#xff…

useEffect的概念以及使用(对接口)

// useEffect的概念以及使用 import {useEffect, useState} from reactconst Url"http://geek.itheima.net/v1_0/channels"function App() {// 创建状态变量const [lustGet,setLustGet]useState([]);// 渲染完了之后执行这个useEffect(() > {// 额外的操作&#x…

【C++】stack、queue和deque的使用

💗个人主页💗 ⭐个人专栏——C学习⭐ 💫点击关注🤩一起学习C语言💯💫 目录 导读 一、stack 1. stack介绍 2. stack使用 二、queue 1. queue介绍 2. queue使用 三、deque 1. deque介绍 2. deque的…

大数据实训项目(小麦种子)-04、大数据实训项目JavaWeb环境搭建

文章目录 前言运行前准备工作1、安装Hadoop3.1.0配置winutils原因描述配置方式注意点(hadoop.dll拷贝System32目录下) 2、hive运行报错(The dir: /tmp/hive on HDFS should be writable. ) 项目环境搭建参考资料 前言 博主介绍&a…

Python设计模式 - 简单工厂模式

定义 简单工厂模式是一种创建型设计模式,它通过一个工厂类来创建对象,而不是通过客户端直接实例化对象。 结构 工厂类(Factory):负责创建对象的实例。工厂类通常包含一个方法,根据输入参数的不同创建并返…

357. 统计各位数字都不同的数字个数

. - 力扣&#xff08;LeetCode&#xff09; class Solution { public:int countNumbersWithUniqueDigits(int n) {vector<int> f(n1);if(n0)return 1;if(n1)return 10;f[0]1;f[1]10;for(int i2;i<n;i)f[i] f[i-1] (f[i-1]-f[i-2])*(10-(i-1));return f[n];} };

电子行业实施MES管理系统的时机是什么

随着信息技术的飞速发展&#xff0c;MES生产管理系统逐渐成为电子企业实现自动化生产和信息化管理的必备工具。那么&#xff0c;何时是电子企业实施MES管理系统的最佳时机呢&#xff1f; 1.生产过程中出现了问题&#xff0c;需要优化和改进。 2.企业需要提高产品交付和响应速…

港理工最新综述:基于LLM的text-to-SQL调查(方法实验数据全面梳理)1

【摘要】文本到SQL旨在将自然语言问题转换为可执行的SQL语句,这对用户提问理解、数据库模式理解和SQL生成都是一个长期存在的挑战。传统的文本到SQL系统包括人工工程和深度神经网络。随后,预训练语言模型(PLMs)被开发并用于文本到SQL任务,取得了可喜的成绩。随着现代数据库变得…

【AIGC】MetaGPT原理以及应用

目录 MetaGPT原理 MetaGPT应用 MetaGPT和传统编程语言相比有什么优势和劣势 视频中的PPT 参考资料 MetaGPT原理 MetaGPT是一种多智能体框架&#xff0c;它结合了元编程技术&#xff0c;通过标准化操作程序&#xff08;SOPs&#xff09;来协调基于大语言模型的多智能体系统…

Python学习打卡:day06

day6 笔记来源于&#xff1a;黑马程序员python教程&#xff0c;8天python从入门到精通&#xff0c;学python看这套就够了 目录 day648、函数综合案例49、数据容器入门50、列表的定义语法51、列表的下标索引1、列表的下标&#xff08;索引&#xff09;2、列表的下标&#xff08…

数据防泄漏的六个步骤|数据防泄漏软件有哪些

在当前复杂多变的网络安全环境下&#xff0c;数据防泄漏软件成为了企业信息安全架构中不可或缺的一环。下面以安企神软件为例&#xff0c;告诉你怎么防止数据泄露&#xff0c;以及好用的防泄露软件。 1. 安企神软件 安企神软件是当前市场上备受推崇的企业级数据防泄漏解决方案…

等待 chrome.storage.local.get() 完成

chrome.storage.local.get() 获取存储处理并计数&#xff0c;内部计数正常&#xff0c;外部使用始终为0&#xff0c;百思不得其解。 如何在继续执行之前等待异步chrome.storage.local.get()完成-腾讯云开发者社区-腾讯云 (tencent.com) 原来我忽略了异步问题&#xff0c;最简…

(虚拟机)VMware软件的安装及Ubuntu系统安装

一、VMware软件的安装 软件下载&#xff0c;可以自己找或者百度网盘下载&#xff1a; 通过百度网盘分享的文件&#xff1a;ubuntu16…等2个文件 链接:https://pan.baidu.com/s/1VEnZKY9DJ1T1vC3ae20gKQ 提取码:11b6 复制这段内容打开「百度网盘APP 即可获取」 1、解压VMwar…

嵌入式操作系统_6.任务间通信

1.任务间通信管理 任务间通信管理也是嵌入式操作系统的关键功能之一。它主要为操作系统的应用程序提供多种类型的数据传输、任务同步/异步操作等手段。 2.操作系统任务之间的关系 由于嵌入式操作系统是为应用提供管理、硬件支持、协调任务和中断处理程序等功能&#xff0c;具备…

Android入门第69天-AndroidStudio中的Gradle使用国内镜像最强教程

背景 AndroidStudio默认连接的是dl.google的gadle仓库。 每次重新build时: 下载速度慢;等待了半天总时build faild;build到一半connection timeout;即使使用了魔法也难以一次build好;这严重影响了我们的学习、开发效率。 当前网络上的使用国内镜像的教程不全 网上的教程…

[初阶数据结构] 包装类 | 泛型

目录 一. 包装类 1.1 什么是包装类? 1.2 包装类的意义 1.3 基本数据类型与包装类 1.4 装箱 1.5 拆箱 1.6 小总结 二. 泛型 2.1 什么是泛型? 2.2 泛型的意义 2.3 泛型的语法 2.4 泛型的编译 2.4.1 下载插件 2.4.2 分析 2.5 上界 2.6 泛型方法 2.7 小总结 三. 总结 一.…