[golang 微服务] 6. GRPC微服务集群+Consul集群+grpc-consul-resolver案例演示

news2024/11/27 2:41:39

一. GRPC微服务集群概念

上一节讲解了consul集群: [golang 微服务] 5. 微服务服务发现介绍,安装以及consul的使用,Consul集群,这样的话,当一台server挂掉之后,集群就会从另一台server中获取服务,这就保证了客户端访问consul集群的负载均衡性. 这里还有一个问题: 就是当终端的对应的 微服务挂掉了,consul集群server就不能访问对应的微服务了,这个怎么办呢?这就引入了 GRPC微服务集群, 那什么是GRPC微服务集群呢?
把一个GRPC微服务部署到多台不同的服务器上的功能,就叫 GRPC微服务集群, 这样当 其中一个微服务挂掉后 ,consul就会访问另外服务器上对应的微服务,从而实现 微服务的负载均衡
GRPC微服务集群主要实现的是 微服务的负载均衡,实现同样的微服务部署在不同的服务器的功能,结合Consul搭建GRPC微服务集群非常简单:
  • 同一个微服务的不同应用使用同样的注册名

  • 同一个微服务的不同应用注册服务的时候使用不同的ID

下面通过代码展示来更进一步了解微服务集群

二.代码展示

  1. 注销相关服务

在consul ui上查看是否存在goods服务,如果有,则先注销之前的goods服务,代码见 [golang 微服务] 5. 微服务服务发现介绍,安装以及consul的使用,Consul集群,注销后就没有对应的微服务了,界面展示如下:

点击client-1进入,查看里面的服务,发现服务没有了

  1. 进行微服务集群的部署

以goods微服务举例,把server/goods部署到2台服务器上,修改main.go中代码,以示区分不同服务器的同一个微服务
比如:把goods部署到一台192.168.1.111上,main.go代码如下:
package main

import (
    "context"
    "fmt"
    "github.com/hashicorp/consul/api"
    "goods/proto/goodsService"
    "net"
    "google.golang.org/grpc"
    "strconv"
)

//rpc远程调用的接口,需要实现goods.proto中定义的Goods服务接口,以及里面的方法
//1.定义远程调用的结构体和方法,这个结构体需要实现GoodsServer的接口

type Goods struct{}

//GoodsServer方法参考goods.pb.go中的接口
/*
// GoodsServer is the server API for Goods service.
type GoodsServer interface {
    // 通过rpc来指定远程调用的方法:
    // AddGoods方法:增加商品, 这个方法里面实现对传入的参数AddGoodsReq, 以及返回的参数AddGoodsRes进行约束
    AddGoods(context.Context, *AddGoodsReq) (*AddGoodsRes, error)
    // 获取商品列表: GetGoodsReq 参数可为空, 返回参数GetGoodsRes是一个商品相关的切片
    GetGoods(context.Context, *GetGoodsReq) (*GetGoodsRes, error)
}
*/
//增加商品数据
func (this Goods) AddGoods(c context.Context, req *goodsService.AddGoodsReq) (*goodsService.AddGoodsRes, error) {
    fmt.Println(req)
    //模拟返回操作,正式项目在这里进行数据库的操作即可,根据操作结果,返回相关数据
    return &goodsService.AddGoodsRes{
        Message: "第一个goods微服务-增加数据成功",
        Success: true,
    }, nil
}

//获取商品列表
func (g Goods) GetGoods(c context.Context, req *goodsService.GetGoodsReq) (*goodsService.GetGoodsRes, error) {
    //  GoodsList []*GoodsModel
    var tempList []*goodsService.GoodsModel  //定义返回的商品列表切片
    //模拟从数据库中获取商品的请求,循环结果,把商品相关数据放入tempList切片中
    for i := 0; i < 10; i++ {
        tempList = append(tempList, &goodsService.GoodsModel{
            Title:   "商品" + strconv.Itoa(i),  // strconv.Itoa(i): 整型转字符串类型
            Price:   float64(i),  //float64(i): 强制转换整型为浮点型
            Content: "测试商品内容" + strconv.Itoa(i),
        })
    }
    return &goodsService.GetGoodsRes{
        GoodsList: tempList,
    }, nil
}

