grpc使用

佐手、 提交于 2020-08-15 13:25:15

1、准备工作

  • 获取google.golang.org/grpc包
go get -u google.golang.org/grpc
  • 安装protobuf工具
brew install protobuf
  • 获取github.com/golang/protobuf/protoc-gen-go包
go get -u github.com/golang/protobuf/protoc-gen-go
  • 安装consul
brew install consul
  • 后台启动consul
nohup consul agent -dev > consul-out.file 2>&1
  • 给grpc服务生成ssl证书,保存在keys文件内,将keys文件夹存放在server/目录下,取出keys文件夹中的server.crt,存放在client/目录下
openssl genrsa -out server.key 2048
openssl req -new -x509 -sha256 -key server.key  -out server.crt -days 36500  -subj /C=CN/ST=CQ/L=fanxp/O=cq/OU=bx/CN=go-grpc-test/emailAddress=xxx@gmail.com

2、代码开发

描述

​ 开发一个用户登录函数,输入用户名和密码,返回用户ID

项目目录
.
├── client
│   ├── go.mod
│   ├── go.sum
│   ├── keys
│   │   └── server.crt
│   ├── main.go
│   └── pbservices
│       └── user.pb.go
└── server
    ├── go.mod
    ├── go.sum
    ├── keys
    │   ├── server.crt
    │   └── server.key
    ├── main.go
    ├── pbfile
    │   └── user.proto
    ├── pbservices
    │   └── user.pb.go
    └── services
        └── user.go

服务端开发

  • 在server/pbfile包下定义user.proto
// proto 文件版本
syntax = "proto3";
// 生成文件的包名
package pbservices;

// 定义用户接口
service User {
	//用户登录函数
  rpc UserLogin(ReqUserLogin) returns (RspUserLogin);
}

// 定义登录函数输入参数
message ReqUserLogin {
  string Username = 1;
  string Password = 2;
}

// 定义登录函数返回参数
message RspUserLogin {
  string Id = 1;
}
  • 进入server/pbfile目录下使用protobuf工具生成user.pb.go,放在server/pbservices文件夹下
protoc --go_out=plugins=grpc:../pbservices/ user.proto
  • 在server/services目录下创建user.go文件,编写函数实现
// services/user.go
package services

import (
	"context"
	"fmt"
	uuid "github.com/satori/go.uuid"
	"grpc-server/pbservices"
)

type UserService struct {
}

func GenerateID() string {
	UUID := uuid.NewV4()
	return UUID.String()
}

/*
		输出输入的用户名和密码,返回随机生成的UUID
*/
func (service *UserService) UserLogin(context context.Context, request *pbservices.ReqUserLogin) (*pbservices.RspUserLogin, error) {
	username := request.Username
	password := request.Password
	fmt.Println(username, password)
	return &pbservices.RspUserLogin{Id: GenerateID()}, nil
}
  • 在server目录下创建main.go文件,编写主函数
package main

import (
	"fmt"
	consulapi "github.com/hashicorp/consul/api"
	"golang.org/x/net/trace"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/grpclog"
	"grpc-server/pbservices"
	"grpc-server/services"
	"log"
	"net"
	"net/http"
	"sync"
)

const (
	service     = "user"
	servicePort = 8081

	consulAddr = "127.0.0.1"
	consulPort = 8500

	tracePort = 50051
)

func startTrace() {
	trace.AuthRequest = func(req *http.Request) (any, sensitive bool) {
		return true, true
	}
	go http.ListenAndServe(fmt.Sprintf(":%d", tracePort), nil)
	grpclog.Info("Trace listen on ", tracePort)
}

func init() {
	grpc.EnableTracing = true
}

// 获取本机IP
func localIP() string {
	addrs, err := net.InterfaceAddrs()
	if err != nil {
		return ""
	}
	for _, address := range addrs {
		if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
			if ipnet.IP.To4() != nil {
				return ipnet.IP.String()
			}
		}
	}
	return ""
}

type ConsulServiceRegister struct {
	ServiceId string           `json:"service_id"`
	Agent     *consulapi.Agent `json:"agent"`
}

