gRPC之gRPC Gateway

这篇具有很好参考价值的文章主要介绍了gRPC之gRPC Gateway。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1、gRPC Gateway

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

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

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

gRPC之gRPC Gateway,gRPC,gateway

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的内容:文章来源地址https://www.toymoban.com/news/detail-723758.html

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

到了这里,关于gRPC之gRPC Gateway的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • golang工程— grpc-gateway健康检查和跨域配置

    grpc健康检查网关跨域配置 grpc健康检查 grpc健康检查使用 服务端配置 网关配置 gateway.go 如果网关中设置了拦截器之类的进行鉴权判断,可以通过 FullName=\\\"/grpc.health.v1.Health/Check\\\" 去忽略鉴权 监控检查默认请求 http://localhost:8081/healthz grpc网关跨域配置 采用中间件的形式封装httph

    2024年02月06日
    浏览(38)
  • golang工程——grpc-gateway 转发http header中自定义字段到grpc上下文元数据

    http header 转发到 grpc上下文 grpc网关可以将请求体内容转发到grpc对应消息中。那如何获取http header头中的信息,本文将介绍如何将http header转发到grpc上下文并采用拦截器,获取http header中的内容。 有些http header中的内置字段是会转发的比如Authorization,但是狠多自定义字段是转发

    2024年02月08日
    浏览(33)
  • 简单使用gateway,以及gateway所需要的依赖

    先声明,使用gateway需要有一定的SpringCloud的基础,再来使用时会看的很明白,使用前需要先开nacos服务,,确定无误。   下面直接开始 首先我们需要两个原来通信的模块,这里分别是service-const与service-provider表示,结构如下  接下来   yml的配置与controller编写这里就不细述了

    2024年02月09日
    浏览(28)
  • 手动搭建gateway,项目集成gateway实现Token效果

    现在想要进行token校验,故引入gateway服务。 首先阅读官网,知道概念:Gateway官网详解 看整体的目录结构 这份配置是用于Spring Cloud Gateway的配置文件,用于构建API网关。让我来解释一下每个部分的含义: 服务器配置: 服务器监听端口为8088。 Servlet的上下文路径设置为\\\"/api\\\",

    2024年02月15日
    浏览(38)
  • 两天学会微服务网关Gateway-Gateway路由规则

    锋哥原创的微服务网关Gateway视频教程: Gateway微服务网关视频教程(无废话版)_哔哩哔哩_bilibili Gateway微服务网关视频教程(无废话版)共计17条视频,包括:1_Gateway简介、2_Gateway工作原理、3_Gateway HelloWorld快速入门等,UP主更多精彩视频,请关注UP账号。 https://www.bilibili.com

    2024年03月18日
    浏览(46)
  • 【深入解析spring cloud gateway】05 gateway请求转发实验

    三个工程: eureka-server eureka-client gateway 实验目的:通过网关访问对应的微服务:eureka-client。gateway和eureka-client注册到eureka-server上 eureka-server略 eureka-client application.yml 提供一个接口 pom.xml application.yml 定义一个filter用于去掉路径中的/gateway 自定义一个GlobalFilter,用于去掉路径

    2024年02月10日
    浏览(49)
  • 【深入解析spring cloud gateway】06 gateway源码简要分析

    上一节做了一个很简单的示例,微服务通过注册到eureka上,然后网关通过服务发现访问到对应的微服务。本节将简单地对整个gateway请求转发过程做一个简单的分析。 主要流程: Gateway Client向 Spring Cloud Gateway 发送请求 请求首先会被HttpWebHandlerAdapter 进行提取组装成网关上下文

    2024年02月10日
    浏览(39)
  • 502 bad gateway什么意思502 bad gateway问题解决办法

    502 bad gateway 是一种常见互联网连接错误,大部分情况就是打不开页面,连接不上网络,访问服务器挂了等问题,下面来看看具体解决方法,希望能够帮助你解决问题。 502 bad gateway什么意思 简单说就是服务器没有收到回应,一般情况下是对方服务器出了问题。 502 bad gatewa

    2024年02月11日
    浏览(39)
  • flink 1.18 sql gateway /sql gateway jdbc

    一 sql gateway 注意 之所以直接启动gateway 能知道yarn session 主要还是隐藏的配置文件,但是配置文件可以被覆盖,多个session 保留最新的applicationid 1 安装flink (略) 2 启动sql-gatway(sql-gateway 通过官网介绍只能运行在session 任务中) 2-1 启动gateway 之前先启动一个flink session ./bin/yarn-

    2024年01月16日
    浏览(48)
  • springcloud3 GateWay章节-Eureka+gateway动态路由负载均衡1

    gateway相当于所有服务的门户,将客户端请求与服务端应用相分离,客户端请求通过gateway后由定义的路由和断言进行转发,路由代表需要转发请求的地址,断言相当于请求这些地址时所满足的条件,只有同时符合路由和断言才给予转发 gateWay是微服务的API网关,能够实现服务的

    2024年02月12日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包