go micro - yaokun123/php-wiki GitHub Wiki
Go-micro
一、为什么不继续使用 gRPC ?
1、管理麻烦
在客户端代码(consignment-cli/cli.go)中,我们手动指定了服务端的地址和端口,在本地修改不是很麻烦。但在生产环境中,各服务可能不在同一台主机上(分布式独立运行),其中任一服务重新部署后 IP 或运行的端口发生变化,其他服务将无法再调用它。如果你有很多个服务,彼此指定 IP 和端口来相互调用,那管理起来很麻烦
2、服务发现
为解决服务间的调用问题,服务发现(service discovery)出现了,它作为一个注册中心会记录每个微服务的 IP 和端口,各微服务上线时会在它那注册,下线时会注销,其他服务可通过名字或 ID 找到该服务类比门面模式。
为不重复造轮子,我们直接使用实现了服务注册的 go-micro 框架。
二、安装
go get -u -v github.com/micro/micro
go get -u -v github.com/micro/go-micro
go get -u -v github.com/micro/protoc-gen-micro
使用 go-micro 自己的编译器插件,在 Makefile 中修改 protoc 命令:
build:
protoc --micro_out=$(GOPATH)/src/shippy/micro-service --go_out=$(GOPATH)/src/shippy/micro-service/proto/consignment proto/consignment/consignment.proto
//注意--micro_out的路径
执行make build之后发现目录proto/consignment下多了consignment.pb.go和consignment.pb.micro.go两个文件
三、服务端使用 go-micro
修改服务端代码 main.go 使用 go-micro
package main
import (
pb "shippy/micro-service/proto/consignment"
"context"
"log"
"github.com/micro/go-micro"
)
//
// 仓库接口
//
type IRepository interface {
Create(consignment *pb.Consignment) (*pb.Consignment, error) // 存放新货物
GetAll() []*pb.Consignment // 获取仓库中所有的货物
}
//
// 我们存放多批货物的仓库,实现了 IRepository 接口
//
type Repository struct {
consignments []*pb.Consignment
}
func (repo *Repository) Create(consignment *pb.Consignment) (*pb.Consignment, error) {
repo.consignments = append(repo.consignments, consignment)
return consignment, nil
}
func (repo *Repository) GetAll() []*pb.Consignment {
return repo.consignments
}
//
// 定义微服务
//
type service struct {
// *pb.UnimplementedShippingServiceServer
repo Repository
}
//
// service 实现 consignment.pb.go 中的 ShippingServiceHandler 接口
// 使 service 作为 gRPC 的服务端
//
// 托运新的货物
//func (s *service) CreateConsignment(ctx context.Context, req *pb.Consignment) (*pb.Response, error) {
func (s *service) CreateConsignment(ctx context.Context, req *pb.Consignment, resp *pb.Response) error {
// 接收承运的货物
consignment, err := s.repo.Create(req)
if err != nil {
return err
}
resp := &pb.Response{Created: true, Consignment: consignment}
return nil
}
// 获取目前所有托运的货物
//func (s *service) GetConsignments(ctx context.Context, req *pb.GetRequest) (*pb.Response, error) {
func (s *service) GetConsignments(ctx context.Context, req *pb.GetRequest, resp *pb.Response) error {
allConsignments := s.repo.GetAll()
resp := &pb.Response{Consignments: allConsignments}
return nil
}
func main() {
server := micro.NewService(
// 必须和 consignment.proto 中的 package 一致
micro.Name("__"),
micro.Version("latest"),
)
// 解析命令行参数
server.Init()
repo := Repository{}
pb.RegisterShippingServiceHandler(server.Server(), &service{repo})
if err := server.Run(); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
go-micro 的实现相比 gRPC 有 3 个主要的变化:
1、创建 RPC 服务器的流程
micro.NewService(...Option) 简化了微服务的注册流程, micro.Run() 也简化了 gRPCServer.Serve(),不再需要手动创建 TCP 连接并监听。
2、微服务的 interface
注意看代码发现 go-micro 将响应参数 Response 提到了入参,只返回 error,整合了 gRPC 的 四种运行模式
3、运行地址的管理
服务的监听端口没有在代码中写死,go-mirco 会自动使用系统或命令行中变量 MICRO_SERVER_ADDRESS 的地址
四、对应更新一下 Makefile
run:
docker run -p 50051:50051 \
-e MICRO_SERVER_ADDRESS=:50051 \
-e MICRO_REGISTRY=mdns \
consignment-service
-e 选项用于设置镜像中的环境变量,其中 MICRO_REGISTRY=mdns 会使 go-micro 在本地使用 mdns 多播作为服务发现的中间层。在生产环境一般会使用 Consul 或 Etcd 代替 mdns 做服务发现,在本地开发先一切从简。
现在执行 make build && make run,你的 consignment-service 就有服务发现的功能了。