grpc 实现grpc gateway(window环境)

官网:https://grpc-ecosystem.github.io/grpc-gateway/
github:https://github.com/grpc-ecosystem/grpc-gateway

grpc gateway的原理官网有介绍。总结一下就是:
gRPC-Gateway帮助你同时以gRPC和RESTful风格提供你的API。grpc-gateway旨在为您的gRPC服务提供HTTP+JSON接口。 做法就是:在服务中附加HTTP语义的少量配置就是使用该库生成反向代理所需的全部内容。客户端的restful Api请求通过反向代理自动转换成grpc可识别的请求去调用grpc服务端。

具体实现步骤:

1、新增echo.proto文件,定义好http接口:

syntax = "proto3";

package pb;
option go_package = "go_grpc/grpc_gateway/proto/hello"; 
// 导入google/api/annotations.proto
import "google/api/annotations.proto"; //🌙这里在goland会提示:proto/hello/echo.proto: Import "google/api/annotations.proto" was not found or had errors.神奇的是:当我换vscode编辑器就没有这个问题。


service EchoService {
  rpc Echo(StringMessage) returns (StringMessage) {
//    // 这里添加了google.api.http注释
    option (google.api.http) = {
      post: "/v1/echo"
      body: "*"
    };
  }
}

message StringMessage {
  string value = 1;
}

2、生成对应的pb文件:

2.1 普通方式(推荐第二种方式:Buf工具)
执行目录是在项目根目录下,即go_grpc目录下:

protoc -I=proto --grpc-gateway_out=./proto --grpc-gateway_opt=paths=source_relative --go_out=proto  --go_opt=paths=source_relative --go-grpc_out=proto --go-grpc_opt=paths=source_relative .\proto\hello\echo.proto

在这里插入图片描述

执行完毕后,就在如上图所示,在proto/hello目录下生成三个文件:
echo.pb.go
echo_grpc.pb.go
echo.pb.gw.go

如果会提示:
在这里插入图片描述
需要在本地目录引入下面两个文件(目录保持一致),点击下面链接进行下载:
https://github.com/googleapis/googleapis/tree/master/google/api
在这里插入图片描述

2.2 使用buf工具生成pb文件

grpc-gateway项目的readme文件有推荐一款工具:Buf(官网链接),这个工具使我们不需要手动下载google/api/annotations.proto这些依赖包到我们的项目中。而且对于生成pb文件也会更加简单方便。

在这里插入图片描述
安装Buf:
https://github.com/bufbuild/buf/releases
点击选择window版本下载,下载后的文件名是buf-Windows-x86_64.exe。将其重命名为buf.exe。然后放到$GOPATH/bin目录下,其实就是和protoc.exe同个目录。
在这里插入图片描述

Buf的用法:

1. 先在proto/hello目录下(即放proto文件的目录下),初始化buf:

go mod init

执行该命令后会生成buf.yaml。

然后在buf.yaml引入第三方依赖:

version: v1
breaking:
  use:
    - FILE
lint:
  use:
    - DEFAULT
name: buf.build/mygrpc/gateway
deps:
  - buf.build/googleapis/googleapis
  - buf.build/grpc-ecosystem/grpc-gateway

其中,name参数第一个值buf.build是固定的,后面两个单词可以自定义。
加入依赖后,记得执行 buf mod update -v 更新一下。(每次对buf.yaml修改都要执行update)

2. 定义buf.gen.yaml文件

version: v1
plugins:
  - plugin: go
    out: ./
    opt:
      - paths=source_relative
# protoc_path可以指定不同版本的protoc工具,但是我在window测试无效。
#    protoc_path: /c/Users/Administrator/go/bin/protoc_new_version/protoc
  - plugin: go-grpc
    out: ./
    opt:
      - paths=source_relative
  - plugin: grpc-gateway
    out: ./
    opt:
      - paths=source_relative
#    protoc_path: /c/Users/Administrator/go/bin/protoc_new_version
  1. 执行下面命令,生成pb文件
 buf generate -v --debug

执行成功后,会在proto文件的目录下生成三个pb文件:
在这里插入图片描述

遇到的bug:

因为我的电脑的protoc.exe和protoc-gen-go.exe用的是老版本,导致我生成的echo.pb.gw.go文件报错(这里应该是echo.pb.go文件有误影响到了gw文件):
在这里插入图片描述

所以去gopath/bin目录下,把旧版本先备份后移除,然后下载比较新的版本。再执行puf generate就能生成正确无误的文件。
在这里插入图片描述

接下来就是测试http post请求是否成功。
这里我们需要启动rpc服务,再启动http服务。

package main

