GRPC基础使用-golang

GRPC基本使用

GRPC介绍

GRPC使用protocol buffer进行接口定义和底层信息交换。客户端可以直接调用服务端的方法,就像调用本地方法一样。在服务端实现这个接口并且运行一个grpc服务来处理客户端的IO调用。客户端有一个存根(stub),它提供与服务端相同的方法。

官网grpc介绍:https://grpc.io/docs/what-is-grpc/introduction/

安装

先决条件:

  • Golang:服务端语言
  • Protocol buffer:数据序列化工具,类似于json
  • Go-Plugin: 生成服务端,客户端代码的插件

Protocal Buffer

$ apt-get update 
$ apt-get install -y protobuf-compiler
$ protoc --version  # Ensure compiler version is 3+

安装教程:https://grpc.io/docs/protoc-installation/

GO-Plugin

$ go install google.golang.org/protobuf/cmd/[email protected]
$ go install google.golang.org/grpc/cmd/[email protected]
$ export PATH="$PATH:$(go env GOPATH)/bin"

安装教程:https://grpc.io/docs/languages/go/quickstart/

go install 是安装编译。
go get是远端拉取,并编译。
如果用go get拉取并没有在GOPATH/bin目录下编译出protoc-gen-go.exe/protoc-gen-go-grpc.exe。
那么需要重新go install编译一次

基础教程

定义.proto文件

  • 定义一个服务
  • 定义服务内提供的方法
  • 定义方法的入出参
syntax="proto3";

// package  proto文件的包名(与go无关)  生成的语言类放在什么包里面  这个package必须紧跟着syntax="proto3"
package pbfiles.user;

import "google/api/annotations.proto";

// 定义生成的pb文件go的package名
option go_package = "/proto";


// 定义service服务 会翻译为UserServiceClient 和 userServiceClient 和对外暴露的 NewUserServiceClient供客户端调用
// 对外暴露GetUserList 和 GetUser方法
// 定义服务
service UserSerivce{
  //定义获取用户列表信息方法
  rpc GetUserList(UserRequest) returns (UserListReply) {}
  // 定义获取用户信息方法
  rpc GetUser(UserRequest) returns (UserReply) {
    option (google.api.http) = {
      get: "/helloworld/{name}"
    };
  }
  // http

}

// 入参
message UserRequest{
  string class_id= 1;
  int32  phone= 2;
}

// GetUserList出参
message UserListReply{
  repeated User user = 1;
}

// GetUser出参
message UserReply{
  User user = 1;
}

message User {
  string name = 1;
  string phone = 2;
  string class_id = 3;
  string sex = 4;
  int32 id = 5;
}

服务端文件目录

代码地址:https://github.com/qiushenglei/grpc-server-golang

│  go.mod
│  go.sum
│  main.go
│
├─.idea
│      .gitignore
│      etcd_new.iml
│      modules.xml
│      workspace.xml
│
├─models
│      user.go
│
├─proto
│      user.pb.go
│      user.proto
│      user_grpc.pb.go
├─third_party
|		google
│
└─service
        userService.go

生成服务端 grpc和结构体 go代码

$ protoc -I ./third_party \
	--proto_path=proto--go_out=. \
	--go_opt=paths=source_relative \
    --go-grpc_out=. --go-grpc_opt=paths=source_relative \
    user.proto
  • I :proto引入三方包路径
  • proto_path:指定proto文件目录,可省略
  • go_out:pb文件输出目录
  • go-grpc_out:grpc service注册go文件输出目录
  • 最后一行是proto文件名

生成文件:

  • .pb.go:包含用于序列化检索请求响应消息类型的所有协议缓冲区代码

  • grpc.pb.go:包含

    • 客户端使用服务定义的方法调用的接口类型(stub存根)

    • 服务端要实现的接口

实例教程:https://grpc.io/docs/languages/go/basics/

服务监听

  • 实现protocol buffer定义的接口
  • tcp监听端口
实现接口
// sevice/userSerivce.go
package service

import (
	"context"
	"etcd_new/models"
	"etcd_new/proto"
	"fmt"
	"github.com/go-xorm/xorm"
	"xorm.io/core"
)

// 实现.proto的service定义的接口
type UserService struct {
	// 业务侧是查询sql,所以提前注册到rpc服务内
	Engine *xorm.Engine
	// grpc规范必须要引入,否则我无法实现这个接口,因为她有个must小写方法
	proto.UnimplementedUserSerivceServer
}

