首页 > 其他分享 >注册中心-consul

注册中心-consul

时间:2024-03-18 15:56:34浏览次数:28  
标签:服务 中心 err proto grpc consul 注册

目录

一、注册中心

1.1 服务注册与发现

  • 在使用微服务后,调用都变成了服务间的调用。

  • 服务间调用需要知道IP、端口等信息。

  • 在没有微服务之前,我们的调用信息一般都是写死在调用方的配置文件里(有的公司把这些信息写到数据库等公共的地方,以方便维护)。

  • 由于业务的复杂,每个服务可能依赖N个其他服务,如果某个服务的IP,端口等信息发生变更,那么所有依赖该服务的服务的配置文件都要去修改,这样显然太麻烦了。

  • 有些服务为了负载是有个多个实例的,而且可能是随时会调整实例的数量。如果每次调整实例数量都要去修改其他服务的配置并重启那太麻烦了。

  • 为了解决这个问题,就有了注册中心

  • 假设我们有服务A需要调用服务B,并且有服务注册发现组件R。整个大致流程将变成3步:

  1. 服务B启动时,向注册中心注册资金
  2. 服务A从从注册中心拉取服务B的信息
  3. 服务A调用服务B
  • 有了服务注册发现组件之后,当修改A服务信息的时候再也不用去修改其他相关服务了

