Golang 微服务框架——Micro的使用

1、简单介绍

 Go Micro是一个插件化的基础框架,基于此能够构建微服务。Micro的设计哲学是『可插拔』的插件化架构。在架构以外,它默认实现了consul做为服务发现,经过http进行通讯,经过protobuf和json进行编解码。html

2、示例

为了更为直观,先从示例开始。git

一、环境搭建(安装依赖)

(1)安装protobufgithub

具体内容参考:https://blog.csdn.net/weixin_42117918/article/details/88920221golang

(2)安装go-microjson

go get github.com/micro/go-micro

(3)安装microbash

go get github.com/micro/micro

(4)安装consul服务器

因为Micro的服务发现并无本身实现,仅仅是提供Plugin来接入第三方服务发现(consul, etcd), 默认使用的是consule,所以须要安装consul。(以Windows开发环境为例)架构

下载consul:https://www.consul.io/downloads.html框架

在安装的位置解压获得 consul.exe 文件(个人解压位置是:C:\consul)微服务

设置环境变量:

启动:

cmd 命令窗口执行:consul agent -dev

consul 自带 UI 界面,打开网址:http://localhost:8500 ,能够看到当前注册的服务界面

二、建立一个proto文件

我定义的文件名为:hello.proto

syntax = "proto3";
message HelloRequest {
    string name = 1;
}
message HelloResponse {
    string greeting = 2;
}
service Greeter {
    rpc Hello(HelloRequest) returns (HelloResponse) {}
}

三、生成  xx.pb.go、xx.micro.go文件

(1)命令及生成的代码以下:

protoc --go_out=plugins=micro:. hello.proto
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: hello.proto

package hello

import (
	fmt "fmt"
	proto "github.com/golang/protobuf/proto"
	math "math"
)

// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf

// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package

