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等工具来管理服务。
还没有评论,快来发表第一个评论吧