import (
	"context"
	"fmt"
	"log"
	"net"
	"net/http"

	pb "go_grpc/grpc_gateway/proto/hello"

	"github.com/golang/glog"
	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

const port = ":4399"

type EchoService struct {
	*pb.UnimplementedEchoServiceServer
}

func NewEchoService() *EchoService {
	return &EchoService{}
}

func (c *EchoService) Echo(ctx context.Context, msg *pb.StringMessage) (*pb.StringMessage, error) {
	reply := fmt.Sprintf("我收到你的信息了,你发送的信息是:%s", msg)
	return &pb.StringMessage{Value: reply}, nil
}

//启动 http server
func runHttpService() error {
	ctx := context.Background()
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()

	// Register gRPC server endpoint
	// Note: Make sure the gRPC server is running properly and accessible
	mux := runtime.NewServeMux()
	opts := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())}
	err := pb.RegisterEchoServiceHandlerFromEndpoint(ctx, mux, "localhost:4399", opts)
	if err != nil {
		return err
	}
	println("http service start!")
	// Start HTTP server (and proxy calls to gRPC server endpoint)
	return http.ListenAndServe(":8081", mux)
}

//启动 rpc service
func runRpcService() error {
	listener, err := net.Listen("tcp", port)
	if err != nil {
		return err
	}

	srv := NewEchoService()
	rpcServer := grpc.NewServer()
	pb.RegisterEchoServiceServer(rpcServer, srv)
	log.Println("start gRPC listen on port " + port)
	return rpcServer.Serve(listener)
}

func main() {
	//flag.Parse()
	defer glog.Flush()

	//得用一个协程启动rpc服务,不能先启动rpc服务,再启动http服务。不然启动rpc服务后会阻塞住
	go func() {
		if err := runRpcService(); err != nil {
			fmt.Printf("runRpcService err:%#v\n", err)
			glog.Fatal(err)
		}
	}()
	println("cowboy very busy")
	//再启动http service
	if err := runHttpService(); err != nil {
		fmt.Printf("runHttpService err:%#v\n", err)
		glog.Fatal(err)
	}
}


注意这里,rpc服务用的是4399端口,http服务用的是8081端口。

接下来启动service/main.go。然后用goland快速建立一个post请求:
File => New => HTTP Request
在这里插入图片描述
我们也用rpc客户端进行测试调用:

package main

import (
	"context"
	"fmt"
	pb "go_grpc/grpc_gateway/proto/hello"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"log"
)

const (
	address = "localhost:4399"
)

func main() {
	conn, err := grpc.Dial(address, grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		log.Println("did not connect.", err)
		return
	}
	defer conn.Close()

	client := pb.NewEchoServiceClient(conn)
	ctx := context.Background()

	req := &pb.StringMessage{Value: "Crazy Thursday"}
	reply, err := client.Echo(ctx, req)
	if err != nil {
		log.Println("fail:", err)
		return
	}
	fmt.Printf("reply:%#v\n", reply)
}

同样,请求也是成功:
在这里插入图片描述
说明grpc网关实现成功了!!!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Linux】linux下使用命令修改jar包内某一个文件中的内容并重新运行jar程序

linux下使用命令修改jar包内某一个文件中的内容并重新运行jar程序 一、背景描述二、vi命令编辑三、启动程序四、拓展--启动脚本 一、背景描述 需求:发现线上的 iotp-irsb-server-v1.0.0.2.jar 包中配置文件的日志级别配置错误,需要在线修改jar包中文件的…

MFC的定义和实际操作方法

我是荔园微风,作为一名在IT界整整25年的老兵,今天从另一个角度来看一下MFC。 完整的应用一般由四个类组成:CWinApp应用类,CFrameWnd窗口框架类,CDocument文档类,CView视类 过程:CWinApp创建CF…

ubuntu iptables开机自启动

一、配置ubuntu路由转发 用在一台电脑有多个网卡的情形下,一个网卡5网段、一个网卡8网段,8网段是网络出口,所以5网段的设备需要入网的话,要路由转发。 sudo iptables -t nat -A POSTROUTING -s 192.168.5.0/24 -j SNAT --to-sou…

STM32速成笔记—概述

文章目录 前言一、专栏简介二、前期准备三、编程规范以及程序架构简介1. 编程规范2. 程序架构 四、STM32F103ZET6简介五、程序模板六、ST-Link调试6.1 硬件连接6.2 Keil配置6.3 下载调试 前言 本人技术菜鸟一枚,2022年大学毕业,大学加入老师实验室&#…

chatgpt赋能python:如何在Python中创建模块:完整指南

如何在Python中创建模块:完整指南 如果你是一位Python开发者,你肯定需要用到模块。模块使得代码更容易组织和管理,并且可以复用许多代码片段, 提高代码的可重用性。在Python中,模块是一组相关函数,方法和变…

oracle expdp导致system表空间满

今天下午,项目经理反馈有套11204版本数据库无法使用了,立刻登录检查环境发现SYSTEM表空间使用率99.99%了 TABLESPACE_NAME MAXSIZE_MB ACTUALSIZE_MB USED_MB FREESPACE_MB SPACE USAGE ----------------- ---------- ------------- ---------- …

Trace32 SRST和TRST、system.attach 和 system.up的区别

