Gin框架系列教程(9)- gin+grpc,实现微服务数据交互


什么是rpc?

1、定义:

远程过程调用协议。和http请求类似,只不过协议不同,rpc使用的大多数为TCP,http请求使用的为HTTP协议。

可以简单的理解为一个节点调用另外一个节点。

2.组成:

  • 客户端:Client,服务调用方。
  • 客户端存根:Client Stub,存放服务端地址信息,将客户端的请求参数数据信息打包成网络消息,再通过网络传输发送给服务端
  • 网络传输模块:Network Service,底层传输,可以是 TCP 或 HTTP
  • 服务端存根:Server Stub,接收客户端发送过来的请求消息并进行解包,然后再调用本地服务进行处理。
  • 服务端等:server,服务的真正提供者。

3.执行过程:

  • Client 客户端通过本地调用的方式调用服务。
  • 客户端存根(Client Stub)接收到调用请求后负责将方法、入参等信息序列化(组装)成能够进行网络传输的消息体。
  • 客户端存根(Client Stub)找到远程的服务地址,并且将消息通过网络发送给服务端。
  • 网络传输
  • 服务端存根(Server Stub)收到消息后进行解码(反序列化操作)。
  • 服务端存根(Server Stub)根据解码结果调用本地的服务进行相关处理
  • 服务端(Server)本地服务业务处理。
  • 处理结果返回给服务端存根(Server Stub)。
  • 服务端存根(Server Stub)序列化结果。
  • 服务端存根(Server Stub)将结果通过网络发送至消费方。
  • 客户端存根(Client Stub)接收到消息,并进行解码(反序列化)。
  • 客户端得到最终结果。

什么是grpc

1、定义:

  • 是一个高性能、开源、通用的RPC框架。
  • gRPC中采用的是HTTP2协议
  • 谷歌的产品
  • gRPC默认使用protoBuf

2.组成:类似rpc

3.执行过程:类似rpc


安装

GRPC + protoc-gen-go

使用以下命令安装protoc-gen-go插件,它用于将protocol buffer文件生成Go代码

这里不做过多介绍,网上有很多教程。

go get -u google.golang.org/grpc
go get -u github.com/golang/protobuf/protoc-gen-go

安装完成,检查是否安装成功。


实际操作:我们接下来定义一个user服务,和一个article服务,然后用gin服务同时来连接两个服务,调用不同数据返回给接口。

我们先定义两个proto文件,然后生成,创建服务端,然后用客户端连接并获取数据。这是大概思路。

下面我们来简单实现一下代码:

1、定义proto文件并生成

定义user.proto

syntax = "proto3";

package userBuf;

option go_package = "./userBuf";

message UserAddReq {
    string name = 1;
    int64 age = 2;
    string sex = 3;
}

message UserAddResp {
    string msg = 1;
    string code = 2;
}

service UserService {
    rpc UserAdd (UserAddReq) returns (UserAddResp);
}

定义article.proto

syntax = "proto3";

package articleBuf;

option go_package = "./articleBuf";

message ArticleAddReq {
    string name = 1;
    int64 age = 2;
    string sex = 3;
}

message ArticleAddResp {
    string msg = 1;
    string code = 2;
}

service ArticleService {
    rpc ArticleAdd (ArticleAddReq) returns (ArticleAddResp);
}


生成protoBuf文件,命令:

protoc --go_out=. --go-grpc_out=. user.proto
protoc --go_out=. --go-grpc_out=. article.proto

2、创建grpc服务端

user.go

package main

import (
	"context"
	"fmt"
	"gprc_demo/user/userBuf"
	"net"

	"google.golang.org/grpc"
)

type UserServer struct {
	userBuf.UnimplementedUserServiceServer
}

func (s UserServer) UserAdd(ctx context.Context, in *userBuf.UserAddReq) (*userBuf.UserAddResp, error) {
	var aa userBuf.UserAddResp
	aa.Msg = in.Name
	aa.Code = "200"
	fmt.Println("user 进来了")
	return &aa, nil
}

