micro服务和go-micro框架的简单介绍和使用

893 阅读7分钟

go-micro框架和micro工具介绍

本文将为 [Gopher]介绍微服务go-micro框架最新安装和使用入门教程。踩过坑。。。

Micro 入门文档

Micro是一个专注于简化分布式系统开发的微服务生态系统。由开源库和工具组成。主要包含以下几种库:

  • micro: 一个包含传统入口点的微服务工具包:API网关,CLI,Slack Bot,Sidecar和Web UI。

  • go-micro: 用于编写微服务的可插入Go-RPC框架; 服务发现,客户端/服务器rpc,pub/sub等,是整个Micro的核心。

    默认使用mdns做服务发现,可以在插件中替换成consul,etcd,k8s等

    组播 广播

  • go-plugins: go-micro的插件,包括etcd,kubernetes(k8s),nats,rabbitmq,grpc等

其他各种库和服务可以在github.com/micro找到。

我们主要使用的框架也是go-micro,在使用之前我们先来了解一下服务发现是个什么东西?有什么作用?

Go-micro CLI脚手架安装

go-micro/cli: Go Micro command line interface (github.com)脚手架文档

Go Micro CLI 是用于开发 Go Micro 项目的命令行界面。

开始

下载并安装 。Go需要>=1.16或更高版本。

go get github.com/go-micro/cli/cmd/go-micro

让我们使用命令创建一个新服务。new

go-micro new service helloworld

按照屏幕上的说明进行操作。接下来,我们可以运行该程序。

cd helloworld
make proto tidy
go-micro run

最后,我们可以调用服务。

go-micro call helloworld Helloworld.Call '{"name": "John"}'

Go-micro的代码生成器安装

go-micro/generator: Protobuf code generation (github.com)安装文档

这是 go-micro 的代码生成器,根据protobuf文件生成相应go-micro代码,下载安装该工具,

go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install github.com/go-micro/generator/cmd/protoc-gen-micro@latest
​
# 其他所需protobuf的依赖,如安装可忽略
go get github.com/golang/protobuf/proto
go get google.golang.org/grpc
go install github.com/golang/protobuf/protoc-gen-go

用法

将服务定义为greeter.proto

syntax = "proto3";
​
package greeter;
option go_package = ".;greeter";
​
service Greeter {
  rpc Hello(Request) returns (Response) {}
}
​
message Request {
  string name = 1;
}
​
message Response {
  string msg = 1;
}

生成代码

protoc --proto_path=. --micro_out=. --go_out=. greeter.proto

您的输出结果应为:

./
    greeter.proto # original protobuf file
    greeter.pb.go # auto-generated by protoc-gen-go
    greeter.micro.go  # auto-generated by protoc-gen-micro

微生成的代码包括客户端和处理程序,可减少样板代码

服务端使用

将处理程序注册到微服务器

type Greeter struct{}

func (g *Greeter) Hello(ctx context.Context, req *proto.Request, rsp *proto.Response) error {
	rsp.Msg = "Hello " + req.Name
	return nil
}

proto.RegisterGreeterHandler(service.Server(), &Greeter{})

客户端使用

使用微客户端创建服务客户端

client := proto.NewGreeterService("greeter", service.Client())

工具错误处理

如果看到有关找不到或无法执行的错误,则可能是您的环境配置不正确。如果您已经安装了 ,请确保已包含在 .protoc-gen-micro protoc protoc-gen-go protoc-gen-micro $GOPATH/bin PATH

或者,将 Go 插件路径指定为命令的参数protoc

protoc --plugin=protoc-gen-go=$GOPATH/bin/protoc-gen-go --plugin=protoc-gen-micro=$GOPATH/bin/protoc-gen-micro --proto_path=. --micro_out=. --go_out=. greeter.proto

micro安装

Go Micro (github.com)项目地址

做了这么久的铺垫,接着让我们来进入主题,micro的学习,首先我们先来安装一下micro开发环境。安装步骤如下:

# 必须:依赖安装
go get github.com/golang/protobuf/protoc-gen-go
go get github.com/micro/micro/v3/cmd/protoc-gen-micro

# 使用 Go Get方式安装
go install github.com/micro/micro/v3@latest

# 或者,二进制文件安装
# Linux
wget -q  https://raw.githubusercontent.com/micro/micro/master/scripts/install.sh -O - | /bin/bash
# Windows
powershell -Command "iwr -useb https://raw.githubusercontent.com/micro/micro/master/scripts/install.ps1 | iex"

# 或者,通过docker镜像安装:
docker pull ghcr.io/micro/micro:latest