// 数据库操作引擎
func NewMysqlEngine() *xorm.Engine {
	engine, err := xorm.NewEngine("mysql", "user:password@tcp(ip:port)/elmcms?charset=utf8")
	if err != nil {
		panic(err)
	}

	engine.ShowSQL(true)
	engine.Logger().SetLevel(core.LOG_DEBUG)
	engine.SetMaxOpenConns(10)

	//返回引擎
	return engine
}

// 实现proto文件定义的rpc暴露的方法
func (s *UserService) GetUserList(ctx context.Context, request *proto.UserRequest) (*proto.UserListReply, error) {
	var userList []models.User
	err := s.Engine.Where("id > ?", 0).Find(&userList)
	if err != nil {
		fmt.Println(err.Error())
	}
	fmt.Println(userList)
	fmt.Println("===========================================")

	//因为返回的是[]*proto.User
	res := make([]*proto.User, len(userList))
	//遍历数据库查询结果 然后重新塞入到新的切片当中去
	for _, u := range userList {
		res = append(res, &proto.User{
			Name:  u.Name,
			Phone: u.Phone,
			Sex:   u.Sex,
			Id:    u.ID,
		})
	}

	return &proto.UserListReply{User: res}, err
}

func (s *UserService) GetUser(ctx context.Context, request *proto.UserRequest) (*proto.UserReply, error) {
	// 业务code
	return &proto.UserReply{}, nil
}

调用到的其他文件

// models/user.go
package models

type User struct {
	Name  string `xorm:"varchar(255)" json:"name"`
	Phone string `xorm:"varchar(255)" json:"phone"`
	Sex   string `xorm:"varchar(255)" json:"sex"`
	ID    int32 `xorm:"int" json:"id"`
}

监听端口
  1. tcp监听端口
  2. new grpc结构体
  3. new 本地服务结构体
  4. 本地服务 和 grpc绑定
//main.go
package main

import (
	"etcd_new/proto"
	"etcd_new/service"
	"flag"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"log"
	"net"
)

var tls = flag.Int("t", 0, "need tls")
var certFile = flag.String("cert", "", "cert file path")
var keyFile = flag.String("key", "", "key file path")

func main () {

	flag.Parse()

	//1.监听
	lis, _ := net.Listen("tcp", "ip:port")

	// 证书验证,没有的话可以省略
	var opts []grpc.ServerOption
	if *tls == 1 {
		if *certFile == "" {
			*certFile = "x509/server_cert.pem"
		}
		if *keyFile == "" {
			*keyFile = "x509/server_key.pem"
		}
		creds, err := credentials.NewServerTLSFromFile(*certFile, *keyFile)
		if err != nil {
			log.Fatalf("Failed to generate credentials %v", err)
		}
		opts = []grpc.ServerOption{grpc.Creds(creds)}
	}

	// 起GRPC服务

	// 2.生成 grpc server结构体
	grpcServer := grpc.NewServer(opts...)

	// 3.生成Instance结构体
	ins := &service.UserService{
		Engine: service.NewMysqlEngine(),
	}

	// 给rpc服务注册,这个方法是proto-go生成的
	proto.RegisterUserSerivceServer(grpcServer, ins)

	// 开启服务,  这个地方我们不需要for死循环 grpc当中会自动帮助我们实时监听
	grpcServer.Serve(lis)
}

客户端文件目录

代码地址: https://github.com/qiushenglei/grpc-client-go

│  go.mod
│  go.sum
│  main.go
│
├─.idea
│      .gitignore
│      modules.xml
│      test.iml
│      workspace.xml
│
├─proto
│      user.pb.go
│      user.proto
│      user_grpc.pb.go

客户端访问

  • 执行protoc ,生成grpc文件
package main

import (
	"fmt"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"testproj/proto"
)

func main() {
	//grpc链接客户端
	conn, err := grpc.Dial("127.0.0.1:8899", grpc.WithInsecure())
	if err != nil {
		fmt.Println(err)
		return
	}
	//程序完成不要忘记关闭链接
	defer conn.Close()
	//真正去链接服务端,pb-grpc生成的
	client := proto.NewUserSerivceClient(conn)
	//调用服务端 第一个是上线文  第二个是要传入的参数  传入的是结构体  返回的也是结构体
	ret, err := client.GetUser(context.Background(), &proto.UserRequest{})
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(ret.User.Id)
	fmt.Println(ret.User.Name)
}

你可能感兴趣的:(golang,rpc)