func main() {
    //------------------------- consul服务相关----------------------
    //注册consul服务
    //1、初始化consul配置
    consulConfig := api.DefaultConfig()
    //设置consul服务器地址: 默认127.0.0.1:8500, 如果consul部署到其它服务器上,则填写其它服务器地址
    consulConfig.Address = "192.168.1.132:8500"
    //2、获取consul操作对象
    consulClient, _ := api.NewClient(consulConfig)
    // 3、配置注册服务的参数
    agentService := api.AgentServiceRegistration{
        ID:      "1",  // 服务id,顺序填写即可
        Tags:    []string{"goods"},  // tag标签
        Name:    "GoodsService",  //服务名称, 注册到服务发现(consul)的K
        Port:    8080, // 端口号: 需要与下面的监听, 指定 IP、port一致
        Address: "192.168.1.111",  // 当前微服务部署    地址: 结合Port在consul设置为V: 需要与下面的监听, 指定 IP、port一致
        Check: &api.AgentServiceCheck{  //健康检测
            TCP:      "192.168.1.111:8080",  //前微服务部署地址,端口 : 需要与下面的监听, 指定 IP、port一致
            Timeout:  "5s",  // 超时时间
            Interval: "30s",  // 循环检测间隔时间
        },
    }

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

    //1. 初始一个 grpc 对象
    grpcServer := grpc.NewServer()
    //2. 注册服务
    //helloService.RegisterGoodsServer(grpcServer, &Goods{})
    // &Hello{}和 new(Hello)相同
    goodsService.RegisterGoodsServer(grpcServer, new(Goods))
    //3. 设置监听, 指定 IP、port
    listener, err := net.Listen("tcp", "192.168.1.111:8080")
    if err != nil {
        fmt.Println(err)
    }
    // 4退出关闭监听
    defer listener.Close()
    //5、启动服务
    grpcServer.Serve(listener)
}
再把goods部署到另一台192.168.1.112上,main.go代码如下:
package main

import (
    "context"
    "fmt"
    "github.com/hashicorp/consul/api"
    "goods/proto/goodsService"
    "net"
    "google.golang.org/grpc"
    "strconv"
)

//rpc远程调用的接口,需要实现goods.proto中定义的Goods服务接口,以及里面的方法
//1.定义远程调用的结构体和方法,这个结构体需要实现GoodsServer的接口

type Goods struct{}

//GoodsServer方法参考goods.pb.go中的接口
/*
// GoodsServer is the server API for Goods service.
type GoodsServer interface {
    // 通过rpc来指定远程调用的方法:
    // AddGoods方法:增加商品, 这个方法里面实现对传入的参数AddGoodsReq, 以及返回的参数AddGoodsRes进行约束
    AddGoods(context.Context, *AddGoodsReq) (*AddGoodsRes, error)
    // 获取商品列表: GetGoodsReq 参数可为空, 返回参数GetGoodsRes是一个商品相关的切片
    GetGoods(context.Context, *GetGoodsReq) (*GetGoodsRes, error)
}
*/
//增加商品数据
func (this Goods) AddGoods(c context.Context, req *goodsService.AddGoodsReq) (*goodsService.AddGoodsRes, error) {
    fmt.Println(req)
    //模拟返回操作,正式项目在这里进行数据库的操作即可,根据操作结果,返回相关数据
    return &goodsService.AddGoodsRes{
        Message: "第二个goods微服务-增加数据成功",
        Success: true,
    }, nil
}

