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是否注册成功
-
启动客户端
来源:oschina
链接:https://my.oschina.net/u/4386603/blog/4273510