func (r *ConsulServiceRegister) registerConsul(consulAddr string, consulPort int, service, serviceAddr string, servicePort int, env string) {
	cfg := consulapi.DefaultConfig()
	cfg.Address = fmt.Sprintf("%v:%d", consulAddr, consulPort)

	client, err := consulapi.NewClient(cfg)
	if err != nil {
		log.Fatal(err.Error())
	}

	check := consulapi.AgentServiceCheck{
		TCP:      fmt.Sprintf("%v:%d", serviceAddr, servicePort),
		Interval: "10s",
		Timeout:  "5s",
		Notes:    "Consul check service health status.",
	}

	serviceId := fmt.Sprintf("%v-%v-%d", serviceAddr, service, servicePort)
	r.ServiceId = serviceId

	registration := &consulapi.AgentServiceRegistration{
		ID:      serviceId,
		Name:    service,
		Address: serviceAddr,
		Port:    servicePort,
		Tags:    []string{env},
		Check:   &check,
	}

	agent := client.Agent()
	if err := agent.ServiceRegister(registration); err != nil {
		log.Fatal(err.Error())
	}
	r.Agent = agent

	services, err := agent.Services()
	if err != nil {
		log.Fatalf("agent get services error: %v", err)
	}
	if _, ok := services[serviceId]; !ok {
		log.Fatalf("missing service: %v", serviceId)
	}
	log.Println("agent get services:", services)

	//健康检测点是否成功添加
	checks, err := agent.Checks()
	if err != nil {
		log.Fatalf("agent get checks err: %v", err)
	}
	if _, ok := checks["service:"+serviceId]; !ok {
		log.Fatalf("missing check: %v", serviceId)
	}
}

func main() {

	var wg sync.WaitGroup
	wg.Add(1)

	//开启grpc服务
	go func() {
		//服务端启用安全证书
		cred, err := credentials.NewServerTLSFromFile("./keys/server.crt", "./keys/server.key")
		if err != nil {
			log.Fatal(err)
		}

		//启用安全证书情况创建服务
		rpcserver := grpc.NewServer(grpc.Creds(cred))
		//普通情况创建服务
		//rpcserver := grpc.NewServer()

		// 注册
		pbservices.RegisterUserServer(rpcserver, &services.UserService{})

		// 启动监听
		lis, err := net.Listen("tcp", fmt.Sprintf("%v:%d", localIP(), servicePort))
		if err != nil {
			log.Fatal(err)
		}
		err = rpcserver.Serve(lis)
		if err != nil {
			log.Fatal(err)
		}
		defer wg.Done()
	}()

	// 服务注册到consul
	wg.Add(1)
	go func() {
		var register = &ConsulServiceRegister{}
		register.registerConsul(consulAddr, consulPort, service, localIP(), servicePort, "")
		defer wg.Done()
	}()

	// 开启grpc调用轨迹服务
	wg.Add(1)
	go func() {
		startTrace()
		defer wg.Done()
	}()

	wg.Wait()
}

客户端开发

package main

import (
	"context"
	"fmt"
	consulapi "github.com/hashicorp/consul/api"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"grpc-client/pbservices"
	"log"
)

const (
	service    = "user"
	consulAddr = "127.0.0.1"
	consulPort = 8500
)

func ConnectConsul(consulAddr string, consulPort int, service, env string) []*consulapi.ServiceEntry {
	var lastIndex uint64
	cfg := consulapi.DefaultConfig()
	cfg.Address = fmt.Sprintf("%v:%d", consulAddr, consulPort)

	client, err := consulapi.NewClient(cfg)
	if err != nil {
		log.Fatal(err.Error())
	}

	services, metaInfo, err := client.Health().Service(service, env, true, &consulapi.QueryOptions{
		WaitIndex: lastIndex,
	})
	if err != nil {
		log.Fatal(err.Error())
	}
	lastIndex = metaInfo.LastIndex

	return services
}

func main() {
	//从consul获取服务列表
	services := ConnectConsul(consulAddr, consulPort, service, "")
	if len(services) == 0 {
		fmt.Println("no host")
		return
	}

	//读取证书
	creds, err := credentials.NewClientTLSFromFile("./keys/server.crt", "go-grpc-test")
	if err != nil {
		log.Fatal(err)
	}
	//获取服务列表第一个服务并且连接
	addr := services[0]
	//启用安全证书情况连接服务
	conn, err := grpc.Dial(fmt.Sprintf("%v:%d", addr.Service.Address, addr.Service.Port),
		grpc.WithTransportCredentials(creds))
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()

	//普通情况连接服务
	//conn, err := grpc.Dial(fmt.Sprintf("%v:%d", addr.Service.Address, addr.Service.Port),
	//  grpc.WithInsecure())
	//if err != nil {
	//	log.Fatal(err)
	//}
	//defer conn.Close()

	//创建User连接客户端,调用UserLogin()函数
	client := pbservices.NewUserClient(conn)
	req := &pbservices.ReqUserLogin{
		Username: "123",
		Password: "234",
	}
	response, err := client.UserLogin(context.Background(), req)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(response.Id)
}

3、测试

  • 启动服务端

  • 查看consul是否注册成功

  • 启动客户端

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!