0.micro框架

在了解go-micro之前,我们先来了解一下什么是micro。

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

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

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

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

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

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

我们先来了解一下服务发现是个什么东西?有什么作用?

1.服务发现

我们在做微服务开发的时候,客户端的一个接口可能需要调用N个服务,客户端必须知道所有服务的网络位置(ip+port),如下图所示
在这里插入图片描述
以往的做法是把服务的地址放在配置文件活数据库中,这样就有以下几个问题:

  • 需要配置N个服务的网络位置,加大配置的复杂性
  • 服务的网络位置变化,需要改变每个调用者的配置
  • 集群的情况下,难以做负载(反向代理的方式除外)

总结起来一句话:服务多了,配置很麻烦,问题一大堆

所以现在就选择服务发现来解决这些问题。我们来看一下,服务发现如何解决这个问题,具体设计如下:在这里插入图片描述
与之前解决方法不同的是,加了个服务发现模块。服务端把当前自己的网络位置注册到服务发现模块(这里注册的意思就是告诉),服务发现就以K-V的方式记录下,K一般是服务名,V就是IP:PORT。服务发现模块定时的轮询查看这些服务能不能访问的了(这就是健康检查)。客户端在调用服务A-N的时候,就跑去服务发现模块问下它们的网络位置,然后再调用它们的服务。这样的方式是不是就可以解决上面的问题了呢?客户端完全不需要记录这些服务的网络位置,客户端和服务端完全解耦!

常见的服务发现框架有:Etcd、Eureka、Consul、Zookeeper

这里我们选择go-micro默认的服务发现框架consul来做一个详细介绍。

2.了解consul并使用

Consul是HashiCorp公司推出的开源工具,用于实现分布式系统的服务发现与配置。包含多个组件,但是作为一个整体,为你的基础设施提供服务发现和服务配置的工具.他提供以下关键特性:

服务发现:consul通过DNS或者HTTP接口使服务注册和服务发现变的很容易,一些外部服务,例如saas提供的也可以一样注册。

健康检查:健康检测使consul可以快速的告警在集群中的操作。和服务发现的集成,可以防止服务转发到故障的服务上面。(心跳机制)

键/值存储:一个用来存储动态配置的系统。提供简单的HTTP接口,可以在任何地方操作。

多数据中心:无需复杂的配置,即可支持任意数量的区域。

官方建议:最好是三台或者三台以上的consul在运行,同名服务最好是三台或三台以上,默认可以搭建集群

2.1consul安装

​Consul用Golang实现,因此具有天然可移植性 (支持 Linux、windows和macOS)。安装包仅包含一个可执行文件。 Consul安装非常简单,只需要下载对应系统的软件包并解压后就可使用。

安装步骤如下:

# 这里以 ubuntu系统为例:
$ wget https://releases.hashicorp.com/consul/1.5.2/consul_1.5.2_linux_amd64.zip
$ unzip consul_1.5.2_linux_amd64.zip
$ sudo mv consul /usr/local/bin/

也可用离线包进行解压安装
链接:https://pan.baidu.com/s/1OQoAdn2_IrWbYbQUCg3_zQ
提取码:a4rd
安装验证:

安装 Consul后,通过执行 consul命令,你可以看到命令列表的输出

~ consul

在这里插入图片描述

无报错即为成功

2.2 consul的命令行

consul安装好之后,我们来使用一下吧。首先我们来看一下consul都有哪些命令。使用命令consul -h可以查看consul支持的所有参数,而且每个参数里面还支持其他参数,下面我们来具体看看。

agent指令是consul的核心,它运行agent来维护成员的重要信息、运行检查、服务宣布、查询处理等等。

-bind=0.0.0.0 		指定 consul所在机器的 IP地址。 默认值:0.0.0.0
-http-port=8500    consul 自带一个web访问的默认端口:8500
-client=127.0.0.1   表明哪些机器可以访问consul 。 默认本机。0.0.0.0 所有机器均可访问。
-config-dir=foo      所有主动注册服务的 描述信息
-data-dir=path       储存所有注册过来的srv机器的详细信息。
-dev                         开发者模式,直接以默认配置启动 consul
-node=hostname  服务发现的名字。
-rejoin                     consul 启动的时候,加入到的 consul集群
-server                    以服务方式开启consul, 允许其他的consul 连接到开启的 consul上 (形成集群)。如果不加 -server, 表示以 “客户端” 的方式开启。不能被连接。
-ui 		                  可以使用 web 页面 来查看服务发现的详情