//获取商品列表
func (g Goods) GetGoods(c context.Context, req *goodsService.GetGoodsReq) (*goodsService.GetGoodsRes, error) {
    //  GoodsList []*GoodsModel
    var tempList []*goodsService.GoodsModel  //定义返回的商品列表切片
    //模拟从数据库中获取商品的请求,循环结果,把商品相关数据放入tempList切片中
    for i := 0; i < 10; i++ {
        tempList = append(tempList, &goodsService.GoodsModel{
            Title:   "商品" + strconv.Itoa(i),  // strconv.Itoa(i): 整型转字符串类型
            Price:   float64(i),  //float64(i): 强制转换整型为浮点型
            Content: "测试商品内容" + strconv.Itoa(i),
        })
    }
    return &goodsService.GetGoodsRes{
        GoodsList: tempList,
    }, nil
}

func main() {
    //------------------------- consul服务相关----------------------
    //注册consul服务
    //1、初始化consul配置
    consulConfig := api.DefaultConfig()
    //设置consul服务器地址: 默认127.0.0.1:8500, 如果consul部署到其它服务器上,则填写其它服务器地址
    consulConfig.Address = "192.168.1.132:8500"
    //2、获取consul操作对象
    consulClient, _ := api.NewClient(consulConfig)
    // 3、配置注册服务的参数
    agentService := api.AgentServiceRegistration{
        ID:      "2",  // 服务id,顺序填写即可
        Tags:    []string{"goods"},  // tag标签
        Name:    "GoodsService",  //服务名称, 注册到服务发现(consul)的K
        Port:    8080, // 端口号: 需要与下面的监听, 指定 IP、port一致
        Address: "192.168.1.112",  // 当前微服务部署    地址: 结合Port在consul设置为V: 需要与下面的监听, 指定 IP、port一致
        Check: &api.AgentServiceCheck{  //健康检测
            TCP:      "192.168.1.112:8080",  //前微服务部署地址,端口 : 需要与下面的监听, 指定 IP、port一致
            Timeout:  "5s",  // 超时时间
            Interval: "30s",  // 循环检测间隔时间
        },
    }

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

    //1. 初始一个 grpc 对象
    grpcServer := grpc.NewServer()
    //2. 注册服务
    //helloService.RegisterGoodsServer(grpcServer, &Goods{})
    // &Hello{}和 new(Hello)相同
    goodsService.RegisterGoodsServer(grpcServer, new(Goods))
    //3. 设置监听, 指定 IP、port
    listener, err := net.Listen("tcp", "192.168.1.112:8080")
    if err != nil {
        fmt.Println(err)
    }
    // 4退出关闭监听
    defer listener.Close()
    //5、启动服务
    grpcServer.Serve(listener)
}
这两台服务器上的代码 不同之处: ID一定要不一样 ,Port为对应服务器上的端口号 ,Address为对应服务器的ip,Name(服务名称),标签(Tag)一定要一致,其他地方不变, 这样 负载均衡的微服务集群就准备好了
然后在这两台服务器上启动服务: go run main.go,这样就注册goods微服务到consul服务发现集群中了,如下图:

GoodsService服务图示:

  1. 客户端进行调度

客户端请求微服务, 要达到这样的效果: 一个客户请求访问192.168.1.111这台goods微服务,另一个客户请求访问192.168.1.112这台goods微服务; 要实现该效果,就要在客户端这边修改相关代码,有两种方式, 如下:

方式一

在客户端client/main.go代码中操作: 在拼接地址步骤时, 写一个算法(随机取获取返回的几个地址的切片),不直接取值
package main

import (
    "client/proto/goodsService"
    "context"
    "fmt"
    "github.com/hashicorp/consul/api"
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials/insecure"
    "strconv"
)