func main() {
	// 创建grpc服务
	s := grpc.NewServer()
	userBuf.RegisterUserServiceServer(s, UserServer{})
	listen, err := net.Listen("tcp", "127.0.0.1:6001")
	fmt.Println("监听6001端口。。。")
	if err != nil {
		fmt.Println("网络错误")
	}
	s.Serve(listen) // grpc服务启动
}

article.go

package main

import (
	"context"
	"fmt"
	"gprc_demo/article/articleBuf"
	"net"

	"google.golang.org/grpc"
)

type ArticleServer struct {
	articleBuf.UnimplementedArticleServiceServer
}

func (s ArticleServer) ArticleAdd(ctx context.Context, in *articleBuf.ArticleAddReq) (*articleBuf.ArticleAddResp, error) {
	var aa articleBuf.ArticleAddResp
	aa.Msg = in.Name
	aa.Code = "200"
	fmt.Println("article 进来了")
	return &aa, nil
}

func main() {
	// 创建grpc服务
	s := grpc.NewServer()
	articleBuf.RegisterArticleServiceServer(s, ArticleServer{})
	listen, err := net.Listen("tcp", "127.0.0.1:6002")
	fmt.Println("监听6002端口。。。")
	if err != nil {
		fmt.Println("网络错误")
	}
	s.Serve(listen) // grpc服务启动
}


3、创建Gin服务端,调用数据

package main

import (
	"gprc_demo/article/articleBuf"
	"gprc_demo/user/userBuf"
	"net/http"

	"github.com/gin-gonic/gin"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

func main() {
	r := gin.Default()

	// 创建grpc连接,grpc客户端
	connUser, err := grpc.Dial("127.0.0.1:6001", grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		panic(err)
	}
	defer connUser.Close()
	userClient := userBuf.NewUserServiceClient(connUser)

	connArticle, err := grpc.Dial("127.0.0.1:6002", grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		panic(err)
	}
	defer connArticle.Close()
	articleClient := articleBuf.NewArticleServiceClient(connArticle)

	// 定义一个Gin路由
	r.POST("/hello", func(c *gin.Context) {
		name := "你好"
		name1 := "小明"

		// 调用gRPC服务
		userReq := &userBuf.UserAddReq{Name: name}
		userResp, userErr := userClient.UserAdd(c, userReq)

		articleRreq := &articleBuf.ArticleAddReq{Name: name1}
		articleResp, articleErr := articleClient.ArticleAdd(c, articleRreq)

		if userErr != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": userErr})
			return
		}
		if articleErr != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error2": articleErr})
			return
		}
		c.JSON(http.StatusOK, gin.H{
			"code":    200,
			"message": userResp.Msg + "," + articleResp.Msg,
		})
	})

	// 启动Gin服务器
	if err := r.Run(":6000"); err != nil {
		panic(err)
	}
}

最终我们的文件结构如图:


上面我们同时连接user服务和article服务,分别传入“你好”,“小明”,两个数据,然后调用服务返回的数据返回给接口,我们看下效果。


好了,数据测试没问题,当然我们用的是ip+端口直连,我们也可以用edcd,consul等工具来管理服务。

点赞1
点击评论0
收藏0
浏览 38
 

还没有评论,快来发表第一个评论吧

免责声明:凡在本网站出现的信息,均仅供参考。本网站将尽力确保所提供信息的准确性及可靠性,但不保证有关资料的准确性及可靠性,注册用户和一般页面游览者在使用前请进一步核实,并对任何自主决定的行为负责。本网站对有关资料所引致的错误、不确或遗漏,概不负任何法律责任(包括侵权责任、合同责任和其它责任)
*尊重作者,转载请注明出处!

创作内容

开启你的爱凌峰创作之旅

发布首篇内容,开通创作中心
快来成为爱凌峰创作者吧~

写文章

板块热门【Gin】