测试上述命令:

# 在终端中,键入:
consul agent -server -bootstrap-expect 1 -data-dir /tmp/consul -node=n1 -bind=192.168.6.108 -ui -rejoin -config-dir=/etc/consul.d/ -client 0.0.0.0

#看到提示:
==> Consul agent running!

需要先在/etc/下面创建consul.d目录

  • -server : 定义agent运行在server模式
  • -bootstrap-expect :在一个datacenter中期望提供的server节点数目,当该值提供的时候,consul一直等到达到指定sever数目的时候才会引导整个集群,该标记不能和bootstrap共用
  • -bind:该地址用来在集群内部的通讯,集群内的所有节点到地址都必须是可达的,默认是0.0.0.0
  • -node:节点在集群中的名称,在一个集群中必须是唯一的,默认是该节点的主机名
  • -ui: 启动web界面 :8500
  • -rejoin:使consul忽略先前的离开,在再次启动后仍旧尝试加入集群中。
  • -config-dir:配置文件目录,里面所有以.json结尾的文件都会被加载
  • -client:consul服务侦听地址,这个地址提供HTTP、DNS、RPC等服务,默认是127.0.0.1所以不对外提供服务,如果你要对外提供服务改成0.0.0.0
  • data-dir:提供一个目录用来存放agent的状态,所有的agent允许都需要该目录,该目录必须是稳定的,系统重启后都继续存在

启动 Google 浏览器, 测试:
在这里插入图片描述

2.2.1停止consul

你可以使用Ctrl-C 不优雅的关闭Agent. 中断Agent之后你可以看到他离开了集群并关闭.

​在退出中,Consul提醒其他集群成员,这个节点离开了.如果你强行杀掉进程.集群的其他成员应该能检测到这个节点失效了.当一个成员离开,他的服务和检测也会从目录中移除.当一个成员失效了,他的健康状况被简单的标记为危险,但是不会从目录中移除.Consul会自动尝试对失效的节点进行重连.允许他从某些网络条件下恢复过来.离开的节点则不会再继续联系.

​此外,如果一个agent作为一个服务器,一个优雅的离开是很重要的,可以避免引起潜在的可用性故障影响达成一致性协议.
consul优雅的退出:

$ consul leave
2.2.2注册服务

搭建好conusl集群后,用户或者程序就能到consul中去查询或者注册服务。可以通过提供服务定义文件或者调用HTTP API来注册一个服务.

这里我们使用定义服务文件来注册一个服务:

{"service": {
    "name": "Faceid",
    "tags": ["rails"],
    "port": 9000
	}
}

服务定义文件在我们的配置目录下面,/etc/consul.d/,文件都是以.json结尾。

注册完服务之后,我们重启consul。

2.2.3查询服务

一旦agent启动并且服务同步了.我们可以通过DNS或者HTTP的API来查询服务.这里我们通过HTTP来查询服务:

$ curl -s 127.0.0.1:8500/v1/catalog/service/faceid