func main() {
    //----------------------------consul相关---------------------------
    //初始化consul配置, 客户端服务器需要一致
    consulConfig := api.DefaultConfig()
    //设置consul服务器地址: 默认127.0.0.1:8500, 如果consul部署到其它服务器上,则填写其它服务器地址
    consulConfig.Address = "192.168.1.132:8500"
    //2、获取consul操作对象
    consulClient, _ := api.NewClient(consulConfig)  //目前先屏蔽error,也可以获取error进行错误处理

    ----------------------------goods微服务相关--------------------------

    //3、获取consul服务发现地址,返回的ServiceEntry是一个结构体数组
    //参数说明:service:服务名称,服务端设置的那个Name, tag:标签,服务端设置的那个Tags,, passingOnly bool, q: 参数
    serviceGoodsEntry, _, _ := consulClient.Health().Service("GoodsService", "test", false, nil)
    
    //拼接地址: 写一个算法(随机取获取返回的几个地址的切片),不直接取值
    //strconv.Itoa: int转string型
    addressGoods := serviceGoodsEntry[0].Service.Address + ":" + strconv.Itoa(serviceGoodsEntry[0].Service.Port)

    // 1、连接服务器
    /*
        credentials.NewClientTLSFromFile :从输入的证书文件中为客户端构造TLS凭证。
        grpc.WithTransportCredentials :配置连接级别的安全凭证(例如,TLS/SSL),返回一个
        DialOption,用于连接服务器。

    */
    grpcGoodsClient, err := grpc.Dial(addressGoods, grpc.WithTransportCredentials(insecure.NewCredentials()))

    if err != nil {
        fmt.Println(err)
    }
    //2、注册客户端
    clientGoods := goodsService.NewGoodsClient(grpcGoodsClient)
    //增加
    res1, _ := clientGoods.AddGoods(context.Background(), &goodsService.AddGoodsReq{
        Goods: &goodsService.GoodsModel{
            Title:   "测试商品",
            Price:   20,
            Content: "测试商品的内容",
        },
    })
    fmt.Println(res1.Message)
    fmt.Println(res1.Success)

    //获取商品数据
    res2, _ := clientGoods.GetGoods(context.Background(), &goodsService.GetGoodsReq{})
    fmt.Printf("%#v", res2.GoodsList)

    for i := 0; i < len(res2.GoodsList); i++ {
        fmt.Printf("%#v\r\n", res2.GoodsList[i])
    }
}

方式二

使用 grpc-consul-resolver实现 域名解析, 在这里需要引入grpc-consul-resolver,直接使用命令 go mod tidy 操作
 package main

import (
    "client/proto/goodsService"
    "context"
    "fmt"
    "github.com/hashicorp/consul/api"
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials/insecure"
    _ "github.com/mbobakov/grpc-consul-resolver" // 必须引入
    "strconv"
)

