gRPC之gRPC Gateway

news2024/11/26 8:24:14

1、gRPC Gateway

etcd3 API全面升级为gRPC后,同时要提供REST API服务,维护两个版本的服务显然不太合理,所以

grpc-gateway 诞生了。通过protobuf的自定义option实现了一个网关,服务端同时开启gRPC和HTTP服务,

HTTP服务接收客户端请求后转换为grpc请求数据,获取响应后转为json数据返回给客户端。结构如图:

在这里插入图片描述

grpc-gateway地址:https://github.com/grpc-ecosystem/grpc-gateway

1.1 安装grpc-gateway

$ go get github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway@v1.16.0
$ go install github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway@v1.16.0

1.2 proto编写

这里用到了google官方Api中的两个proto描述文件,直接拷贝不要做修改,里面定义了protocol buffer扩展的

HTTP option,为grpc的http转换提供支持。

annotations.proto文件的内容:

// ./proto/google/api/annotations.proto
syntax = "proto3";

package google.api;

option go_package = "google/api;google_api";

import "google/api/http.proto";
import "google/protobuf/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;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;
}

编写自定义的proto描述文件hello_http.proto

// ./proto/hello_http/hello_http.proto
syntax = "proto3";
package hello_http;
option go_package = "./hello_http;hello_http";

import "google/api/annotations.proto";

// 定义Hello服务
service HelloHTTP {
    // 定义SayHello方法
    rpc SayHello(HelloHTTPRequest) returns (HelloHTTPResponse) {
        // http option
        option (google.api.http) = {
            post: "/example/echo"
            body: "*"
        };
    }
}

// HelloRequest 请求结构
message HelloHTTPRequest {
    string name = 1;
}

// HelloResponse 响应结构
message HelloHTTPResponse {
    string message = 1;
}

这里在原来的SayHello方法定义中增加了http optionPOST方式,路由为/example/echo

1.3 编译proto

$ cd proto