可以得到一串json数据,可拿去进行json格式解析,还可以打开web页面,查看注册的服务。默认consul对应的端口是8500,在浏览器输入地址`localhost:8500,也可以看到注册的服务
在这里插入图片描述

2.2.4健康检查

健康检查是服务发现的关键组件.预防使用到不健康的服务.和服务注册类似,一个检查可以通过检查定义或HTTP API请求来注册.我们将使用和检查定义来注册检查.和服务类似,因为这是建立检查最常用的方式.

在/etc/consul.d/目录下面创建文件web2.json,内容如下:

{"service": {
    "name": "web",
    "tags": ["extract", "verify", "compare", "idcard"],
    "address": "192.168.137.130",
    "port": 9000,
    "check": {
        "id": "api",
          "name": "HTTP API on port 9000",
        "http": "http://localhost:9000",
        "interval": "10s",
        "timeout": "1s"
        }
   }
}

如果我们没有开启服务,那么健康检查一定会报错

consul做健康检查的必须是Script、HTTP、TCP、TTL中的一种。
Script类型需要提供Script脚本和interval变量。**具体配置如下:

{  
  "check": {  
    "id": "mem-util",  
    "name": "Memory utilization",  
    "script": "/usr/local/bin/check_mem.py",  
    "interval": "10s",  
    "timeout": "1s"  
  }  
} 

通过执行外部应用进行健康检查:这种外部程序具有退出代码,并可能产生一些输出;脚本按照指预置时间间隔来调用(比如,每30秒调用一次),类似于Nagios插件系统,脚本输出限制在4K以内,输出大于4K将截断。默认情况下,脚本超时时间为30秒——可通过timeout来配置。

HTTP类型必须提供http和Interval字段。具体代码如下:

{  
  "check": {  
    "id": "api",  
    "name": "HTTP API on port 5000",  
    "http": "http://localhost:5000/health",  
    "interval": "10s",  
    "timeout": "1s"  
  }  
}  

这种检查将按照预设的时间间隔创建一个HTTP “get”请求。HTTP响应代码来标示服务所处状态:任何2xx代码视为正常,429表示警告——有很多请求;其他值表示失败。
这种类型的检查应使用curl或外部程序来处理HTTP操作。默认情况下,HTTP Checks中,请求超时时间等于调用请求的间隔时间,最大10秒。有可能使用客制的HTTP check,可以自由配置timeout时间,输出限制在4K以内,输出大于4K将截断。

TCP类型需要提供tcp和Interval字段。具体代码如下:

{  
  "check": {  
    "id": "ssh",  
    "name": "SSH TCP on port 22",  
    "tcp": "localhost:22",  
    "interval": "10s",  
    "timeout": "1s"  
  }  
}  

这种检查将按照预设的时间间隔与指定的IP/Hostname和端口创建一个TCP连接。服务的状态依赖于TCP连接是否成功——如果连接成功,则状态是“success”;否则状态是“critical”。如果一个Hostname解析为一个IPv4和一个IPv6,将尝试连接这两个地址,第一次连接成功则服务状态是“success”。默认情况下,TCP checks中,请求超时时间等于调用请求的间隔时间,最大10秒。也是可以自由配置的。

TTL(Timeto Live生存时间)类型只需提供ttl,具体配置如下:

{  
  "check": {  
    "id": "web-app",  
    "name": "Web App Status",  
    "notes": "Web app does a curl internally every 10 seconds",  
    "ttl": "30s"  
  }  
}  

这种checks为给定TTL保留了最后一种状态,checks的状态必须通过HTTP接口周期性更新,如果外部接口没有更新状态,那么状态就会被认定为不正常。 TTL checks同时会将其最后已知状态更新至磁盘,这允许Agent通过重启后恢复到已知的状态。通过TTL端上一次check来维持健康状态的有效性。

其他更多consul功能,我们可以参考http://www.liangxiansen.cn/2017/04/06/consul/

2.3consul和grpc结合使用

先从GitHub下载consul包

$ go get -u -v github.com/hashicorp/consul
将grpc服务注册到consul上
  1. 创建 proto文件, 指定 rpc 服务
  2. 启动 consul 服务发现
  3. 启动server
    1. 获取consul 对象。
    2. 使用 consul对象,将 server 信息,注册给 consul
    3. 启动服务
  4. 启动client
    1. 获取consul 对象。
    2. 使用consul对象,从consul 上获取健康的 服务。
    3. 再访问服务 (grpc远程调用)

代码示例:
proto文件:

syntax = "proto3";

package pb;

message Person {
    string name = 1;
    int32 age = 2;
}

// 添加 rpc服务
service hello {
    rpc sayHello (Person) returns (Person);
}

服务端:

package main

import (
	"google.golang.org/grpc"
	"day02/pb"
	"context"
	"net"
	"fmt"
	"github.com/hashicorp/consul/api"
)

// 定义类
type Children struct {
}

// 绑定类方法, 实现借口
func (this *Children)SayHello(ctx context.Context, p *pb.Person) (*pb.Person, error) {
	p.Name = "hello  " + p.Name
	return p, nil
}

func main()  {
	// 把grpc服务,注册到consul上.
	// 1. 初始化consul 配置
	consulConfig := api.DefaultConfig()

	// 2. 创建 consul 对象
	consulClient, err := api.NewClient(consulConfig)
	if err != nil {
		fmt.Println("api.NewClient err:", err)
		return
	}
	// 3. 告诉consul, 即将注册的服务的配置信息
	reg := api.AgentServiceRegistration {
		ID:"bj38",
		Tags:[]string{"grcp", "consul"},
		Name:"grpc And Consul",
		Address:"127.0.0.1",
		Port:8800,
		Check:&api.AgentServiceCheck{
			CheckID:"consul grpc test",
			TCP:"127.0.0.1:8800",
			Timeout:"1s",
			Interval:"5s",
		},
	}

	// 4. 注册 grpc 服务到 consul 上
	consulClient.Agent().ServiceRegister(&reg)

//以下为 grpc 服务远程调用

	// 1.初始化 grpc 对象,
	grpcServer := grpc.NewServer()

	// 2.注册服务
	pb.RegisterHelloServer(grpcServer, new(Children))

	// 3.设置监听, 指定 IP/port
	listener, err := net.Listen("tcp", "127.0.0.1:8800")
	if err != nil {
		fmt.Println("Listen err:", err)
		return
	}
	defer listener.Close()

	fmt.Println("服务启动... ")

	// 4. 启动服务
	grpcServer.Serve(listener)

}

客户端:

package main

import (
	"google.golang.org/grpc"
	"day02/pb"
	"context"
	"fmt"
	"github.com/hashicorp/consul/api"
	"strconv"
)

func main()  {
	// 初始化 consul 配置
	consulConfig := api.DefaultConfig()

	// 创建consul对象 -- (可以重新指定 consul 属性: IP/Port , 也可以使用默认)
	consulClient, err := api.NewClient(consulConfig)

	// 服务发现. 从consuL上, 获取健康的服务
	services, _, err := consulClient.Health().Service("grpc And Consul", "grcp", true, nil)

	// 简单的负载均衡.
	
	addr := services[0].Service.Address + ":" + strconv.Itoa(services[0].Service.Port)
	
//以下为 grpc 服务远程调用///
	// 1. 链接服务
	//grpcConn, _ := grpc.Dial("127.0.0.1:8800", grpc.WithInsecure())

	// 使用 服务发现consul 上的 IP/port 来与服务建立链接
	grpcConn, _ := grpc.Dial(addr, grpc.WithInsecure())

	// 2. 初始化 grpc 客户端
	grpcClient := pb.NewHelloClient(grpcConn)

	var person pb.Person
	person.Name = "Andy"
	person.Age = 18

	// 3. 调用远程函数
	p, err := grpcClient.SayHello(context.TODO(), &person)

	fmt.Println(p, err)
}

3go-micro框架

做了这么久的铺垫,接着让我们来进入主题,go-micro的学习。

3.1go-micro安装

首先我们先来安装一下go-micro开发环境。安装步骤如下:

#安装go-micro
go get -u -v github.com/micro/go-micro
#安装工具集
go get -u -v github.com/micro/micro
#安装protobuf插件
go get -u github.com/golang/protobuf/{proto,protoc-gen-go}
go get -u github.com/micro/protoc-gen-micro

或者通过docker镜像安装:

$ docker pull microhq/micro

安装之后输入micro命令,显示如下就证明安装成功
在这里插入图片描述

3.2go-micro使用

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

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
#服务类型,可以是微服务src,或者web项目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项目
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
#docker生成文件
├── Dockerfile
├── Makefile
└── README.md

#编译后将web端呼叫srv端的客户端连接内容修改为srv的内容
#需要进行调通
创建微服务
$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协议
├── proto/example
│   └── example.proto
#docker生成文件
├── Dockerfile
#编译文件
├── 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

go-micro框架创建完成后即可对其文件中的代码进行相应修改,实现自己的项目需求。

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