func main() {
    //直接进行连接服务器拨号操作
    grpcGoodsClient, err := grpc.Dial(
        "consul://192.168.234.132:8500/GoodsService",  //服务发现地址+服务名称
        grpc.WithTransportCredentials(insecure.NewCredentials()),
        //轮询调度策略
        grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "round_robin"}`),
    )

    if err != nil {
        fmt.Println(err)
    }

    //2、注册客户端
    clientGoods := goodsService.NewGoodsClient(grpcGoodsClient)
    //增加
    res1, _ := clientGoods.AddGoods(context.Background(), &goodsService.AddGoodsReq{
        Goods: &goodsService.GoodsModel{
            Title:   "测试商品",
            Price:   20,
            Content: "测试商品的内容",
        },
    })
    fmt.Println(res1.Message)
    fmt.Println(res1.Success)

    //获取商品数据
    res2, _ := clientGoods.GetGoods(context.Background(), &goodsService.GetGoodsReq{})
    fmt.Printf("%#v", res2.GoodsList)

    for i := 0; i < len(res2.GoodsList); i++ {
        fmt.Printf("%#v\r\n", res2.GoodsList[i])
    }
}
使用 grpc-consul-resolver 进行操作,省去了consul相关操作对象连接,要方便一下

然后运行go run main.go,可以看到,该客户端调用微服务是随机的

好了,微服务集群就搭建好了,这样就实现了: 当一台微服务挂了,客户端照样能够调度访问,因为实现了微服务的负载均衡操作

[上一节][golang 微服务] 5. 微服务服务发现介绍,安装以及consul的使用,Consul集群

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/647197.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

opencv初学记录

准备工作&#xff1a; 1.找一张图片 2.准备python运行环境&#xff0c;并导入库&#xff0c;pip install opencv-python 读取文件&#xff0c;并打印维度 import cv2 #为什么是cv2不是cv呢&#xff0c;这个2指的是c的api&#xff0c;是为了兼容老板&#xff0c;cv指的就是c&am…

设计模式(十七):行为型之状态模式

设计模式系列文章 设计模式(一)&#xff1a;创建型之单例模式 设计模式(二、三)&#xff1a;创建型之工厂方法和抽象工厂模式 设计模式(四)&#xff1a;创建型之原型模式 设计模式(五)&#xff1a;创建型之建造者模式 设计模式(六)&#xff1a;结构型之代理模式 设计模式…

强化学习笔记-12 Eligibility Traces

前篇讨论了TD算法将MC同Bootstrap相结合&#xff0c;拥有很好的特性。本节所介绍的Eligibility Traces&#xff0c;其思想是多个TD(n)所计算预估累积收益按权重进行加权平均&#xff0c;从而得到更好的累积收益预估值。 价值预估模型的参数更新式子可以调整为&#xff1a; 1. O…

Vue CLI $nextTick 过渡与动画

3.12.$nextTick 这是一个生命周期钩子 **this.$nextTick(回调函数)**在下一次DOM更新结束后执行其指定的回调 什么时候用&#xff1a;当改变数据后&#xff0c;要基于更新后的新DOM进行某些操作时&#xff0c;要在nextTick所指定的回调函数中执行 使用 $nextTick 优化 Todo…

【UE 从零开始制作坦克】2-控制坦克移动(简单的移动效果)

效果 步骤 1. 新建蓝图类&#xff0c;父类选择“VehicleWheel&#xff08;载具车轮&#xff09;” 这里就命名为“TankWheel” 双击打开“TankWheel”&#xff0c;设置形状半径为40 2. 打开 “BP_West_Tank_M1A1Abrams” 选中“网格体&#xff08;VehicleMesh&#xff09;&…

DeepSurvk部署教程

DeepSurvk部署教程 作者:千树、Totoro github项目地址 https://github.com/arturomoncadatorres/deepsurvk Pypi项目地址 https://pypi.org/project/deepsurvk/ 一、DeepSurvk简介 项目作者原话(翻译) DeepSurv 是一种 Cox 比例风险深度神经网络&#xff0c;用于模拟患者协变…

某网站指纹反爬处理

一、问题分析 【疑惑】&#xff1a;使用python的requests库发起get或post请求返回403代码错误&#xff0c;使用postman发起请求发现状态码<200>竟然成功了。这是什么原因&#xff1f;首先排除ip问题&#xff0c;ip有问题的话postman也访问不了。难道是headers出现了问题…

VanillaNet实战:使用VanillaNet实现图像分类

文章目录 摘要安装包安装timm安装 grad-cam 数据增强Cutout和MixupEMA项目结构计算mean和std生成数据集 摘要 论文翻译&#xff1a;https://blog.csdn.net/m0_47867638/article/details/131057152 官方源码&#xff1a;https://github.com/huawei-noah/VanillaNet VanillaNet…

【送书福利-第十二期】机工社Python与AI好书来袭!~

大家好&#xff0c;我是洲洲&#xff0c;欢迎关注&#xff0c;一个爱听周杰伦的程序员。关注公众号【程序员洲洲】即可获得10G学习资料、面试笔记、大厂独家学习体系路线等…还可以加入技术交流群欢迎大家在CSDN后台私信我&#xff01; 本文目录 一、前言二、书籍介绍1、认识AI…

SQL Server 数据加密功能解析

数据加密是数据库被破解、物理介质被盗、备份被窃取的最后一道防线&#xff0c;数据加密&#xff0c;一方面解决数据被窃取安全问题&#xff0c;另一方面有关法律要求强制加密数据。SQL Server的数据加密相较于其他数据库&#xff0c;功能相对完善&#xff0c;加密方法较多。通…

技术背后的温度,夸克App升级智能、普惠、公益高考服务

夸克2023高考信息服务进行了全面升级&#xff0c;通过信息查询、填报工具及专家指导等多维度产品及内容&#xff0c;给考生打造个性化、全周期、全链路的智能信息服务&#xff0c;以提升考生和家长志愿决策的效率。 6月14日&#xff0c;夸克App升级智能选志愿、志愿表等高考信息…

【Java基础学习打卡06】编程语言

目录 引言一、计算机语言是什么&#xff1f;二、计算机语言分类三、计算机语言介绍1.C语言2.C语言3.Java语言4.Python语言 总结 引言 本文主要是理解计算机语言是什么&#xff0c;有哪些分类&#xff0c;分类下有哪些编程语言&#xff0c;以及了解主流的编程语言。 一、计算机…

【源码可分享】教你用Python制作自动答题脚本,实现自动答题,100%正确率!

文章目录 前言一、自动答题的原理二、自动答题的步骤三、Python实现自动答题的方法总结 前言 当今社会&#xff0c;人们的生活越来越依赖于计算机技术&#xff0c;而Python作为一种高级编程语言&#xff0c;已经成为了众多程序员的首选语言。Python具有简单易学、代码简洁、可…

【Java基础学习打卡08】Java语言跨平台原理

目录 引言一、Java程序运行机制二、Java虚拟机三、Java跨平台总结 引言 Java语言编程的一大优势便是跨平台&#xff0c;本文将介绍Java语言是如何实现跨平台的。 一、Java程序运行机制 计算机高级语言按照程序的执行方式可以分为编译型语言和解释型语言。 编译型语言&#x…

一、PyTorch基础

一、PyTorch基本操作 1&#xff0c;导包 import torch2&#xff0c;查看版本号 torch.__version__ """ 2.0.1cpu """3&#xff0c;初始化(全零)矩阵 x torch.empty(3,2) x """ tensor([[7.2868e-44, 8.1275e-44],[6.7262e-4…

教你用Fiddler捕获HTTPS请求

安装Fiddler 这里不特别说明了&#xff0c;网上搜索一大把&#xff0c;根据安装引导一步步安装即可。&#xff08;这里采用的是fiddler v4.6&#xff09; 配置Fiddler 1、打开fiddler配置Tools –>Telerik Fiddler Options。 如果你想学习Fiddler抓包工具&#xff0c;我这…

vue基础--重点

&#xff01;1、vue的特性 &#xff01;2、v-model 双向数据绑定指令 &#xff08;data数据源变化&#xff0c;页面变化&#xff1b; 页面变化&#xff0c;data数据源也变化&#xff09; 1、v-model 会感知到 框中数据变化 2、v-model 只有在表单元素中使用&#xff0c;才能…

CSS体验透视效果

CSS体验透视效果 使用css视距属性perspective结合动画效果实现透视效果。 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><me…

金融数字化转型将驶向何处?存储如何保驾护航?

文 | 螳螂观察 作者 | 李永华 “XX行业数字化进入深水区”&#xff0c;经常被谈及。 在金融行业&#xff0c;“深水区”说法却很少出现——这里的数字化始终在“深水区”。 作为数字经济的重要组成部分&#xff0c;金融行业一直走在数字化转型的前沿&#xff0c;总是有新的…

11-执行上下文和执行栈

一、执行上下文 &#x1f35f;&#x1f35f;&#x1f35f;是一种对js代码执行环境的抽象概念 只要有js代码运行&#xff0c;一定是运行在执行上下文中 执行上下文的类型分为三种&#xff1a; 全局执行上下文&#xff1a;只有一个&#xff0c;浏览器中的全局对象就是 window对…