安装之后输入micro命令,显示如下就证明安装成功

/ # ./micro --help
NAME:
   micro - API first development platform

USAGE:
   micro [global options] command [command options] [arguments...]

VERSION:
   v4.3.1

接着我们来看一下micro的使用。

启动micro服务

首先,我们必须启动micro server。执行此操作的命令是:

micro server # 启动命令

# docker方式:启动micro server
root@123:~$ docker run -d -p 8080:8080 -p 8081:8081 ghcr.io/micro/micro:latest server #创建 micro服务
5227920ac2098e3acd939b3fc3aa3b2850783efd12293ea1a11ea1a39ea57025 

登录Micro

在使用 micro server 前需要先登录服务,默认的账号密码如下:

账号/密码:admin/micro

root@123:~/GoTestMicro# docker exec -it compassionate_meitner sh
/ # ls
bin    etc    lib    micro  opt    root   sbin   sys    usr
dev    home   media  mnt    proc   run    srv    tmp    var
/ # ./micro login
Enter username: admin
Enter password:
Successfully logged in.

micro使用

首先我们先来创建一个go micro框架的项目,我们使用micro命令,可以创建微服务,web项目等,具体用法如下:

micro

new		Create a new Micro service by specifying a directory path relative to your $GOPATH
#创建	通过指定相对于$GOPATH的目录路径,创建一个新的微服务。

USAGE:	#常用用法
micro new [command options][arguments...]

#指定服务的命名空间
--namespace "go.micro"	Namespace for the service e.g com.example
#服务类型,可以是微服务srv,或者web项目,或者api等						
--type "srv"			Type of service e.g api, fnc, srv, web
#服务的正式完整定义						
--fqdn 					FQDN of service e.g com.example.srv.service (defaults to namespace.type.alias)
#别名是在指定时作为组合名的一部分使用的短名称  别名						
--alias 				Alias is the short name used as part of combined name if specified

我们常用的就是创建微服务和web项目,如下:

创建一个web项目

micro new --type "web" micro/rpc/web
Creating service go.micro.web.web in /home/itcast/go/src/micro/rpc/web
.
├── main.go 	#主函数
├── plugin.go	#插件文件
├── handler	#被调用处理函数
│   └── handler.go
├── html	#前端页面
│   └── index.html
├── Dockerfile	#docker生成文件
├── Makefile
└── README.md

#编译后将web端呼叫srv端的客户端连接内容修改为srv的内容
#需要进行调通

打开我们web项目下的main.go文件,内容如下:

func main() {
	service := web.NewService( // 创建1个web服务
		web.Name("go.micro.web.web"), //注册服务名
		web.Version("latest"),        //服务的版本号
		web.Address(":8080"),         //!添加端口
	)

	if err := service.Init(); err != nil { //服务进行初始化
		log.Fatal(err)
	}

	//处理请求  / 的路由   //当前这个web微服务的 html文件进行映射
	service.Handle("/", http.FileServer(http.Dir("html")))

	//处理请求 /example/call  的路由   这个相应函数 在当前项目下的handler
	service.HandleFunc("/example/call", handler.ExampleCall)
	if err := service.Run(); err != nil { //运行服务
		log.Fatal(err)
	}
}

接着我们再来看一下我们重点需要操作的文件,handle.go,内容如下:

package handler

import (
	"context"
	"encoding/json"
	"net/http"
	"time"

	"github.com/micro/go-micro/client"
	example "micro/rpc/srv/proto/example" //将srv中的proto的文件导入进来进行通信的使用
)

// 相应请求的业务函数
func ExampleCall(w http.ResponseWriter, r *http.Request) {
	// 将传入的请求解码为json
	var request map[string]interface{}
	if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
		http.Error(w, err.Error(), 500)
		return
	}

	// 调用服务
	//替换掉原有的服务名
	//通过服务名和
	exampleClient := example.NewExampleService("go.micro.srv.srv", client.DefaultClient)
	rsp, err := exampleClient.Call(context.TODO(), &example.Request{
		Name: request["name"].(string),
	})
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}

	// we want to augment the response
	response := map[string]interface{}{
		"msg": rsp.Msg,
		"ref": time.Now().UnixNano(),
	}

	// encode and write the response as json
	if err := json.NewEncoder(w).Encode(response); err != nil {
		http.Error(w, err.Error(), 500)
		return
	}
}

创建一个微服务项目

$micro new --type "srv" t1/t1
#"srv" 是表示当前创建的微服务类型
#micro是相对于go/src下的文件夹名称 可以根据项目进行设置 
#srv是当前创建的微服务的文件名
Creating service go.micro.srv.srv in /home/itcast/go/src/t1/t1