[image-20220521004920992](http://photo.liuqingzheng.top/20220521-image-20220521004920992 .png)

1.2 分布式一致性算法

  • 一致性就是数据保持一致,在分布式系统中,可以理解为多个节点中数据的值是一致

  • 一致性协议算法主要有:Paxos、Raft、ZAB、Gossip

  • 具体参照:https://zhuanlan.zhihu.com/p/130332285

  • Paxos算法是Leslie Lamport在1990年提出的一种基于消息传递的一致性算法,非常难以理解,基于Paxos协议的数据同步与传统主备方式最大的区别在于:Paxos只需超过半数的副本在线且相互通信正常,就可以保证服务的持续可用,且数据不丢失。

  • Raft是斯坦福大学的Diego Ongaro、John Ousterhout两个人以易理解为目标设计的一致性算法,已经有了十几种语言的Raft算法实现框架,较为出名的有etcd,Google的Kubernetes也是用了etcd作为他的服务发现框架。

  • Raft是Paxos的简化版,与Paxos相比,Raft强调的是易理解、易实现,Raft和Paxos一样只要保证超过半数的节点正常就能够提供服务。这篇文章 《ETCD教程-2.Raft协议》 详细讲解了Raft原理,非常有意思,感兴趣的同学可以看看。

  • ZooKeeper Atomic Broadcast (ZAB, ZooKeeper原子消息广播协议)是ZooKeeper实现分布式数据一致性的核心算法,ZAB借鉴Paxos算法,但又不像Paxos算法那样,是一种通用的分布式一致性算法,它是一种特别为ZooKeeper专门设计的支持崩溃恢复的原子广播协议

1.3 注册中心选型

[image-20220521010452074](http://photo.liuqingzheng.top/20220521-image-20220521010452074 .png)

二、Consul

2.1 介绍

  • Consul 是 HashiCorp 公司推出的开源工具,用于实现分布式系统的服务发现与配置。与其它分布式服务注册与发现的方案,Consul 的方案更“一站式”,内置了服务注册与发现框 架、分布一致性协议实现、健康检查、Key/Value 存储、多数据中心方案,不再需要依赖其它工具(比如 ZooKeeper 等)。

  • Consul 使用起来也较为简单,使用 Go 语言编写,因此具有天然可移植性(支持Linux、windows和Mac OS X);安装包仅包含一个可执行文件,方便部署,与 Docker 等轻量级容器可无缝配合
    Consul 主要特征

2.2 consul特点

CP模型,使用 Raft 算法来保证强一致性,不保证可用性;

支持服务注册与发现、健康检查、KV Store功能。

支持多数据中心,可以避免单数据中心的单点故障,而其部署则需要考虑网络延迟, 分片等情况等。

支持安全服务通信,Consul可以为服务生成和分发TLS证书,以建立相互的TLS连接。

支持 http 和 dns 协议接口;

官方提供 web 管理界面

2.3 安装

(1)普通安装

i. win

//1  下载
//2 加入环境变量
//3 执行
consul agent -dev

ii. mac

//1  下载
//2  加入环境变量
//3  执行
consul agent -dev

(2)docker安装

//1 使用docker下载consul
//同步时间:ntpdate cn.pool.ntp.org
docker pull docker.io/library/consul

//2 修改consul tag
docker tag docker.io/library/consul consul

//3 启动
docker run -d -p 8500:8500 -p 8300:8300 -p 8301:8301 -p 8302:8302 -p 8600:8600/udp   consul consul agent -dev -client=0.0.0.0

//4 浏览器中访问web页面
// 8500端口:http端口,我们进行服务注册发现使用http端口
// 8600端口:dns端口

//5 dig 命令,通过域名解析地址和端口
//dig命令dns地址为10.0.0.102,
//端口为:8600,
//域名为:service的名字.service.consul ---》自动生成
//查询类型指定为为srv
dig @10.0.0.102 -p 8600 consul.service.consul SRV

[image-20220521012903958](http://photo.liuqingzheng.top/20220528-image-20220521012903958 .png)

三、常用API

3.1 服务注册

// put 请求
// 地址:/v1/agent/service/register
// Service Registration Flags
	-name:The name of the service to register-->服务的名字
	-id:The ID of the service. This will default to -name if not set--》id不设置与name一致
	-tag value - Associate a tag with the service instance. This flag can be specified multiples times.-->服务的标签
	-address - The address of the service. If this isn't specified, it will default to the address registered with the local agent.
	-port - The port of the service.

(1)使用postman注册演示

// 向http://10.0.0.102:8500/v1/agent/service/register发送put请求
// body体数据为
{
    "Name":"lqz",
    "ID":"ddd",
    "Tags":["lqz","web"],
    "Address":"127.0.0.1",
    "Port":8080

}

[image-20220521020045128](http://photo.liuqingzheng.top/20220528-image-20220521020045128 .png)

(2)Go 语言注册

package main

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

const (
	consulAddress = "10.0.0.102:8500"
)

func RegisterConsul(localIP string, localPort int, name string,id string, tags []string) error {
	// 创建连接consul服务配置
	config := consulapi.DefaultConfig()
	config.Address = consulAddress
	client, err := consulapi.NewClient(config)
	if err != nil {
		fmt.Println("consul client error : ", err)
	}

	// 创建注册到consul的服务到
	registration := new(consulapi.AgentServiceRegistration)
	registration.ID = id
	registration.Name = name //根据这个名称来找这个服务
	registration.Port = localPort
	//registration.Tags = []string{"lqz", "web"} //这个就是一个标签,可以根据这个来找这个服务,相当于V1.1这种
	registration.Tags = tags //这个就是一个标签,可以根据这个来找这个服务,相当于V1.1这种
	registration.Address = localIP

	// 增加consul健康检查回调函数
	check := new(consulapi.AgentServiceCheck)
	check.HTTP = fmt.Sprintf("http://%s:%d/health", registration.Address, registration.Port)
	check.Timeout = "5s"                         //超时
	check.Interval = "5s"                        //健康检查频率
	check.DeregisterCriticalServiceAfter = "30s" // 故障检查失败30s后 consul自动将注册服务删除
	registration.Check = check
	// 注册服务到consul
	err = client.Agent().ServiceRegister(registration)
	if err != nil {
		return err
	}
	return nil

}

func main() {
	// 1 注册
	RegisterConsul("192.168.1.1",8080,"lqz_web","lqz_web",[]string{"lqz","web"})

}

3.2 服务删除

(1)使用postman注册演示

// 向 http://10.0.0.102:8500/v1/agent/service/deregister/ddd发送put请求

(2)go代码

func DeleteService() error {
	// 创建连接consul服务配置
	config := consulapi.DefaultConfig()
	config.Address = consulAddress
	client, err := consulapi.NewClient(config)
	if err != nil {
		fmt.Println("consul client error : ", err)
	}

	err = client.Agent().ServiceDeregister("qqq")
	if err != nil {
		return err
	}
	return nil

}

3.3 设置健康检查

3.4 获取服务

(1)获取所有

func GetAllService() (map[string]*consulapi.AgentService, error) {
	// 创建连接consul服务配置
	config := consulapi.DefaultConfig()
	config.Address = consulAddress
	client, err := consulapi.NewClient(config)
	if err != nil {
		fmt.Println("consul client error : ", err)
	}
	res, err := client.Agent().Services()
	if err != nil {
		return nil, err
	}
	return res, nil
}

(2)过滤服务

func FilterService() (map[string]*consulapi.AgentService, error) {
	// 创建连接consul服务配置
	config := consulapi.DefaultConfig()
	config.Address = consulAddress
	client, err := consulapi.NewClient(config)
	if err != nil {
		fmt.Println("consul client error : ", err)
	}
	//res, err := client.Agent().ServicesWithFilter(`Service=="lqz-web03"`)// 按服务名字过滤
	res, err := client.Agent().ServicesWithFilter(`Port==8087`) // 按端口过滤
	if err != nil {
		return nil, err
	}
	return res, nil

}

3.5 同一个服务注册多次

name一样,id不一样会把name一样的放到一起

3.6 gRPC注册服务和健康检查

gRPC预留了健康检查的proto接口,并且实现了,我们只需要引入注册到微服务中即可

https://github.com/grpc/grpc/blob/master/doc/health-checking.md

通过proto生成的go文件路径:google.golang.org/grpc/health/grpc_health_v1

健康检查代码路径:google.golang.org/grpc/health

package main

import (
	"context"
	"fmt"
	consulapi "github.com/hashicorp/consul/api"
	"google.golang.org/grpc"
	"google.golang.org/grpc/health"
	"google.golang.org/grpc/health/grpc_health_v1"
	"grpc_proto_demo/proto_default_demo/proto"
	"net"
)

type GreeterServer struct {
}

// proto 的service中只写了一个方法,现在只写一个,如果写了多个,都要实现
func (h GreeterServer) SayHello(ctx context.Context, in *proto.HelloRequest) (*proto.HelloResponse, error) {
	// 接收客户端发送过来的数据,打印出来
	fmt.Println("客户端传入的名字是:", in.Name)
	fmt.Println("客户端传入的年龄是:", in.Age)
	fmt.Println("客户端传入的女孩们是:", in.Girls)
	// 返回给客户端
	return &proto.HelloResponse{
		Reply: "服务端给你回复",
	}, nil
}

// 服务端代码
func main() {
	// 第一步:new一个server
	g := grpc.NewServer()
	// 第二步:生成一个结构体对象
	s := GreeterServer{}
	// 第三步: 把s注册到g对象中
	proto.RegisterGreeterServer(g, &s)
	// 第四步:启动服务,监听端口
	lis, error := net.Listen("tcp", "192.168.31.226:50052")
	if error != nil {
		panic("启动服务异常")
	}

	//******** 注册grpc服务和设置健康检查***开始*****
	// 1 设置健康检查
	//health.NewServer()具体实现grpc已经帮我们写好了
	grpc_health_v1.RegisterHealthServer(g,health.NewServer())
	// 2 注册grpc服务---》需要使用机器的真实ip
	RegisterConsul("192.168.31.226",50052,"grpc_test","grpc_test001",[]string{"grpc","lqz"})
	//******** 注册grpc服务和设置健康检查***结束*****

	g.Serve(lis)

}

func RegisterConsul(localIP string, localPort int, name string,id string, tags []string) error {
	// 创建连接consul服务配置
	config := consulapi.DefaultConfig()
	config.Address = "10.0.0.102:8500"
	client, err := consulapi.NewClient(config)
	if err != nil {
		fmt.Println("consul client error : ", err)
	}

	// 创建注册到consul的服务到
	registration := new(consulapi.AgentServiceRegistration)
	registration.ID = id
	registration.Name = name //根据这个名称来找这个服务
	registration.Port = localPort
	//registration.Tags = []string{"lqz", "web"} //这个就是一个标签,可以根据这个来找这个服务,相当于V1.1这种
	registration.Tags = tags //这个就是一个标签,可以根据这个来找这个服务,相当于V1.1这种
	registration.Address = localIP

	// 增加consul健康检查回调函数
	check := new(consulapi.AgentServiceCheck)
	check.GRPC = "192.168.31.226:50052" // 健康检查地址只需要写grpc服务地址端口即可,会自动检查
	check.Timeout = "5s"                         //超时
	check.Interval = "5s"                        //健康检查频率
	check.DeregisterCriticalServiceAfter = "30s" // 故障检查失败30s后 consul自动将注册服务删除
	registration.Check = check
	// 注册服务到consul
	err = client.Agent().ServiceRegister(registration)
	if err != nil {
		return err
	}
	return nil

}

四、案例(gin-grpc-consul)

(1)整体流程和目录结构

[image-20220528012249481](http://photo.liuqingzheng.top/20220528-image-20220528012249481 .png)

// 0 grpc服务注册到consul中
// 1 浏览器访问gin服务的 /index
// 2 gin服务到consul中发现grpc服务
// 3 gin服务向grpc发送请求获得数据
// 4 返回给浏览器

// 目录结构
grpc_gin_consul
  -gin_web
  	-main.go
  -grpc_srv
    -proto
      -hello.proto
    -server
    	-main.go

(2)gin_web/main.go

package main

import (
	"context"
	"fmt"
	"github.com/gin-gonic/gin"
	consulapi "github.com/hashicorp/consul/api"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"grpc_proto_demo/grpc_gin_consul/grpc_srv/proto"
)

// 可能有多个grpc服务,我们只返回一个
func getFirstGrpcRegister()(host string,port int,err error)  {
	// 创建连接consul服务配置
	config := consulapi.DefaultConfig()
	config.Address = "10.0.0.102:8500"
	client, err := consulapi.NewClient(config)
	if err != nil {
		fmt.Println("consul client error : ", err)
	}
	//res, err := client.Agent().Services()
	res, err := client.Agent().ServicesWithFilter(`Service=="grpc_test"`)
	if err != nil {
		return "", 0,err
	}
	fmt.Println(res)
	for _,value:=range res{
		host=value.Address
		port=value.Port
	}
	return // 命名返回值
	
}
func main() {
	r:=gin.Default()
	r.GET("/index", func(c *gin.Context) {
		host,port,_:=getFirstGrpcRegister()
		fmt.Println(host,port)
		// 第一步:连接服务端
		conn, err := grpc.Dial(fmt.Sprintf("%s:%d",host,port), grpc.WithTransportCredentials(insecure.NewCredentials()))
		if err != nil {
			fmt.Println(err)
			c.JSON(200,"连接grpc服务异常")
		}
		//defer 关闭
		defer conn.Close()
		// 第二步:创建客户端调用
		client := proto.NewGreeterClient(conn)
		// 测试默认值
		resp,err:=client.SayHello(context.Background(),&proto.HelloRequest{
			Name: "lqz",
			Age: 19,
		})
		if err != nil {
			c.JSON(200,"服务器错误")
		}
		c.JSON(200,resp.Reply)

	})

	r.Run()
}

(3)grpc_srv/proto/hello.proto

syntax = "proto3";
option go_package = ".;proto";

service Greeter{
  rpc SayHello (HelloRequest) returns (HelloResponse) {}

}

// 类似于go的结构体,可以定义属性
message HelloRequest {
  string name = 1; // 1 是编号,不是值
  int32 age = 2;

}
// 定义一个响应的类型
message HelloResponse {
  string reply =1;
}

(4)生成go文件

protoc --go_out=. ./hello.proto
protoc --go-grpc_out=. --go-grpc_opt=require_unimplemented_servers=false ./hello.proto

(5)grpc_srv/server/main.go

package main

import (
	"context"
	"fmt"
	consulapi "github.com/hashicorp/consul/api"
	"google.golang.org/grpc"
	"google.golang.org/grpc/health"
	"google.golang.org/grpc/health/grpc_health_v1"
	"grpc_proto_demo/grpc_gin_consul/grpc_srv/proto"
	"net"
)

type GreeterServer struct {
}

// proto 的service中只写了一个方法,现在只写一个,如果写了多个,都要实现
func (h GreeterServer) SayHello(ctx context.Context, in *proto.HelloRequest) (*proto.HelloResponse, error) {
	// 接收客户端发送过来的数据,打印出来
	fmt.Println("客户端传入的名字是:", in.Name)
	fmt.Println("客户端传入的年龄是:", in.Age)
	return &proto.HelloResponse{
		Reply: "gin-调用grpc,grpc给的回复",
	}, nil
}

// 服务端代码
func main() {
	// 第一步:new一个server
	g := grpc.NewServer()
	// 第二步:生成一个结构体对象
	s := GreeterServer{}
	// 第三步: 把s注册到g对象中
	proto.RegisterGreeterServer(g, &s)
	// 第四步:启动服务,监听端口
	lis, error := net.Listen("tcp", "192.168.31.226:50052")
	if error != nil {
		panic("启动服务异常")
	}

	//******** 注册grpc服务和设置健康检查********
	// 1 设置健康检查
	//health.NewServer()具体实现grpc已经帮我们写好了
	grpc_health_v1.RegisterHealthServer(g,health.NewServer())
	// 2 注册grpc服务
	RegisterConsul("192.168.31.226",50052,"grpc_test","grpc_test001",[]string{"grpc","lqz"})


	g.Serve(lis)

}

func RegisterConsul(localIP string, localPort int, name string,id string, tags []string) error {
	// 创建连接consul服务配置
	config := consulapi.DefaultConfig()
	config.Address = "10.0.0.102:8500"
	client, err := consulapi.NewClient(config)
	if err != nil {
		fmt.Println("consul client error : ", err)
	}

	// 创建注册到consul的服务到
	registration := new(consulapi.AgentServiceRegistration)
	registration.ID = id
	registration.Name = name //根据这个名称来找这个服务
	registration.Port = localPort
	//registration.Tags = []string{"lqz", "gin_web"} //这个就是一个标签,可以根据这个来找这个服务,相当于V1.1这种
	registration.Tags = tags //这个就是一个标签,可以根据这个来找这个服务,相当于V1.1这种
	registration.Address = localIP

	// 增加consul健康检查回调函数
	check := new(consulapi.AgentServiceCheck)
	check.GRPC = "192.168.31.226:50052" // 健康检查地址只需要写grpc服务地址端口即可,会自动检查
	check.Timeout = "5s"                         //超时
	check.Interval = "5s"                        //健康检查频率
	check.DeregisterCriticalServiceAfter = "30s" // 故障检查失败30s后 consul自动将注册服务删除
	registration.Check = check
	// 注册服务到consul
	err = client.Agent().ServiceRegister(registration)
	if err != nil {
		return err
	}
	return nil

}

标签:服务,中心,err,proto,grpc,consul,注册
From: https://www.cnblogs.com/Mcoming/p/18080577

相关文章

  • 262:vue+openlayers 移动地图获取中心点经纬度信息
    第262个点击查看专栏目录本示例介绍演示如何在vue+openlayers中移动地图并获取中心点经纬度信息。这里主要用到了是view的getCenter方法,这一功能在实际项目中很有用,能给出一个清晰的定位。直接复制下面的vue+openlayers源代码,操作2分钟即可运行实现效果文......
  • 香港公司变更注册地址所需材料及流程全解析
    香港公司变更注册地址:所需材料及流程全解析      各位老板好,我是经典世纪胡云帅,随着业务的拓展和发展,香港公司可能需要变更其注册地址。变更注册地址不仅关系到公司的日常运营,还与公司的法律地位和品牌形象息息相关。本文将为您详细介绍香港公司变更注册地址所需的材......
  • Navicat 16 注册教程
     下载NavicateCracker.exe:https://files.cnblogs.com/files/blogs/667310/NavicatCracker.zip?t=1710724387&download=trueNavicat16注册教程 先断网!先断网!!先断网!!!1)以管理员身份运行注册机。2)点击··,选择实际安装目录,再点击`Patch` 正常弹出下面......
  • 去中心化钱包的交易设计
    一、上币 二、购买币基本流程是:前端从后台获取到全部的购买服务供应商信息,包括名称、支持的法币列表、支持的法币->token购买列表;用户在前端先选择要支付法币、要花费的金额、购入token存放地址;确定后,转换到列表展示每个供应商可买入的token数量、费用等要维护的供应商......
  • 【SpringBoot】头条新闻项目实现CRUD登录注册
    文章目录一、头条案例介绍二、技术栈介绍三、前端搭建四、基于SpringBoot搭建项目基础架构4.1数据库脚本执行4.2搭建SprintBoot工程4.2.1导入依赖:4.2.2编写配置4.2.3工具类准备4.3MybatisX逆向工程五、后台功能开发5.1用户模块开发5.1.1jwt和token介绍5.1.......
  • JS实现之用户注册协议倒计时
    要求:按钮显示5秒倒计时,5秒前禁点,5秒后可以点击按钮运行结果:5秒前5秒后思路开启禁用按钮属性disabled获取按钮元素开启倒计时函数setInterval调用函数,函数里包括变量自减,使用DOM对象修改时间,关闭按钮禁用属性HTML代码:首先禁用按钮属性<textareaname=""id=""......
  • 【干货】Onlyfans注册订阅手把手教程
    前言本文来教大家如何注册OnlyFans,并且如何通过支付宝(ps:是使用支付宝购买虚拟卡来实现)订阅和支付 OnlyFans ,并且提供年龄验证等问题解决方案一、OnlyFans介绍OnlyFans是一个允许创作者分享自己的独家内容的平台,简称o站。这个平台允许创作者创建一个订阅服务,粉丝需要支付......
  • 全套大型体检中心PEIS源码 医院PEIS管理系统源码
    大型体检中心PEIS源码 医院PEIS管理系统源码医院智慧体检系统,该系统覆盖医院体检科、体检中心的所有业务,完成从预约、登记、收费、检查、检验、出报告、分析、报表等所有工作。系统可以对团检的每个环节设有操作界面,从检前的预约、记录、EXCEL数据批量导入、自动筛选、自......
  • setup factory添加注册表
       result=SessionVar.Get("%AppFolder%");proversion=SessionVar.Get("%ProductVer%");Registry.CreateKey(HKEY_LOCAL_MACHINE,"SOFTWARE\\*******\\***")Registry.SetValue(HKEY_LOCAL_MACHINE,"SOFTWARE\\*******\\......
  • 马德里商标如何申请 马德里商标注册条件 马德里上商标续展
    一、谁可以注册使用马德里体系?只要与体系中的一个成员有个人或商业联系,就可以使用马德里体系。这意味着必须是马德里体系108个成员覆盖的124个国家其中之一的公民,或者拥有住所或工商营业场所。对于我国而言,申请人应在我国设有真实有效的工商营业场所;或在我国境内有住所......