go微服务学习 go-micro框架

Posted Demonwuwen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了go微服务学习 go-micro框架相关的知识,希望对你有一定的参考价值。

本节简单介绍micro和consul,具体使用放在下一节

1 micro简介

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

Micro解决了在云内外构建分布式系统的关键要求。它利用微服务体系结构模式,并提供一组作为平台构建基块的服务. Micro 处理分布式系统的复杂性,并提供更简单的可编程抽象.

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

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

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

    组播 广播

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

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

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

2 go-micro的主要功能

  • 服务发现:自动服务注册和名称解析。服务发现是微服务开发的核心。当服务A需要与服务B通话时,它需要该服务的位置。默认发现机制是多播DNS(mdns),一种零配置系统。您可以选择使用SWIM协议为p2p网络设置八卦,或者为弹性云原生设置设置consul
  • 负载均衡:基于服务发现构建的客户端负载均衡。一旦我们获得了服务的任意数量实例的地址,我们现在需要一种方法来决定要路由到哪个节点。我们使用随机散列负载均衡来提供跨服务的均匀分布,并在出现问题时重试不同的节点
  • 消息编码:基于内容类型的动态消息编码。客户端和服务器将使用编解码器和内容类型为您无缝编码和解码Go类型。可以编码任何种类的消息并从不同的客户端发送。客户端和服务器默认处理此问题。这包括默认的protobuf和json
  • 请求/响应:基于RPC的请求/响应,支持双向流。我们提供了同步通信的抽象。对服务的请求将自动解决,负载平衡,拨号和流式传输。启用tls时,默认传输为http / 1.1或http2
  • Async Messaging:PubSub是异步通信和事件驱动架构的一流公民。事件通知是微服务开发的核心模式。启用tls时,默认消息传递是点对点http / 1.1或http2
  • 可插拔接口:Go Micro为每个分布式系统抽象使用Go接口,因此,这些接口是可插拔的,并允许Go Micro与运行时无关,可以插入任何基础技术
    插件地址:https://github.com/micro/go-plugins

2.1 服务发现

服务发现是微服务开发的核心。当服务A需要与服务B通话时,它需要该服务的位置。

我们在做微服务开发的时候,客户端的一个接口可能需要调用N个服务,客户端必须知道所有服务的网络位置(ip+port),如下图所示

以往的做法是把服务的地址放在配置文件活数据库中,这样就有以下几个问题:

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

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

所以现在就选择服务发现来解决这些问题。

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

2.2 了解consul并使用

Consul是一种服务网格解决方案,提供具有服务发现、配置和分段功能的全功能控制平面。这些特性可以根据需要单独使用,也可以一起使用以构建完整的服务网格。Consul需要一个数据平面,并支持代理和本地集成模型。Consul附带一个简单的内置代理,这样一切都可以开箱即用,但也支持第三方代理集成,如Envoy。
他提供了以下特性
服务发现:consul通过DNS或者HTTP接口使服务注册和服务发现变的很容易,一些外部服务,例如saas提供的也可以一样注册。

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

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

安全服务通信:领事可以为服务生成和分发TLS证书,以建立相互的TLS连接。意图可用于定义允许哪些服务进行通信。服务细分可以很容易地进行管理,并且意图可以实时更改,而不是使用复杂的网络拓扑和静态防火墙规则。

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

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

2.2.1 consul安装

安装教程链接

1、MacOS 安装
此方法安装比较方便:打开终端输入以下两行命令

brew tap hashicorp/tap
brew install hashicorp/tap/consul
注:这种安装方法会自动更新版本

如果需要更新到最新版本:

brew upgrade hashicorp/tap/consul

2、Ubuntu安装
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/

安装完成后终端输入consul确认:

consul


出现如图所示,即安装完毕。

2.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 页面 来查看服务发现的详情

info: 指令提供了各种操作时可以用到的debug信息,对于client和server,info有返回不同的子系统信息,目前有以下几个KV信息:agent(提供agent信息),consul(提供consul库的信息),raft(提供raft库的信息),serf_lan(提供LAN gossip pool),serf_wan(提供WAN gossip pool)

leave:指令触发一个优雅的离开动作并关闭agent,节点离开后不会尝试重新加入集群中。运行在server状态的节点,节点会被优雅的删除,这是很严重的,在某些情况下一个不优雅的离开会影响到集群的可用性。

members:指令输出consul agent目前所知道的所有的成员以及它们的状态,节点的状态只有alive、left、failed三种状态。

-detailed:输出每个节点更详细的信息。
-rpc-addr:一个rpc地址,agent可以链接上来发送命令,如果没有指定,默认是127.0.0.1:8400。
-status:过滤出符合正则规则的节点

reload:指令可以重新加载agent的配置文件。SIGHUP指令在重新加载配置文件时使用,任何重新加载的错误都会写在agent的log文件中,并不会打印到屏幕。

测试上述命令: 'bind的ip设置为你自己的ip'

consul agent -server -bootstrap-expect 1 -data-dir /tmp/consul -node=n1 -bind=192.168.1.5 -ui -rejoin -d -client 0.0.0.0

此时看到提示:

打开浏览器:localhost:8500可以看到如下界面

如果我们创建自己的web.json文件

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

命令行输入增加-config-dir=/path 就制定web.json的文件夹路径

