Golang - Go-Micro(一)

今天了解了下GO的微服务,看了下Go-micro,做下记录。

简单介绍

Micro是一个用来简化微服务开发的框架,提供了如下功能:

  • Go Micro - 基于Golang的插件式RPC框架,提供服务发现,客户端负载均衡,编码,同步和异步通讯功能。
  • API - API Gateway(API 网关), 用来提供处理http请求。可以作为一个http的反向代理或者翻译相关的http请求到RPC服务。
  • Sidecar - 用来接入其他语言编写的应用到Micro中。
  • Web - 提供一个web dashboard,并且可以为Micro应用提供反向代理。
  • CLI - 用来跟Micro服务交互的命令行工具。
  • Bot - 用它我们可以在我们的服务中与Slack, HipChat, XMPP通讯。

架构图:

安装依赖

1.安装 consul

由于Micro的服务发现并没有自己实现,仅仅是提供Plugin来接入第三方服务发现(consul, etcd), 默认使用的是consule 安装参考: consul installation doc

2.安装protobuf

1
go get github.com/micro/protobuf/{proto,protoc-gen-go}

发现无法用 protoc-gen-go 生成代码。所以我去下载了protobuf

这里我是MAC系统所以,下载对应的mac版本 protobuf

解压将bin目录下的protoc放到物理机/usr/local/sbin下面。

3.安装go-micro

1
go get github.com/micro/go-micro

4.安装micro

1
go get github.com/micro/micro

接下来我们简单使用micro来构建一个hello world 应用。

构建

1.创建一个proto文件

proto语法请参考 google protobuf

1
2
3
4
5
6
7
8
9
10
11
12
13
syntax = "proto3";

service HelloWorld {
rpc Hello(HelloRequest) returns (HelloResponse) {}
}

message HelloRequest {
string name = 1;
}

message HelloResponse {
string greeting = 2;
}

2.编译成go

1
protoc --go_out=plugins=micro:. hello_world.proto

生成文件hello_world.pb.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: hello_world.proto

/*
Package hello_world is a generated protocol buffer package.

It is generated from these files:
hello_world.proto

It has these top-level messages:
HelloRequest
HelloResponse
*/
package hello_world

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

import (
client "github.com/micro/go-micro/client"
server "github.com/micro/go-micro/server"
context "golang.org/x/net/context"
)

// 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.ProtoPackageIsVersion2 // please upgrade the proto package

type HelloRequest struct {
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
}

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

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

type HelloResponse struct {
Greeting string `protobuf:"bytes,2,opt,name=greeting" json:"greeting,omitempty"`
}

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

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

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

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

// Client API for HelloWorld service

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

type helloWorldClient struct {
c client.Client
serviceName string
}

func NewHelloWorldClient(serviceName string, c client.Client) HelloWorldClient {
if c == nil {
c = client.NewClient()
}
if len(serviceName) == 0 {
serviceName = "helloworld"
}
return &helloWorldClient{
c: c,
serviceName: serviceName,
}
}

func (c *helloWorldClient) Hello(ctx context.Context, in *HelloRequest, opts ...client.CallOption) (*HelloResponse, error) {
req := c.c.NewRequest(c.serviceName, "HelloWorld.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 HelloWorld service

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

func RegisterHelloWorldHandler(s server.Server, hdlr HelloWorldHandler, opts ...server.HandlerOption) {
s.Handle(s.NewHandler(&HelloWorld{hdlr}, opts...))
}

type HelloWorld struct {
HelloWorldHandler
}

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

func init() { proto.RegisterFile("hello_world.proto", fileDescriptor0) }

var fileDescriptor0 = []byte{
// 136 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0xcc, 0x48, 0xcd, 0xc9,
0xc9, 0x8f, 0x2f, 0xcf, 0x2f, 0xca, 0x49, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x57, 0x52, 0xe2,
0xe2, 0xf1, 0x00, 0x09, 0x06, 0xa5, 0x16, 0x96, 0xa6, 0x16, 0x97, 0x08, 0x09, 0x71, 0xb1, 0xe4,
0x25, 0xe6, 0xa6, 0x4a, 0x30, 0x2a, 0x30, 0x6a, 0x70, 0x06, 0x81, 0xd9, 0x4a, 0xda, 0x5c, 0xbc,
0x50, 0x35, 0xc5, 0x05, 0xf9, 0x79, 0xc5, 0xa9, 0x42, 0x52, 0x5c, 0x1c, 0xe9, 0x45, 0xa9, 0xa9,
0x25, 0x99, 0x79, 0xe9, 0x12, 0x4c, 0x60, 0x85, 0x70, 0xbe, 0x91, 0x19, 0x17, 0x17, 0x58, 0x71,
0x38, 0xc8, 0x12, 0x21, 0x0d, 0x2e, 0x56, 0x30, 0x4f, 0x88, 0x57, 0x0f, 0xd9, 0x1a, 0x29, 0x3e,
0x3d, 0x14, 0x13, 0x95, 0x18, 0x92, 0xd8, 0xc0, 0xee, 0x31, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff,
0x7e, 0xdc, 0x2e, 0x9b, 0xa4, 0x00, 0x00, 0x00,
}

3.生成服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package main

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

type HelloWorld struct{}

func (g *HelloWorld) Hello(ctx context.Context, req *hello_world.HelloRequest, rsp *hello_world.HelloResponse) error {
rsp.Greeting = "Hello World: " + req.Name
return nil
} // 实现hello_world service中Hello方法

func main() {
service := micro.NewService(
micro.Name("hello_world"), // 定义service的名称为hello_world
micro.Version("latest"),
micro.Metadata(map[string]string{
"type": "helloworld",
}),
)

service.Init() // 初始化service

hello_world.RegisterHelloWorldHandler(service.Server(), new(HelloWorld)) // 注册服务

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

4.客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package main

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

func main() {
service := micro.NewService(
micro.Name("hello_world"),
micro.Version("latest"),
micro.Metadata(map[string]string{
"type": "helloworld",
}),
)

service.Init()

greeter := hello_world.NewHelloWorldClient("hello_world", service.Client()) // 创建服务hello_world的client对象, 以便调用其中定义的RPC方法'Hello'

rsp, err := greeter.Hello(context.TODO(), &hello_world.HelloWorldRequest{Name: "Alice"}) // 传入HelloWorldRequest对象作为调用RPC方法的参数'Hello'
if err != nil {
fmt.Println(err)
return
}

fmt.Println(rsp.Greeting)
}

总结开发流程

公共开发模块

  • 创建消息protobuf文件, 在其中定义消息
  • 根据定义的protobuf文件,生成基于该语言的实现类

    服务端

  • 实现在消息中定义的RPC方法
  • 初始化一个新的Service对象
  • 注册服务
  • 运行服务

    客户端

  • 初始化一个新的Service对象
  • 定义请求消息
  • 传入请求消息调用服务端定义RPC方法
  • 解释响应消息

参考资料

0%