# 编译google.api
$ protoc -I . --go_out=plugins=grpc:. google/api/*.proto

# 编译hello_http.proto
$ protoc -I . --go_out=plugins=grpc:. hello_http/*.proto

# 编译hello_http.proto gateway
$ protoc --grpc-gateway_out=logtostderr=true:. hello_http/hello_http.proto

注意这里需要编译google/api中的两个proto文件,最后使用grpc-gateway编译生成hello_http_pb.gw.go

件,这个文件就是用来做协议转换的,查看文件可以看到里面生成的http handler,处理proto文件中定义的路由

example/echo接收POST参数,调用HelloHTTP服务的客户端请求grpc服务并响应结果。

1.4 实现服务端

server.go的内容:

package main

import (
	"context"
	"fmt"
	"net"
	// 引入编译生成的包
	pb "demo/proto/hello_http"
	"google.golang.org/grpc"
	"log"
)

const (
	// Address gRPC服务地址
	Address = "127.0.0.1:50053"
)

// 定义helloService并实现约定的接口
type helloService struct{}

// HelloService Hello服务
var HelloService = helloService{}

// SayHello 实现Hello服务接口
func (h helloService) SayHello(ctx context.Context, in *pb.HelloHTTPRequest) (*pb.HelloHTTPResponse, error) {
	resp := new(pb.HelloHTTPResponse)
	resp.Message = fmt.Sprintf("Hello %s.", in.Name)
	return resp, nil
}

func main() {
	listen, err := net.Listen("tcp", Address)
	if err != nil {
		log.Fatalf("Failed to listen: %v", err)
	}
	// 实例化grpc Server
	s := grpc.NewServer()
	// 注册HelloService
	pb.RegisterHelloHTTPServer(s, HelloService)
	log.Println("Listen on " + Address)
	s.Serve(listen)
}

1.5 客户端实现

client.go的内容:

package main

import (
	"context"
	pb "demo/proto/hello_http"
	"google.golang.org/grpc"
	"log"
)

const (
	// Address gRPC服务地址
	Address = "127.0.0.1:50053"
)

func main() {
	// 连接
	conn, err := grpc.Dial(Address, grpc.WithInsecure())
	if err != nil {
		log.Fatalln(err)
	}
	defer conn.Close()
	// 初始化客户端
	c := pb.NewHelloHTTPClient(conn)
	// 调用方法
	req := &pb.HelloHTTPRequest{Name: "gRPC"}
	res, err := c.SayHello(context.Background(), req)
	if err != nil {
		log.Fatalln(err)
	}
	log.Println(res.Message)
}

1.6 http server

server_http.go的内容:

package main

import (
	"fmt"
	"github.com/grpc-ecosystem/grpc-gateway/runtime"
	gw "demo/proto/hello_http"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"log"
	"net/http"
)

func main() {
	ctx := context.Background()
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()
	// grpc服务地址
	endpoint := "127.0.0.1:50053"
	mux := runtime.NewServeMux()
	opts := []grpc.DialOption{grpc.WithInsecure()}
	// HTTP转grpc
	err := gw.RegisterHelloHTTPHandlerFromEndpoint(ctx, mux, endpoint, opts)
	if err != nil {
		log.Fatalf("Register handler err:%v\n", err)
	}
	log.Println("HTTP Listen on 8080")
	http.ListenAndServe(":8080", mux)
}

就是这么简单。开启了一个http server,收到请求后根据路由转发请求到对应的RPC接口获得结果。grpc-

gateway做的事情就是帮我们自动生成了转换过程的实现。

1.7 测试

依次开启gRPC服务和HTTP服务端:

[root@zsx demo]# go run server.go
2023/02/12 09:38:52 Listen on 127.0.0.1:50053
[root@zsx demo]# go run server_http.go
2023/02/12 09:39:07 HTTP Listen on 8080

调用grpc客户端:

[root@zsx demo]# go run client.go
2023/02/12 09:39:37 Hello gRPC.
# 发送 HTTP 请求
[root@zsx demo]# curl -X POST -k http://localhost:8080/example/echo -d "{\"name\":\"gRPC-HTTP\"}"
{"message":"Hello gRPC-HTTP."}
# 项目结构
$ tree demo
demo
├── client.go
├── go.mod
├── go.sum
├── proto
│   ├── google # googleApi http-proto定义
│   │   └── api
│   │       ├── annotations.pb.go
│   │       ├── annotations.proto
│   │       ├── http.pb.go
│   │       └── http.proto
│   └── hello_http
│       ├── hello_http.pb.go
│       ├── hello_http.pb.gw.go # gateway编译后文件
│       └── hello_http.proto
├── server.go # gRPC服务端
└── server_http.go # HTTP服务端

4 directories, 12 files

1.8 升级版服务端(gRPC转换HTTP)

上面的使用方式已经实现了我们最初的需求,grpc-gateway 项目中提供的示例也是这种使用方式,这样后台需

要开启两个服务两个端口。其实我们也可以只开启一个服务,同时提供http和gRPC调用方式。

新建一个项目,基于上面的项目改造,客户端只要修改调用的proto包地址就可以了。

1.8.1 服务端实现

server.go的内容:

package main

import (
	"crypto/tls"
	"github.com/grpc-ecosystem/grpc-gateway/runtime"
	pb "demo/proto/hello_http"
	"golang.org/x/net/context"
	"golang.org/x/net/http2"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"log"
	"io/ioutil"
	"net"
	"net/http"
	"strings"
)

// 定义helloHTTPService并实现约定的接口
type helloHTTPService struct{}

// HelloHTTPService Hello HTTP服务
var HelloHTTPService = helloHTTPService{}

// SayHello 实现Hello服务接口
func (h helloHTTPService) SayHello(ctx context.Context, in *pb.HelloHTTPRequest) (*pb.HelloHTTPResponse, error) {
	resp := new(pb.HelloHTTPResponse)
	resp.Message = "Hello " + in.Name + "."
	return resp, nil
}

func main() {
	endpoint := "127.0.0.1:50052"
	conn, err := net.Listen("tcp", endpoint)
	if err != nil {
		log.Fatalf("TCP Listen err:%v\n", err)
	}
	// grpc tls server
	creds, err := credentials.NewServerTLSFromFile("./cert/server/server.pem", "./cert/server/server.key")
	if err != nil {
		log.Fatalf("Failed to create server TLS credentials %v", err)
	}
	grpcServer := grpc.NewServer(grpc.Creds(creds))
	pb.RegisterHelloHTTPServer(grpcServer, HelloHTTPService)
	// gw server
	ctx := context.Background()
	dcreds, err := credentials.NewClientTLSFromFile("./cert/server/server.pem", "test.example.com")
	if err != nil {
		log.Fatalf("Failed to create client TLS credentials %v", err)
	}
	dopts := []grpc.DialOption{grpc.WithTransportCredentials(dcreds)}
	gwmux := runtime.NewServeMux()
	if err = pb.RegisterHelloHTTPHandlerFromEndpoint(ctx, gwmux, endpoint, dopts); err != nil {
		log.Fatalf("Failed to register gw server: %v\n", err)
	}
	// http服务
	mux := http.NewServeMux()
	mux.Handle("/", gwmux)
	srv := &http.Server{
		Addr:      endpoint,
		Handler:   grpcHandlerFunc(grpcServer, mux),
		TLSConfig: getTLSConfig(),
	}
	log.Printf("gRPC and https listen on: %s\n", endpoint)
	if err = srv.Serve(tls.NewListener(conn, srv.TLSConfig)); err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
	return
}

func getTLSConfig() *tls.Config {
	cert, _ := ioutil.ReadFile("./cert/server/server.pem")
	key, _ := ioutil.ReadFile("./cert/server/server.key")
	var demoKeyPair *tls.Certificate
	pair, err := tls.X509KeyPair(cert, key)
	if err != nil {
		log.Fatalf("TLS KeyPair err: %v\n", err)
	}
	demoKeyPair = &pair
	return &tls.Config{
		Certificates: []tls.Certificate{*demoKeyPair},
		NextProtos:   []string{http2.NextProtoTLS}, // HTTP2 TLS支持
	}
}

// grpcHandlerFunc returns an http.Handler that delegates to grpcServer on incoming gRPC
// connections or otherHandler otherwise. Copied from cockroachdb.
func grpcHandlerFunc(grpcServer *grpc.Server, otherHandler http.Handler) http.Handler {
	if otherHandler == nil {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			grpcServer.ServeHTTP(w, r)
		})
	}
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.ProtoMajor == 2 && strings.Contains(r.Header.Get("Content-Type"), "application/grpc") {
			grpcServer.ServeHTTP(w, r)
		} else {
			otherHandler.ServeHTTP(w, r)
		}
	})
}

gRPC服务端接口的实现没有区别,重点在于HTTP服务的实现。gRPC是基于http2实现的,net/http包也实现了

http2,所以我们可以开启一个HTTP服务同时服务两个版本的协议,在注册http handler的时候,在方法

grpcHandlerFunc中检测请求头信息,决定是直接调用gRPC服务,还是使用gateway的HTTP服务。

net/http中对http2的支持要求开启https,所以这里要求使用https服务。

步骤:

  • 注册开启TLS的grpc服务
  • 注册开启TLS的gateway服务,地址指向grpc服务
  • 开启HTTP server
1.8.2 客户端实现

client.go的内容:

package main

import (
	"context"
	// 引入proto包
	pb "demo/proto/hello_http"
	"google.golang.org/grpc"
	// 引入grpc认证包
	"google.golang.org/grpc/credentials"
	"log"
)

const (
	// Address gRPC服务地址
	Address = "127.0.0.1:50052"
)

func main() {
	log.Println("客户端连接!")
	// TLS连接
	creds, err := credentials.NewClientTLSFromFile("./cert/server/server.pem", "test.example.com")
	if err != nil {
		log.Fatalf("Failed to create TLS credentials %v", err)
	}
	conn, err := grpc.Dial(Address, grpc.WithTransportCredentials(creds))
	if err != nil {
		log.Fatalln("err:", err)
	}
	defer conn.Close()
	// 初始化客户端
	c := pb.NewHelloHTTPClient(conn)
	// 调用方法
	req := &pb.HelloHTTPRequest{Name: "gRPC"}
	res, err := c.SayHello(context.Background(), req)
	if err != nil {
		log.Fatalln(err)
	}
	log.Println(res.Message)
}
1.8.3 测试
[root@zsx demo]# go run server.go
2023/02/12 09:57:44 gRPC and https listen on: 127.0.0.1:50052

[root@zsx demo]# go run  client.go
2023/02/12 09:59:46 客户端连接!
2023/02/12 09:59:46 Hello gRPC.
# 发送 HTTP 请求
[root@zsx demo]# curl -X POST -k https://localhost:50052/example/echo -d "{\"name\":\"gRPC-HTTP\"}"
{"message":"Hello gRPC-HTTP."}
# 项目结构
$ tree demo/
demo/
├── cert
│   ├── ca.crt
│   ├── ca.csr
│   ├── ca.key
│   ├── ca.srl
│   ├── client
│   │   ├── client.csr
│   │   ├── client.key
│   │   └── client.pem
│   ├── openssl.cnf
│   └── server
│       ├── server.csr
│       ├── server.key
│       └── server.pem
├── client.go
├── go.mod
├── go.sum
├── proto
│   ├── google
│   │   └── api
│   │       ├── annotations.pb.go
│   │       ├── annotations.proto
│   │       ├── http.pb.go
│   │       └── http.proto
│   └── hello_http
│       ├── hello_http.pb.go
│       ├── hello_http.pb.gw.go
│       └── hello_http.proto
└── server.go

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

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

相关文章

10月14日,每日信息差

今天是2023年10月14日,以下是为您准备的8条信息差 第一、中国石油摘得日本碳信用实货交易首单。据了解,日本交易所集团旗下的东京证券交易所11日宣布,交易二氧化碳排放量的“碳信用市场”正式开始运营 第二、前三季度全国铁路投产新线1402公…

解决git在window11操作很慢,占用很大cpu的问题

【git在window11操作很慢,占用很大cpu,最后也执行失败】 在谷歌输入:git very slow in window 11。通过下面链接终于找到了解决方案: https://www.reddit.com/r/vscode/comments/sulebx/slow_git_in_wsl_after_updating_to_window…

【算法优选】 前缀和专题——壹

文章目录 😎前言🎄[前缀和](https://www.nowcoder.com/practice/acead2f4c28c401889915da98ecdc6bf?tpId230&tqId2021480&ru/exam/oj&qru/ta/dynamic-programming/question-ranking&sourceUrl/exam/oj?page1&tab%25E7%25AE%2597%2…

Go语言入门心法(一)

Go语言入门心法(一) Go语言入门心法(二): 结构体 Go语言入门心法(三): 接口 一: go语言中变量认知 go语言中变量的定义: (要想飞|先会走)||(翻身仗|抹遗憾 ) |(二八定律)(先量变)|(再质变)||&#x…

JVM基础:初识JVM

IDE:IntelliJ IDEA 2022.1.3 x64 操作系统:win10 x64 位 家庭版 文章目录 一、JVM是什么?二、JVM有哪些功能?2.1 解释和运行2.2 内存管理2.3 即时编译 三、有哪些常见的JVM?3.1 常见JVM3.2 Java虚拟机规范3.3 HotSpot的…

工业网关它的功能是什么

随着工业4.0的到来,工业网关在工业自动化领域中的作用越来越重要。工业网关是一种连接不同工业设备的网络设备,它能够实现不同设备之间的通信和数据传输。本文将以HiWoo Box为例,介绍工业网关的主要功能和应用场景。 一、工业网关的主要功能…

推荐几款简单易用的协作化项目管理工具

您是否正在寻找一种有效且简单的项目管理工具来帮助您与团队成员协作?项目管理工具在当今的商业世界中已经变得必不可少,因为它们帮助团队保持组织和生产力。找到合适的工具是困难的,因为有太多的选择。有些工具是为特定类型的项目设计的,而…

zabbix触发器与动作

一、触发器(Trigger) 1、概念: 在 Zabbix 中,触发器用于监测 Zabbix 监控系统中的各种指标和条件,并在特定条件满足时触发警报。(触发器用于定义监控项的报警阈值) 2、触发器对象&#xff1a…

CCF CSP认证 历年题目自练Day31

题目一 试题编号: 202206-1 试题名称: 归一化处理 时间限制: 500ms 内存限制: 512.0MB 题目背景 在机器学习中,对数据进行归一化处理是一种常用的技术。 将数据从各种各样分布调整为平均值为 0、方差为 1的标准分布&a…

Windows服务器监控工具

随着Windows服务器成为大多数网络不可或缺的一部分,一些关键业务功能永远依赖于它们的正常运行时间。其可用性和性能受到打击可能会对这些功能产生不利影响,进而极大地影响收入。 由于这些情况,通过主动衡量其性能并使用有效的Windows服务器…

html进阶语法

html进阶 列表、表格、表单 目标:掌握嵌套关系标签的写法,使用列表标签布局网页 01-列表 作用:布局内容排列整齐的区域。 列表分类:无序列表、有序列表、定义列表。 无序列表 作用:布局排列整齐的不需要规定顺序的…

21面向对象描述器

目录 1、什么是描述器? 1、原始的代码可以理解成为这样: 2、增加解释器可以改成如下,解释器就是集增删改查为一体的一个小的property 有一点需要注意的地方是:property里面内置的参数不是get_age()就是不用调用。 3、装饰器可…

【从零开始学习Redis | 第一篇】快速了解Redis

前言: 本篇对于Redis的讲解可以让我们简单的了解什么是Redis以及他的简单应用。主要还是因为我在学习苍穹外卖的时候,用到了这个知识点,而在平时的各种学习中,对于Redis的大名也是早有耳闻,因此今天来简单的介绍一下re…

基于动物迁徙优化的BP神经网络(分类应用) - 附代码

基于动物迁徙优化的BP神经网络(分类应用) - 附代码 文章目录 基于动物迁徙优化的BP神经网络(分类应用) - 附代码1.鸢尾花iris数据介绍2.数据集整理3.动物迁徙优化BP神经网络3.1 BP神经网络参数设置3.2 动物迁徙算法应用 4.测试结果…

推荐八个大学搜题软件和学习工具哪个好用且免费,一起对比看看

以下分享的软件提供了各种实用的功能,如数学公式计算、语文阅读辅助等,让大学生们在学习过程中更加高效和便利。 1.九超查题 这是一个老公众号了,我身边的很多朋友都在用,支持超新星、学习强国、知到、智慧树和各类专业网课题目…

PCB板的元素组成

PCB板是电子工艺一道重要的步骤,市面上几乎所有的电子产品的主板组成都是PCB板。 那正常一块PCB板上有哪些元素呢?正常一般会包括边框,过孔,通孔,铺铜等等。 焊盘: 就是用于焊接元器件,IC等引脚…

NSSCTF做题(9)

[GDOUCTF 2023]<ez_ze> 看见输入框而且有提示说是ssti注入 输入{{7*7}} 试试&#xff0c;发现报错 输入{%%}发现了是jinja2模板 找到关键函数 Python SSTI利用jinja过滤器进行Bypass ph0ebuss Blog 原理见这篇文章&#xff0c;这里直接给出payload {%set ninedict(aaa…

电子电器架构——基于Adaptive AUTOSAR的电子电器架构简析

基于Adaptive AUTOSAR的电子电器架构简析 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明…

Jetpack:007-Kotlin中的Button

文章目录 1. 概念介绍2. 使用方法2.1 Button2.2 IconButton2.3 ElevatedButton2.4 OutlinedButton2.5 TextButton2.6 FloatingActionButton 3. 示例代码4. 内容总结 我们在上一章回中介绍了Jetpack中输入框相关的内容&#xff0c;本章回中将要介绍 Button。闲话休提&#xff0…

微信小程序入门讲解【超详细】

一. 微信小程序简介 1.1 什么是小程序 2017年度百度百科十大热词之一 微信小程序&#xff08;wei xin xiao cheng xu&#xff09;&#xff0c;简称小程序&#xff0c;英文名Mini Program&#xff0c;是一种不需要下载安装即可使用的应用( 张小龙对其的定义是无需安装&#xf…