首页
登录 | 注册

【微服务记录】用go-micro编写微服务

1 用工具micro快速生成服务模板

go-micro提供了一个命令行工具micro

dotzdeMacBook-Pro-2:foo dotz$ micro
NAME:
   micro - A microservice toolkit

USAGE:
   micro [global options] command [command options] [arguments...]
   
VERSION:
   1.0.0
   
COMMANDS:
    api		Run the api gateway
    bot		Run the chatops bot
    cli		Run the interactive CLI
    registry	Query registry
    call	Call a service
    stream	Create a service stream
    publish	Publish a message to a topic
    health	Query the health of a service
    stats	Query the stats of a service
    list	List items in registry
    register	Register an item in the registry
    deregister	Deregister an item in the registry
    get		Get item from registry
    proxy	Run the service proxy
    service	Run a micro service
    new		Create a service template
    web		Run the web dashboard

GLOBAL OPTIONS:
   --client 									Client for go-micro; rpc [$MICRO_CLIENT]
   --client_request_timeo

使用该工具的new命令我们快速生成一个微服务项目

dotzdeMacBook-Pro-2:foo dotz$ micro new foo

【微服务记录】用go-micro编写微服务

生成模板后,我们需要将Protobuf原型文件编译下,需要注意的是插件要使用micro(如果使用过gRPC可以类比)

dotzdeMacBook-Pro-2:foo dotz$ protoc  --go_out=plugins=micro:. proto/example/*.proto

可以直接运行make build,这样直接生成接口文件,并完成项目编译

dotzdeMacBook-Pro-2:foo dotz$ make build
protoc --proto_path=/opt/goHome/src:. --micro_out=. --go_out=. proto/example/example.proto
go build -o foo-srv main.go plugin.go

然后运行项目,现在go-micro默认使用mdns插件进行服务注册和发现,可以传入参数,使用consul作为服务发现和注册插件

dotzdeMacBook-Pro-2:foo dotz$ ./foo-srv --registry=consul
2019/03/27 16:16:25 Transport [http] Listening on [::]:64352
2019/03/27 16:16:25 Broker [http] Connected to [::]:64353
2019/03/27 16:16:25 Registry [consul] Registering node: go.micro.srv.foo-d9ceb8a5-9a3c-4e5e-9533-124402568a3b

我们访问https://127.0.0.1:8500,可以发现我们的服务已经注册到consul

【微服务记录】用go-micro编写微服务

2 服务编写

2.1 初始化一个服务

	// 初始化服务
	service := micro.NewService(
		micro.Name("go.micro.srv.foo"),
		micro.Version("latest"),
	)

	// 包含默认选项,和参数解析
	service.Init()

初始化服务可以传入选项,如服务的名字,版本等,所有的选项定义在一个接口中:

type Options struct {
    Broker    broker.Broker
    Cmd       cmd.Cmd
    Client    client.Client
    Server    server.Server
    Registry  registry.Registry
    Transport transport.Transport

    // Before and After funcs
    BeforeStart []func() error
    BeforeStop  []func() error
    AfterStart  []func() error
    AfterStop   []func() error

    // Other options for implementations of the interface
    // can be stored in a context
    Context context.Context
}

定义了服务之后,需要调用服务的Init函数初始化,主要是设置一些默认选项,和解析参数,如传入--registry=consul,会解析到consul注册服务选项。

2.2 定义API

交互基于Protobuf协议,方法跟gRPC是类似的,首先要定义接口原型(包含方法约定和消息约定),这里的示例定义了三个方法Call,Stream和PingPong

syntax = "proto3";

package go.micro.srv.foo;

service Example {
	rpc Call(Request) returns (Response) {}
	rpc Stream(StreamingRequest) returns (stream StreamingResponse) {}
	rpc PingPong(stream Ping) returns (stream Pong) {}
}

message Message {
	string say = 1;
}

message Request {
	string name = 1;
}

message Response {
	string msg = 1;
}

message StreamingRequest {
	int64 count = 1;
}

message StreamingResponse {
	int64 count = 1;
}

message Ping {
	int64 stroke = 1;
}

message Pong {
	int64 stroke = 1;
}

经过命令操作,从原型文件生成我们所需的接口代码文件

dotzdeMacBook-Pro-2:foo dotz$ protoc  --go_out=plugins=micro:. proto/example/*.proto

接着我们需要实现预先定义的接口(protoc命令不是帮我们实现方法,只是将我们的接口规范信息转换为Protobuf格式),形式跟gRPC是一样的

func(ctx context.Context, req interface{}, rsp interface{}) error

下面是具体实现

package handler

import (
	"context"

	"github.com/micro/go-log"

	example "foo/proto/example"
)

type Example struct{}

// Call is a single request handler called via client.Call or the generated client code
func (e *Example) Call(ctx context.Context, req *example.Request, rsp *example.Response) error {
	log.Log("Received Example.Call request")
	rsp.Msg = "Hello " + req.Name
	return nil
}

// Stream is a server side stream handler called via client.Stream or the generated client code
func (e *Example) Stream(ctx context.Context, req *example.StreamingRequest, stream example.Example_StreamStream) error {
	log.Logf("Received Example.Stream request with count: %d", req.Count)

	for i := 0; i < int(req.Count); i++ {
		log.Logf("Responding: %d", i)
		if err := stream.Send(&example.StreamingResponse{
			Count: int64(i),
		}); err != nil {
			return err
		}
	}

	return nil
}

// PingPong is a bidirectional stream handler called via client.Stream or the generated client code
func (e *Example) PingPong(ctx context.Context, stream example.Example_PingPongStream) error {
	for {
		req, err := stream.Recv()
		if err != nil {
			return err
		}
		log.Logf("Got ping %v", req.Stroke)
		if err := stream.Send(&example.Pong{Stroke: req.Stroke}); err != nil {
			return err
		}
	}
}

接着就是将实现的接口注册为RPC方法

	//注册RPC方法
	example.RegisterExampleHandler(service.Server(), new(handler.Example))

2.3 运行服务监听

	// 运行服务
	if err := service.Run(); err != nil {
		log.Fatal(err)
	}

3 编写客户端

package main

import (
	"context"
	"fmt"

	micro "github.com/micro/go-micro"
	example "foo/proto/example"
)


func main() {
	// Create a new service. Optionally include some options here.
	service := micro.NewService(micro.Name("greeter.client"))
	service.Init()

	// Create new greeter client
	client := example.NewExampleService("go.micro.srv.foo", service.Client())

	// Call the greeter
	rsp, err := client.Call(context.TODO(), &example.Request{Name: "John"})
	if err != nil {
		fmt.Println(err)
	}

	// Print response
	fmt.Println(rsp.GetMsg())
}

运行输出

dotzdeMacBook-Pro-2:client dotz$ go run client.go --registry=consul
Hello John

 



2020 jeepxie.net webmaster#jeepxie.net
10 q. 0.009 s.
京ICP备10005923号