consul agent -server -bootstrap-expect 1 -data-dir /tmp/consul -node=n1 -bind=192.168.1.5 -ui -rejoin -config-dir=/Users/demon/Documents/go学习/consul.d -client 0.0.0.0

打开就看到如下界面。

2.3 注册服务到consul命令

步骤:

  • 1 进入配置文件路径
  • 2 创建json文件
  • 3 按json语法填写服务信息
    {
    “service”:{
    “name”: “Faceid”,
    “tags”: [“rails”],
    “port”: 9000
    }
    }
  • 4 重新启动consul
 consul agent -server -bootstrap-expect 1 -data-dir /tmp/consul -node=n1 -bind=192.168.1.5 -ui -rejoin -config-dir=/Users/demon/Documents/go学习/consul.d -client 0.0.0.0
  • 5 查询服务
    • 1浏览器查看

关闭consul 使用命令 consul leave

2.4 健康检查

  1. 打开web.json配置文件
  2. 写入服务配置信息,给web.json文件添加如下信息:
{
	"service":{
		"name": "Faceid",
		"tags": ["rails"],
		"port": 9000,
		"check":{
			"id": "api",
			"name": "rails check",
			"http": "http:192.168.1.5:8800",
			"interval": "5s",
			"timeout": "1s"
		}

	}
}
  1. 执行consul reload 或者,consul leave关闭consul,重新启动consul
    终端可看到如下信息
  2. 用浏览器输入localhost:8500查看Faceid这个服务情况

    此时可以看到不健康,因为我们在json中设置了1ms消息超时检测,没有服务Faceid给consul回复。

除了 http 实现健康检查外,还可以使用 “脚本”、“tcp”、“ttl” 方式进行健康检查。

2.5 consul和grpc结合

安装 consul 源码包:

$ go get -u -v github.com/hashicorp/consul

使用整体流程

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

1. 创建proto文件,指定rpc服务
创建person.proto文件

syntax = "proto3";

option go_package = "./pb";

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

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

在当前文件夹终端输入如下命令

protoc --go_out=. *.proto --go-grpc_out=. *.proto

此时会生成一个pb文件夹,里面就是通过person.proto文件生成的go文件
person.pb.goperson_grpc.pb.go

2. 启动 consul 服务发现
通过命令

consul agent -dev
此时可以通过打开localhost:8500查看

3. 启动server
服务端代码编写。
创建server文件夹,在里面创建server.go

package main

import (
	"context"
	"fmt"
	"github.com/hashicorp/consul/api"
	"google.golang.org/grpc"
	"grpcPrac/consul_grpc/pb"
	"net"
)
type Children struct {
	pb.UnimplementedHelloServer
}

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: "FaceID",
		Tags: []string{"grpc","consul"},
		Name: "grpc and consul",
		Address: "127.0.0.1",
		Port: 12345,
		Check: &api.AgentServiceCheck{
			CheckID: "consul grpc test",
			TCP: "127.0.0.1:12345",
			Timeout: "1s",
			Interval: "5s",
		},
	}

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


	// grpc 服务远程调用

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

	//2 注册服务
	pb.RegisterHelloServer(grpcServer,&Children{})

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

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

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

}

启动服务
go run server.go

4. 启动client

package main

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

func main() {
  //初始化consul配置,客户端服务器需要一致
  consultConfig := api.DefaultConfig()

  //获取consul操作对象
  regisgerClient, err := api.NewClient(consultConfig)
  if err!= nil{
  	fmt.Println("api.NewClient err:",err)
  	return
  }

  //获取地址
  serviceEntry,_,_:=regisgerClient.Health().Service("grpc and consul","grpc",true,nil)

  //简单负载均衡
  addr := serviceEntry[0].Service.Address+":"+strconv.Itoa(serviceEntry[0].Service.Port)

  //1.连接服务
  grpcConn, err := grpc.Dial(addr,grpc.WithInsecure())
  if err!= nil{
  	fmt.Println("grpc.Dial err:",err)
  	return
  }

  //2.初始化客户端
  grpcClient := pb.NewHelloClient(grpcConn)
  var person pb.Person
  person.Name = "Huang"
  person.Age = 18

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

启动客户端,可以看到如下结果

我们其实还可以打开localhost:8500查看consul服务

2.6 服务注销

当我们需要注销服务的时候,就可以编写以下程序。

package main

import "github.com/hashicorp/consul/api"

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

	//2.创建consul对象
	consulClient,_ := api.NewClient(consulConfig)

	//3.注销服务
	consulClient.Agent().ServiceDeregister("FaceID")
}

运行程序后,我们查看web端就可以发现,grpc and consul这一项服务消失了

3 go-micro核心接口

go-micro之所以可以高度订制和他的框架结构是分不开的,go-micro由8个关键的interface组成,每一个interface都可以根据自己的需求重新实现,这8个主要的inteface也构成了go-micro的框架结构

参考资料:
topgoer -GoMicro入门
2021年Go语言零基础微服务项目
consul官网

以上是关于go微服务学习 go-micro框架的主要内容,如果未能解决你的问题,请参考以下文章

go微服务学习 go-micro框架-—— micro安装

go微服务学习 go-micro框架-—— micro安装

go微服务学习 go-micro框架-—— micro安装

go微服务框架go-micro深度学习 整体架构介绍

go微服务框架go-micro深度学习 Registry服务的注册和发现

go微服务学习 go-micro框架