Golang——gRPC gateway网关

news2024/11/26 13:55:47

前言

        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 option
        option(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 main

import (
	"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 Server
	s := grpc.NewServer()
	//组成HelloService服务
	hello_http.RegisterHelloHTTPServer(s, HelloService)

	log.Println("Listen on " + addr)
	s.Serve(ls)
}

实现客户端

package main

import (
	"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 main

import (
	"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转grpc
	err := 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 main

import (
	"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转grpc
	if 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.Certificate
	pair, err := tls.X509KeyPair(cert, key)
	if err != nil {
		log.Println("X509KeyPair fail ", err)
		return nil
	}

	demoKeyPair = &pair
	return &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.coloradmin.cn/o/1826100.html

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

相关文章

消息群发工具制作的过程和需要用到的源代码!

在信息化快速发展的今天,消息群发工具因其高效、便捷的特点,在各个领域得到了广泛的应用,无论是企业营销、社交互动,还是日常通知,消息群发工具都发挥着不可替代的作用。 本文将详细介绍消息群发工具的制作过程&#…

[RL9] Rocky Linux 9.4 搭载 PG 16.1

副标题:Rocky Linux 9.4 升级实录,及 PG 16 相关内容 背景 Rocky Linux 9.4 (以下简称 RL) 于5月9日正式发布,本文记录了从 RL 9.3 升级到 9.4 的过程,以及升级前后的一些变化。 之前介绍过 RL 9 的相关内容,请戳&…

【C++】STL中stack、queue、deque的使用

前言:在前面我们学习了List的模拟实现与使用,今天我们进一步的来学习stack、queue、deque的使用方法,然后为后面的模拟实现做一下铺垫。 💖 博主CSDN主页:卫卫卫的个人主页 💞 👉 专栏分类:高质量&#xff…

liunx常见指令

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 目录 前言 二、安装环境 1.租借服务器 2.下载安装 XShell 3.使用xshll登录服务器 三、Linux基础命令 一、文件和命令 ​编辑1、cd 命令 2、pwd 命令 3、ls 命令 4、cp 命令 …

【three.js案例一】智慧星球

直接附上源码: import * as THREE from three; import { OrbitControls } from three/addons/controls/OrbitControls.js;//场景 const scene = new THREE.Scene();const geometry = new THREE.SphereGeometry(50,32,16);console.log(.postion,geometry.attributes.position)…

上网行为管理产品有哪些?好用的四款上网行为管理产品

上网行为管理产品是现代企业网络安全架构中的重要组成部分,它们旨在帮助企业有效监控、管理和控制员工的网络使用行为,确保网络资源的合理利用,保障信息安全,提升工作效率。 以安企神为例,我们将详细介绍它的主要功能…

python保存文件后打不开的原因是什么

引入数据集,奇怪的是怎么也打不开,显示不存在这个文件: 但是,我将文件改个名字,就打开了,难道csv的文件命名必须有一定合法性? import pandas users pandas.read_csv("H:\python\data an…

OpenDevin 环境配置及踩坑指南

不惧怕任何环境配置 首先 clone 项目,然后查看开发者文档:https://github.com/OpenDevin/OpenDevin/blob/main/Development.md make setup-config 自定义 LLM 配置 首先这个 devin 写的是支持自定义的 LLM 配置,并且提供了交互式命令供我们…

华为云计算和数通有什么用?大咖在这里为你讲解

网工这一职业的就业前景,是一直以来都被看好的。薪资水平普遍较高,随着经验的积累,薪资水平还会不断提升,职业发展路径也非常广阔。 谈到网工,就绕不开华为认证,华为认证作为网络工程师的一块金字招牌&…

mini web框架示例

web框架: 使用web框架专门负责处理用户的动态资源请求,这个web框架其实就是一个为web服务器提供服务的应用程序 什么是路由? 路由就是请求的url到处理函数的映射,也就是说提前把请求的URL和处理函数关联好 管理路由可以使用一个…

MySQL 中 Varchar(50) 和 varchar(500) 区别是什么?

一. 问题描述 我们在设计表结构的时候,设计规范里面有一条如下规则: 对于可变长度的字段,在满足条件的前提下,尽可能使用较短的变长字段长度。 为什么这么规定?我在网上查了一下,主要基于两个方面 基于存储空间的考…

Springboot实现微信小程序登录功能

目录 一 什么是微信登录功能 二 实现微信登录功能的整体逻辑 三 微信登录功能实现步骤 一 什么是微信登录功能 微信小程序登录功能一般用于开发微信小程序的时候,我们需要使用微信授权登录我们的微信小程序,本篇博客就微信小程序实现微信授权登录以及s…

Javaweb8 数据库Mybatis+JDBC

Mybatis Dao层,用于简化JDBC开发 1步中的实体类 int类型一般用Integer :如果用int类型 默认值为0,会影响数据的判断,用Integer默认值是null,不会给数据的判断造成干扰 2.在application .properties里配置数据库的链接信息-四要素 #驱动类名称 #URL #用…

stm32f103 HAL库 HC-SR04测距

目录 一、实现测距二、添加TIM3控制LED根据距离以不同频率闪烁三、观察时序Modebus协议12路超声波雷达设计方案1. 系统架构设计2. 硬件设计3. 软件设计4. 通信协议设计5. 用户接口6. 安全和冗余7. 测试和验证8. 电源和物理封装9. 文档和支持 一、实现测距 配置时钟 配置定时器…

0-1 构建用户画像数仓

目录 前言 一、用户画像概述 1.1 用户画像 1.2 用户标签 1.3 用户群组 二、建设标签和标签体系 2.1 标签体系 2.1.1 统计类标签 2.1.2 规则类标签 2.1.3 机器学习挖掘类标签 2.2 标签建设流程 2.2.1 需求收集与分析 2.2.2 产出标签需求文档 2.2.3 标签的开发 H…

docker安装消息队列mq中的rabbit服务

在现代化的分布式系统中,消息队列(Message Queue, MQ)已经成为了一种不可或缺的组件。RabbitMQ作为一款高性能、开源的消息队列软件,因其高可用性、可扩展性和易用性而广受欢迎。本文将详细介绍如何在Docker环境中安装RabbitMQ服务…

04-appium元素定位

一、uiautomatorviewer uiautomatorviewer是Android-SDK自带的一个元素定位工具,非常简单好用,可以使用该工具查看app应用中的元素属性,帮助我们在代码中进行元素定位。 1)使用步骤 在Android-SDK安装目录中进入到 android-sdk-…

【TB作品】STM32F102C8T6单片机,PWM发生器

硬件: STM32F102C8T6核心板,按键,0.96 OLED显示屏。 软件: 1、硬件启动触发单片机输出PWM,未触发之前PWM输出为低电平。 2、按键修改PWM的变化模式、变化时间长度、占空比上下限。 3、输出的PWM是固定的10kHZ的。 4、变…

Redis的安装(linux、docker)与其基本的api使用

一、Redis简介 Redis是一个开源的,使用 C 编写,高性能的Key-Value的NoSQL数据库。 SQL :关系型数据库,例如:MySQL,Oracle等等NoSQL :Not Only SQL 不仅仅是SQL,表示是非关系型数据库…

【漏洞复现】东胜物流软件 GetProParentModuTreeList SQL注入漏洞

0x01 产品简介 东胜物流软件是青岛东胜伟业软件有限公司-款集订单管理、仓库管理、运输管理等多种功能于一体的物流管理软件。该公司初创于2004年11月(前身为青岛景宏物流信息技术有限公司),专注于航运物流相关环节的产品和服务。东胜物流信息管理系统货代版采用MS…