type HelloRequest struct {
	Name                 string   `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
	XXX_NoUnkeyedLiteral struct{} `json:"-"`
	XXX_unrecognized     []byte   `json:"-"`
	XXX_sizecache        int32    `json:"-"`
}

func (m *HelloRequest) Reset()         { *m = HelloRequest{} }
func (m *HelloRequest) String() string { return proto.CompactTextString(m) }
func (*HelloRequest) ProtoMessage()    {}
func (*HelloRequest) Descriptor() ([]byte, []int) {
	return fileDescriptor_61ef911816e0a8ce, []int{0}
}

func (m *HelloRequest) XXX_Unmarshal(b []byte) error {
	return xxx_messageInfo_HelloRequest.Unmarshal(m, b)
}
func (m *HelloRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
	return xxx_messageInfo_HelloRequest.Marshal(b, m, deterministic)
}
func (m *HelloRequest) XXX_Merge(src proto.Message) {
	xxx_messageInfo_HelloRequest.Merge(m, src)
}
func (m *HelloRequest) XXX_Size() int {
	return xxx_messageInfo_HelloRequest.Size(m)
}
func (m *HelloRequest) XXX_DiscardUnknown() {
	xxx_messageInfo_HelloRequest.DiscardUnknown(m)
}

var xxx_messageInfo_HelloRequest proto.InternalMessageInfo

func (m *HelloRequest) GetName() string {
	if m != nil {
		return m.Name
	}
	return ""
}

type HelloResponse struct {
	Greeting             string   `protobuf:"bytes,2,opt,name=greeting,proto3" json:"greeting,omitempty"`
	XXX_NoUnkeyedLiteral struct{} `json:"-"`
	XXX_unrecognized     []byte   `json:"-"`
	XXX_sizecache        int32    `json:"-"`
}

func (m *HelloResponse) Reset()         { *m = HelloResponse{} }
func (m *HelloResponse) String() string { return proto.CompactTextString(m) }
func (*HelloResponse) ProtoMessage()    {}
func (*HelloResponse) Descriptor() ([]byte, []int) {
	return fileDescriptor_61ef911816e0a8ce, []int{1}
}

func (m *HelloResponse) XXX_Unmarshal(b []byte) error {
	return xxx_messageInfo_HelloResponse.Unmarshal(m, b)
}
func (m *HelloResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
	return xxx_messageInfo_HelloResponse.Marshal(b, m, deterministic)
}
func (m *HelloResponse) XXX_Merge(src proto.Message) {
	xxx_messageInfo_HelloResponse.Merge(m, src)
}
func (m *HelloResponse) XXX_Size() int {
	return xxx_messageInfo_HelloResponse.Size(m)
}
func (m *HelloResponse) XXX_DiscardUnknown() {
	xxx_messageInfo_HelloResponse.DiscardUnknown(m)
}

var xxx_messageInfo_HelloResponse proto.InternalMessageInfo

func (m *HelloResponse) GetGreeting() string {
	if m != nil {
		return m.Greeting
	}
	return ""
}

func init() {
	proto.RegisterType((*HelloRequest)(nil), "HelloRequest")
	proto.RegisterType((*HelloResponse)(nil), "HelloResponse")
}

func init() { proto.RegisterFile("hello.proto", fileDescriptor_61ef911816e0a8ce) }

var fileDescriptor_61ef911816e0a8ce = []byte{
	// 130 bytes of a gzipped FileDescriptorProto
	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xce, 0x48, 0xcd, 0xc9,
	0xc9, 0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x57, 0x52, 0xe2, 0xe2, 0xf1, 0x00, 0x71, 0x83, 0x52,
	0x0b, 0x4b, 0x53, 0x8b, 0x4b, 0x84, 0x84, 0xb8, 0x58, 0xf2, 0x12, 0x73, 0x53, 0x25, 0x18, 0x15,
	0x18, 0x35, 0x38, 0x83, 0xc0, 0x6c, 0x25, 0x6d, 0x2e, 0x5e, 0xa8, 0x9a, 0xe2, 0x82, 0xfc, 0xbc,
	0xe2, 0x54, 0x21, 0x29, 0x2e, 0x8e, 0xf4, 0xa2, 0xd4, 0xd4, 0x92, 0xcc, 0xbc, 0x74, 0x09, 0x26,
	0xb0, 0x42, 0x38, 0xdf, 0xc8, 0x98, 0x8b, 0xdd, 0x1d, 0xc4, 0x4e, 0x2d, 0x12, 0xd2, 0xe0, 0x62,
	0x05, 0xeb, 0x13, 0xe2, 0xd5, 0x43, 0xb6, 0x43, 0x8a, 0x4f, 0x0f, 0xc5, 0x38, 0x25, 0x86, 0x24,
	0x36, 0xb0, 0x63, 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xf5, 0x83, 0xc3, 0x7d, 0x9b, 0x00,
	0x00, 0x00,
}

(2)命令及生成的代码以下:

protoc --proto_path=. --micro_out=. --go_out=. hello.proto
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: hello.proto

package hello

import (
	fmt "fmt"
	proto "github.com/golang/protobuf/proto"
	math "math"
)

import (
	context "context"
	client "github.com/micro/go-micro/client"
	server "github.com/micro/go-micro/server"
)

// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf

// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package

// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ client.Option
var _ server.Option

// Client API for Greeter service

type GreeterService interface {
	Hello(ctx context.Context, in *HelloRequest, opts ...client.CallOption) (*HelloResponse, error)
}

type greeterService struct {
	c    client.Client
	name string
}

func NewGreeterService(name string, c client.Client) GreeterService {
	if c == nil {
		c = client.NewClient()
	}
	if len(name) == 0 {
		name = "greeter"
	}
	return &greeterService{
		c:    c,
		name: name,
	}
}

func (c *greeterService) Hello(ctx context.Context, in *HelloRequest, opts ...client.CallOption) (*HelloResponse, error) {
	req := c.c.NewRequest(c.name, "Greeter.Hello", in)
	out := new(HelloResponse)
	err := c.c.Call(ctx, req, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

// Server API for Greeter service

type GreeterHandler interface {
	Hello(context.Context, *HelloRequest, *HelloResponse) error
}

func RegisterGreeterHandler(s server.Server, hdlr GreeterHandler, opts ...server.HandlerOption) error {
	type greeter interface {
		Hello(ctx context.Context, in *HelloRequest, out *HelloResponse) error
	}
	type Greeter struct {
		greeter
	}
	h := &greeterHandler{hdlr}
	return s.Handle(s.NewHandler(&Greeter{h}, opts...))
}

type greeterHandler struct {
	GreeterHandler
}

func (h *greeterHandler) Hello(ctx context.Context, in *HelloRequest, out *HelloResponse) error {
	return h.GreeterHandler.Hello(ctx, in, out)
}

生成的文件如上图所示。

四、服务端代码

package main

import (
	"fmt"
	"github.com/micro/go-micro"
	"golang.org/x/net/context"
)
import pb "MyMicro" //import proto生成的类

type Greeter struct {
}

/*
实现proto生成的hello.micro.go中的
type GreeterHandler interface {
	Hello(context.Context, *HelloRequest, *HelloResponse) error}
*/
func (g *Greeter) Hello(ctx context.Context, rep *pb.HelloRequest, rsp *pb.HelloResponse) error {
	rsp.Greeting = "Hello" + rep.Name
	//fmt.Printf()
	return nil
}
func main() {
	//新建一个服务
	service := micro.NewService(micro.Name("greeter"),
		micro.Version("latest"),
		micro.Metadata(map[string]string{"type": "hello world"}))
	service.Init()                                                   //初始化服务
	err := pb.RegisterGreeterHandler(service.Server(), new(Greeter)) //注册服务
	if err != nil {
		fmt.Println("注册服务出现了问题...", err)
		return
	}
	//运行服务
	if err := service.Run(); err != nil {
		fmt.Println("服务运行出现了错误:", err)
	}
}

五、客户端代码

package main

import (
	pb "MyMicro"
	"fmt"
	"github.com/micro/go-micro"
	"golang.org/x/net/context"
)

func main() {
	service := micro.NewService(micro.Name("greeter"),
		micro.Version("latest"),
		micro.Metadata(map[string]string{"type": "hello world"}))
	service.Init()
	greeter := pb.NewGreeterService("greeter", service.Client()) //调用proto生成的hello.micro.go中的NewGreeterService方法
	res := pb.HelloRequest{}
	res.Name = "jiangzhou"
	rsp, err := greeter.Hello(context.TODO(), &res) //Client API for Greeter service
	if err != nil {
		fmt.Println("请求服务出现了问题...", err)
		return
	}
	fmt.Println("服务返回的结果为:", rsp.Greeting)

}

六、运行

(1)启动consul:consul agent -dev(Windows命令行命令 )

(2)运行服务器端:

(3)运行客户端端:

3、原理

参考:http://www.javashuo.com/article/p-zjbowguz-kp.html