.
├── main.go #主函数存放位置
├── plugin.go	#插件
├── handler	#服务提供函数的实现
│   └── example.go
├── subscriber	#订阅服务
│   └── example.go
├── proto/example	#proto协议文件
│   └── example.proto
├── Dockerfile	#docker生成文件
├── Makefile	#编译文件
└── README.md

download protobuf for micro:	#插件提示,已安装,可忽略

brew install protobuf
go get -u github.com/golang/protobuf/{proto,protoc-gen-go}
go get -u github.com/micro/protoc-gen-micro

compile the proto file example.proto:

cd /home/itcast/go/src/micro/rpc/srv
protoc --proto_path=. --go_out=. --micro_out=. proto/example/example.proto

我们来看一下微服务中的main.go文件:

package main

import (
	"github.com/micro/go-micro/util/log"
	"t1/t1/handler"
	"t1/t1/subscriber"

	t1 "t1/t1/proto/t1"
)

func main() {
	service := micro.NewService( // 创建一个micro服务
		micro.Name("go.micro.srv.t1"),
		micro.Version("latest"),
	)
	service.Init()                                                                    // 初始化服务
	t1.RegisterT1Handler(service.Server(), new(handler.T1))                           // 注册服务
	micro.RegisterSubscriber("go.micro.srv.t1", service.Server(), new(subscriber.T1)) // 注册一个发布器
	micro.RegisterSubscriber("go.micro.srv.t1", service.Server(), subscriber.Handler)// 注册一个函数到发布器
	if err := service.Run(); err != nil { // 运行服务
		log.Fatal(err)
	}
}

默认情况下,go micro框架使用的是自己封装的rpc通信,这里我们可以替换成grpc,代码如下:

package main

import (
	"github.com/micro/go-micro/util/log"
	"t1/t1/handler"
	t1 "t1/t1/proto/t1"
	"t1/t1/subscriber"
)

func main() {
	service := grpc.NewService( // 创建一个micro服务
		micro.Name("go.micro.srv.t1"),
		micro.Version("latest"),
	)
	service.Init()                                          // 初始化服务
	t1.RegisterT1Handler(service.Server(), new(handler.T1)) // 注册服务

	micro.RegisterSubscriber("go.micro.srv.t1", service.Server(), new(subscriber.T1)) // 注册一个发布器
	micro.RegisterSubscriber("go.micro.srv.t1", service.Server(), subscriber.Handler) // 注册一个函数到发布器
	if err := service.Run(); err != nil { // 运行服务
		log.Fatal(err)
	}
}

服务发现:

在go-micro包中,共有4种注册实现consul、gossip、mdns、memory,前两个都是基于hashicorp公司的协议,mdns则是基于组网广播实现,memory则是本地实现。

consul 依赖hashicorp的组件,但是功能强大、完整 gossip 基于SWIM协议广播,零依赖 mdns 轻量、零依赖,但是对环境有要求,某些环境不支持mdns的无法正常使用 memory 本地解决方案,不可跨主机访问 另外在go-plugins中有其它注册实现,比如etcd、eureka、k8s、nats、zk等等

大体解释下接口中每个方法的作用

  • Init 初始化
  • Options 获取配置选项
  • Register 注册服务
  • Deregister 卸载服务
  • GetService 获取指定服务
  • ListServices 列出所有服务
  • Watch watcher 负责侦听变动
  • String 注册信息转成字符串描述
package main

import (
	"github.com/micro/go-micro/registry"
	"github.com/micro/go-micro/registry/consul"
	"github.com/micro/go-micro/util/log"
	"t1/t1/handler"
	t1 "t1/t1/proto/t1"
	"t1/t1/subscriber"
)

func main() {
	reg := consul.NewRegistry(func(options *registry.Options) {	//初始化服务发现
		options.Addrs = []string{
			"192.168.137.130:8500",
		}
	})

	service := grpc.NewService(	// 创建一个micro服务
		micro.Name("go.micro.srv.t1"),
		micro.Version("latest"),
		micro.Registry(reg),
	)
	service.Init() // 初始化服务
	t1.RegisterT1Handler(service.Server(), new(handler.T1))	// 注册服务
	micro.RegisterSubscriber("go.micro.srv.t1", service.Server(), new(subscriber.T1))	// 注册一个发布器
	micro.RegisterSubscriber("go.micro.srv.t1", service.Server(), subscriber.Handler) // 注册一个函数到发布器
	if err := service.Run(); err != nil {	// 运行服务
		log.Fatal(err)
	}
}