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 就有服务发现的功能了。