目录 TRST-Resets the JTAG TAP controller and the CPU internal debug logic SRST- Resets the CPU core and peripherals SYStem.Mode Down SYStem.Mode Nodebug SYStem.Mode Prepare SYStem.Mode Go SYStem.Mode Attach SYStem.Mode StandBy SYStem.Mode Up 下图为…

ProGuard 进阶系列(二)配置解析

书接上文,从开源库中把代码下载到本地后,就可以在 IDE 中进行运行了。从 main 方法入手,可以看到 ProGuard 执行的第一步就是去解析参数。本文的内容主要分析源码中我们配置的规则解析的实现。 在上一篇文章末尾,在 IDE 中&#x…

大数据Doris(三十七):Spark Load导入HDFS数据

文章目录 Spark Load导入HDFS数据 一、准备HDFS数据 二、创建Doris表 三、创建Spark Load导入任务

【Reids】搭建主从集群以及主从数据同步原理

目录 一、搭建主从集群 1、介绍 2、搭建 二、数据同步原理 1、全量同步 2、主节点如何判断是不是第一次连接 3、增量同步 4、优化主从数据同步 一、搭建主从集群 1、介绍 单节点的Redis并发能力存在上限,要提高并发能力就需要搭建主从集群,实现…

【LLM GPT】李宏毅大型语言模型课程

目录 1 概述1.1 发展历程1.2 预训练监督学习预训练的好处 1.3 增强式学习1.4 对训练数据的记忆1.5 更新参数1.6 AI内容检测1.7 保护隐私1.8 gpt和bert穷人怎么用gpt 2 生成式模型2.1 生成方式2.1.1 各个击破 Autoregressive2.1.2 一次到位 Non-autoregressive2.1.3 两者结合 2.…

RabbitMQ虚拟主机无法启动的原因和解决方案

RabbitMQ虚拟主机无法启动的原因和解决方案 摘要: RabbitMQ是一个广泛使用的开源消息代理系统,但在使用过程中可能会遇到虚拟主机无法启动的问题。本文将探讨可能导致该问题的原因,并提供相应的解决方案,以帮助读者解决RabbitMQ虚…

第五章 模型篇: 模型保存与加载

参考教程: https://pytorch.org/tutorials/beginner/basics/saveloadrun_tutorial.html 文章目录 pytorch中的保存与加载torch.save()torch.load()代码示例 模型的保存与加载保存 state_dict()nn.Module().load_state_dict()加载模型参数保存模型本身加载模型本身 c…

K8s 中 port, targetPort, NodePort的区别

看1个例子: 我们用下面命令去创建1个pod2, 里面运行的是1个nginx kubectl create deployment pod2 --imagenginx当这个POD被创建后, 其实并不能被外部访问, 因为端口映射并没有完成. 我们用下面这个命令去创建1个svc &#xff…

chatgpt赋能python:Python怎样让画笔变粗

Python怎样让画笔变粗 Python是一门强大的编程语言,不仅适用于数据分析和机器学习等领域,也可以用来进行图像处理。在Python中,我们可以使用Pillow库来进行图像操作。在本篇文章中,我们将介绍如何使用Python和Pillow来让画笔变粗…

vue2_markdown的内容目录生成

文章目录 ⭐前言⭐引入vue-markdown💖 全局配置💖 渲染选项💖 取出markdown的标题层级 ⭐结束 ⭐前言 大家好!我是yma16,本文分享在vue2的markdown文本内容渲染和目录生成 背景: 优化个人博客功能&#xf…

delphi的ARM架构支持与System.Win.WinRT库

delphi的ARM架构支持与System.Win.WinRT库 目录 delphi的ARM架构支持与System.Win.WinRT库 一、WinRT 二、delphi的System.Win.WinRT库 2.1、支持ARM芯片指令 2.2、基于WinRT技术的特点 2.3、所以使用默认库而未经转化的服务端应用并不支持ARM架构服务器 2.4、对默认库…

【Linux】初步认识Linux系统

Linux 操作系统 主要作用是管理好硬件设备,并为用户和应用程序提供一个简单的接口,以便于使用。 作为中间人,连接硬件和软件 常见操作系统 桌面操作系统 WindowsmacOsLinux 服务器操作系统 LinuxWindows Server 嵌入式操作系统 Linux …

深度学习图像分类、目标检测、图像分割源码小项目

​demo仓库和视频演示: 银色子弹zg的个人空间-银色子弹zg个人主页-哔哩哔哩视频 卷积网路CNN分类的模型一般使用包括alexnet、DenseNet、DLA、GoogleNet、Mobilenet、ResNet、ResNeXt、ShuffleNet、VGG、EfficientNet和Swin transformer等10多种模型 目标检测包括…

Java关键词synchronized

目录 一、通过卖票系统观察多线程的安全隐患 二、synchronized的基本知识 1.使用synchronized的原因 2.synchronized的作用 3.synchronized的基本格式 a.synchronized加在方法名前 b.synchronized用在方法中 4. Java锁机制 5.synchronized注意事项 三、使用synchronize…