【微服务网关——服务发现】

1.服务发现

1.1 介绍

  • 服务发现是指用注册中心来记录服务信息,以便其他服务快速查找已注册服务
  • 服务发现分类:
    • 客户端服务发现
    • 服务端服务发现

1.2 客户端服务发现

客户端服务发现(Client-side Service Discovery)是一种微服务架构中的模式,用于让客户端应用动态地发现并调用其他服务的实例,而无需通过一个中介(例如负载均衡器或服务网关)。它通常用于分布式系统中,通过客户端直接决定并选择与哪个服务实例通信,从而实现服务发现和负载均衡。
在这里插入图片描述

1.3 服务端服务发现

服务端服务发现(Server-side Service Discovery)是另一种服务发现模式,与客户端服务发现相对。在这种模式中,服务的实例发现和负载均衡由服务端组件处理,客户端只需将请求发送给一个固定的入口点(如负载均衡器或 API 网关),由这个入口点负责将请求路由到合适的服务实例。
在这里插入图片描述

2.zookeeper

2.1 zookeeper介绍

Apache ZooKeeper 是一个用于分布式系统中的协调服务。它提供了一套高效、可靠的分布式协调工具,用于实现服务注册、配置管理、同步、领导者选举等功能。Zookeeper 的设计初衷是简化分布式应用中的协调任务,从而使应用开发更容易。

  • 是一个分布式数据库(程序协调服务),Hadoop子项目
  • 树状方式维护节点方数据的增、删、改、查
  • 监听通知机制:通过监听可以获取相应消息事件(内容,子节点)

2.2 zookeeper安装

安装zookeeper

  • 参考官方文档安装
    • http://zookeeper.apache.org/doc/r3.6.0/zookeeperStarted.html
    • 下载时需要注意下载的是编译过的二进制文件,不是源码
    • 不然会爆错:找不到或无法加载主类 org.apache.zookeeper.server.quorum.QuorumPeerMain
  • 解压缩
  • 编辑 conf/zoo.cfg
tickTime=2000
dataDir=/var/lib/zookeeper
clientPort=2181
  • 运行 bin/zkServer.sh start

2.3 zookeeper核心功能

在这里插入图片描述

  • 持久节点
    • 一直存在服务器上
  • 临时节点
    • 会话失效,节点自动清理
  • 顺序节点
    • 节点创建,自动分配序列号

2.3.1 增删改查API

package mainimport ("fmt""github.com/samuel/go-zookeeper/zk""time"
)var (host = []string{"127.0.0.1:2181"}
)func main() {//连接客户端conn, _, err := zk.Connect(host, 5*time.Second)if err != nil {panic(err)}//增if _, err := conn.Create("/test_tree2", []byte("tree_content"),0, zk.WorldACL(zk.PermAll)); err != nil {fmt.Println("create err", err)}//查nodeValue, dStat, err := conn.Get("/test_tree2")if err != nil {fmt.Println("get err", err)return}fmt.Println("nodeValue", string(nodeValue))//改,需要先查询得到版本号if _, err := conn.Set("/test_tree2", []byte("new_content"),dStat.Version); err != nil {fmt.Println("update err", err)}//删除,也,需要先查询得到版本号_, dStat, _ = conn.Get("/test_tree2")if err := conn.Delete("/test_tree2", dStat.Version); err != nil {fmt.Println("Delete err", err)//return}//验证存在hasNode, _, err := conn.Exists("/test_tree2")if err != nil {fmt.Println("Exists err", err)//return}fmt.Println("node Exist", hasNode)//增加if _, err := conn.Create("/test_tree2", []byte("tree_content"),0, zk.WorldACL(zk.PermAll)); err != nil {fmt.Println("create err", err)}//设置子节点,如果上游节点不存在则会报错if _, err := conn.Create("/test_tree2/subnode", []byte("node_content"),0, zk.WorldACL(zk.PermAll)); err != nil {fmt.Println("create err", err)}//获取子节点列表childNodes, _, err := conn.Children("/test_tree2")if err != nil {fmt.Println("Children err", err)}fmt.Println("childNodes", childNodes)
}

2.3.2 监听子节点变化

package mainimport ("fmt""github.com/e421083458/gateway_demo/proxy/zookeeper""log""os""os/signal""syscall"
)var addr = "127.0.0.1:2002"func main() {//获取zk节点列表zkManager := zookeeper.NewZkManager([]string{"127.0.0.1:2181"})zkManager.GetConnect()defer zkManager.Close()// 注册一个节点err := zkManager.RegistServerPath("/real_server", "127.0.0.1")err = zkManager.RegistServerPath("/real_server/test", "127.0.0.1:8823")err = zkManager.RegistServerPath("/real_server/test2", "127.0.0.1:8823")if err != nil {return}// 获取节点列表zlist, err := zkManager.GetServerListByPath("/real_server")fmt.Println("server node:")fmt.Println(zlist)if err != nil {log.Println(err)}//动态监听节点变化chanList, chanErr := zkManager.WatchServerListByPath("/real_server")go func() {for {select {case changeErr := <-chanErr:fmt.Println("changeErr")fmt.Println(changeErr)case changedList := <-chanList:fmt.Println("watch node changed")fmt.Println(changedList)}}}()time.Sleep(time.Second * 5)zkManager.RegistServerPath("/real_server/test3", "127.0.0.2:8888")//关闭信号监听quit := make(chan os.Signal)signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)<-quit
}

在这里插入图片描述

2.3.3 监听节点内容变化

package mainimport ("fmt""github.com/e421083458/gateway_demo/proxy/zookeeper""log""os""os/signal""syscall""time"
)var addr = "127.0.0.1:2002"
func main() {//获取zk节点列表zkManager := zookeeper.NewZkManager([]string{"127.0.0.1:2181"})zkManager.GetConnect()defer zkManager.Close()// 注册一个节点err := zkManager.RegistServerPath("/rs_server_conf", "192.168.1.101")if err != nil {fmt.Printf("2001:%v \n", err)return}// 获取节点列表zlist, err := zkManager.GetServerListByPath("/rs_server_conf")fmt.Println("server node:")fmt.Println(zlist)if err != nil {log.Println(err)}获取节点内容zc, _, err := zkManager.GetPathData("/rs_server_conf")if err != nil {log.Println(err)}fmt.Println("get node data:")fmt.Println(string(zc))//动态监听节点内容dataChan, dataErrChan := zkManager.WatchPathData("/rs_server_conf")go func() {for {select {case changeErr := <-dataErrChan:fmt.Println("changeErr")fmt.Println(changeErr)case changedData := <-dataChan:fmt.Println("WatchGetData changed")fmt.Println(string(changedData))}}}()// 尝试修改内容time.Sleep(5 * time.Second)_, z, err := zkManager.GetPathData("/rs_server_conf")if err != nil {return}err = zkManager.SetPathData("/rs_server_conf", []byte(addr), z.Version)if err != nil {fmt.Sprintf("2002:%v \n", err)return}//关闭信号监听quit := make(chan os.Signal)signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)<-quit
}

在这里插入图片描述

3.网关实现服务发现原理

3.1 网关实现客户端服务发现

在这里插入图片描述

3.2 网关实现服务端服务发现

在这里插入图片描述

  • 将服务注册到zookeeper中
  • 网关通过监听zookeeper中的事件来感知变化

4.网关拓展服务发现

  • 下游机器启动时创建临时节点:节点名与内容为服务地址
  • 以观察者模式构建负载均衡配置LoadBalanceConf
  • 负载均衡配置LoadBalanceConf与负载均衡器整合

4.1 观察者模式

观察者模式(Observer Pattern)是一种行为设计模式,它定义了一种一对多的依赖关系,使得多个观察者对象可以同时监听某一个主题对象。当这个主题对象的状态发生变化时,会通知所有观察者对象,使它们能够自动更新。观察者模式常用于实现事件处理系统,如用户界面事件、订阅/发布系统等。

  • 关键概念
  • 主题(Subject):也称为发布者(Publisher),它维护一组观察者对象,并提供注册和移除观察者的方法。当主题的状态发生变化时,会通知所有观察者。
  • 观察者(Observer):也称为订阅者(Subscriber),它定义了一个更新接口,用于接收来自主题的通知。每个观察者在接收到通知后,可以执行特定的操作。
  • 通知(Notification):指的是主题状态变化时向观察者发送的信号或消息。

4.2 以观察者模式构建负载均衡配置

在这里插入图片描述

package load_balanceimport ("fmt""github.com/e421083458/gateway_demo/proxy/zookeeper"
)// 配置主题
type LoadBalanceConf interface {Attach(o Observer)GetConf() []stringWatchConf()UpdateConf(conf []string)
}type LoadBalanceZkConf struct {observers    []Observerpath         stringzkHosts      []stringconfIpWeight map[string]stringactiveList   []stringformat       string
}func (s *LoadBalanceZkConf) Attach(o Observer) {s.observers = append(s.observers, o)
}func (s *LoadBalanceZkConf) NotifyAllObservers() {for _, obs := range s.observers {obs.Update()}
}func (s *LoadBalanceZkConf) GetConf() []string {confList := []string{}for _, ip := range s.activeList {weight, ok := s.confIpWeight[ip]if !ok {weight = "50" //默认weight}confList = append(confList, fmt.Sprintf(s.format, ip)+","+weight)}return confList
}//更新配置时,通知监听者也更新
func (s *LoadBalanceZkConf) WatchConf() {zkManager := zookeeper.NewZkManager(s.zkHosts)zkManager.GetConnect()fmt.Println("watchConf")chanList, chanErr := zkManager.WatchServerListByPath(s.path)go func() {defer zkManager.Close()for {select {case changeErr := <-chanErr:fmt.Println("changeErr", changeErr)case changedList := <-chanList:fmt.Println("watch node changed")s.UpdateConf(changedList)}}}()
}//更新配置时,通知监听者也更新
func (s *LoadBalanceZkConf) UpdateConf(conf []string) {s.activeList = conffor _, obs := range s.observers {obs.Update()}
}func NewLoadBalanceZkConf(format, path string, zkHosts []string, conf map[string]string) (*LoadBalanceZkConf, error) {zkManager := zookeeper.NewZkManager(zkHosts)zkManager.GetConnect()defer zkManager.Close()zlist, err := zkManager.GetServerListByPath(path)if err != nil {return nil, err}mConf := &LoadBalanceZkConf{format: format, activeList: zlist, confIpWeight: conf, zkHosts: zkHosts, path: path}mConf.WatchConf()return mConf, nil
}type Observer interface {Update()
}type LoadBalanceObserver struct {ModuleConf *LoadBalanceZkConf
}func (l *LoadBalanceObserver) Update() {fmt.Println("Update get conf:", l.ModuleConf.GetConf())
}func NewLoadBalanceObserver(conf *LoadBalanceZkConf) *LoadBalanceObserver {return &LoadBalanceObserver{ModuleConf: conf,}
}

4.3 负载均衡配置LoadBalanceConf与负载均衡器整合

package mainimport ("github.com/e421083458/gateway_demo/proxy/load_balance""github.com/e421083458/gateway_demo/proxy/middleware"proxy2 "github.com/e421083458/gateway_demo/proxy/proxy""log""net/http"
)var (addr = "127.0.0.1:2002"
)func main() {mConf, err := load_balance.NewLoadBalanceZkConf("http://%s/base","/real_server",[]string{"127.0.0.1:2181"},map[string]string{"127.0.0.1:2003": "20"})if err != nil {panic(err)}rb := load_balance.LoadBanlanceFactorWithConf(load_balance.LbWeightRoundRobin, mConf)proxy := proxy2.NewLoadBalanceReverseProxy(&middleware.SliceRouterContext{}, rb)log.Println("Starting httpserver at " + addr)log.Fatal(http.ListenAndServe(addr, proxy))
}

4.4 客户端服务发现实现

网关主动通过心跳检测区检测客户端的服务
在这里插入图片描述

  • 下游机器启动时无需进行任何操作
  • 以观察者模式构建负载均衡配置LoadBalanceConf
  • 负载均衡配置固定时间频率监测下游节点健康状况
package load_balanceimport ("fmt""net""reflect""sort""time"
)const (//default check settingDefaultCheckMethod    = 0DefaultCheckTimeout   = 2DefaultCheckMaxErrNum = 2DefaultCheckInterval  = 5
)type LoadBalanceCheckConf struct {observers    []ObserverconfIpWeight map[string]stringactiveList   []stringformat       string
}func (s *LoadBalanceCheckConf) Attach(o Observer) {s.observers = append(s.observers, o)
}func (s *LoadBalanceCheckConf) NotifyAllObservers() {for _, obs := range s.observers {obs.Update()}
}func (s *LoadBalanceCheckConf) GetConf() []string {confList := []string{}for _, ip := range s.activeList {weight, ok := s.confIpWeight[ip]if !ok {weight = "50" //默认weight}confList = append(confList, fmt.Sprintf(s.format, ip)+","+weight)}return confList
}//更新配置时,通知监听者也更新
func (s *LoadBalanceCheckConf) WatchConf() {fmt.Println("watchConf")go func() {confIpErrNum := map[string]int{}for {changedList := []string{}for item, _ := range s.confIpWeight {conn, err := net.DialTimeout("tcp", item, time.Duration(DefaultCheckTimeout)*time.Second)//todo http statuscodeif err == nil {conn.Close()if _, ok := confIpErrNum[item]; ok {confIpErrNum[item] = 0}}if err != nil {if _, ok := confIpErrNum[item]; ok {confIpErrNum[item] += 1} else {confIpErrNum[item] = 1}}if confIpErrNum[item] < DefaultCheckMaxErrNum {changedList = append(changedList, item)}}sort.Strings(changedList)sort.Strings(s.activeList)if !reflect.DeepEqual(changedList, s.activeList) {s.UpdateConf(changedList)}time.Sleep(time.Duration(DefaultCheckInterval) * time.Second)}}()
}//更新配置时,通知监听者也更新
func (s *LoadBalanceCheckConf) UpdateConf(conf []string) {fmt.Println("UpdateConf", conf)s.activeList = conffor _, obs := range s.observers {obs.Update()}
}func NewLoadBalanceCheckConf(format string, conf map[string]string) (*LoadBalanceCheckConf, error) {aList := []string{}//默认初始化for item, _ := range conf {aList = append(aList, item)}mConf := &LoadBalanceCheckConf{format: format, activeList: aList, confIpWeight: conf}mConf.WatchConf()return mConf, nil
}

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

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

相关文章

Vine: 一种全新定义 Vue 函数式组件的解决方案

7月6日的 vue confg 大会上 ShenQingchuan 大佬介绍了他的 Vue Vine 项目&#xff0c; 一种全新定义 Vue 函数式组件的解决方案。 和 React 的函数式组件有异曲同工之妙&#xff0c;写起来直接起飞了。 让我们来快速体验一下 vine&#xff0c; 看看到底给我们带来了哪些惊喜吧…

[Python]配置邮件服务,发送邮件

本文以163邮件系统为例&#xff0c;登录之后&#xff0c;点击设置&#xff0c;开启如下设置项。 即可使用代码发送邮件&#xff0c;并携带附件。 开启SMTP 普通邮件 import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart# 1…

[算法] 优先算法(四):滑动窗口(下)

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏: &#x1f9ca; Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 &#x1f355; Collection与…

20240708 每日AI必读资讯

&#x1f916;破解ChatGPT惊人耗电&#xff01;DeepMind新算法训练提效13倍&#xff0c;能耗暴降10倍 - 谷歌DeepMind研究团队提出了一种加快AI训练的新方法——多模态对比学习与联合示例选择&#xff08;JEST&#xff09;&#xff0c;大大减少了所需的计算资源和时间。 - JE…

Java线程的创建·启动和休眠

一.线程的创建和启动 Java中创建线程的两种方式 ◆继承java.lang.Thread类 ◆实现java.lang.Runnable接口 ◆使用线程的步骤 继承Thread类创建线程 ◆自定义线程类继承自Thread类 ◆重写run()方法&#xff0c;编写线程执行体 ◆创建线程对象&#xff0c;调用start()方法启动…

目标检测YOLO实战应用案例100讲-基于深度学习的无人机影像小目标识别(续)

目录 3.2 实验平台和环境 3.3 实验评价指标 3.4 基础框架YOLOv5在无人机数据集上的实验 3.4.1 实验结果 3.4.2 结果分析 4基于深度学习的无人机影像目标检测算法 4.1 基于改进YOLOv5的小目标检测算法研究 4.1.1 增加注意力机制 4.1.2 增加检测层 4.1.3多尺…

2024年 春秋杯 网络安全联赛夏季赛 Web方向 题解WirteUp 部分

brother 题目描述&#xff1a;web哥&#xff0c;打点容易提权难。 打点就是最简单的SSTI。 执行下find / -user root -perm -4000 -print 2>/dev/null找一下具备suid权限的命令 /usr/lib/dbus-1.0/dbus-daemon-launch-helper /usr/bin/chsh /usr/bin/gpasswd /usr/bin/n…

互联网十万个为什么之什么是数据备份?

数据备份是按照一定的备份频率创建数据副本的过程&#xff0c;将重要的数据复制到其它位置或者存储介质&#xff0c;并对生成的副本保留一定的时长。备份通常储存在不同的物理介质或云端&#xff0c;以确保数据的连续性和完整性。有效的备份策略至关重要&#xff0c;以防止数据…

macos 10.15系统下载包,macOS Catalina for mac

macOS Catalina 让你喜欢的种种 Mac 体验都更进一步。你可以领略音乐、播客这两款全新 Mac app 的表演&#xff1b;在 Mac 上畅享各款自己心爱的 iPad app&#xff1b;拿起 iPad 和 Apple Pencil&#xff0c;拓展工作空间&#xff0c;释放创意灵感&#xff1b;再打开那些平时常…

【STL】vector的模拟实现

目录 vector的介绍和使用 vector的介绍 vector的使用 构造函数 迭代器 空间增长问题 vector的增删查改等 vector的迭代器失效问题 vector的模拟实现 insert reserve push_back push_front resize erase pop_back pop_front 代码 vector的介绍和使用 vecto…

【PB案例学习笔记】-29制作一个调用帮助文档的小功能

写在前面 这是PB案例学习笔记系列文章的第29篇&#xff0c;该系列文章适合具有一定PB基础的读者。 通过一个个由浅入深的编程实战案例学习&#xff0c;提高编程技巧&#xff0c;以保证小伙伴们能应付公司的各种开发需求。 文章中设计到的源码&#xff0c;小凡都上传到了gite…

个人视角,社会影响力:自媒体的魅力所在

随着数字化时代的到来&#xff0c;自媒体正成为信息传播领域的一场革命。个人视角与社会影响力的结合&#xff0c;赋予了自媒体独特的魅力。在传统媒体受限制的同时&#xff0c;自媒体为每个人提供了表达自己观点和思想的自由。个体的真实视角使得自媒体在信息传播中发挥着重要…

14-40 剑和诗人14 - 为什么机器学习需要合成数据

​​​​​​ 数据是人工智能的命脉。如果没有高质量、有代表性的训练数据&#xff0c;我们的机器学习模型将毫无用处。但随着神经网络规模越来越大、人工智能项目越来越雄心勃勃&#xff0c;人们对数据的需求也越来越大&#xff0c;我们面临着一场危机——现实世界的数据收集和…

如何优化 PostgreSQL 中对于复杂数学计算的查询?

文章目录 一、理解复杂数学计算的特点二、优化原则&#xff08;一&#xff09;索引优化&#xff08;二&#xff09;查询重写&#xff08;三&#xff09;数据库配置调整&#xff08;四&#xff09;使用数据库内置函数的优势 三、具体的优化方案和示例&#xff08;一&#xff09;…

华为开源自研AI框架昇思MindSpore应用案例:FCN图像语义分割

Mask R-CNN MaskRCNN是一种概念简单、灵活、通用的目标实例分割框架&#xff0c;在检测出图像中目标的同时&#xff0c;还为每一个实例生成高质量掩码。这种称为Mask R-CNN的方法&#xff0c;通过添加与现有边框检测分支平行的预测目标掩码分支&#xff0c;达到扩展Faster R-CN…

leetcode判断二分图

判断二分图 图的问题肯定要用到深度优先遍历或者广度优先遍历&#xff0c;但又不是单纯的深度优先遍历算法和广度优先遍历算法&#xff0c;而是需要在遍历的过程中加入与解决题目相关的逻辑。 题干中说了&#xff0c;这个图可能不是连通图&#xff0c;这个提示有什么作用呢&a…

shared_ptr 线程安全

为什么 shared_ptr 可以安全地在多个线程中共享&#xff1f; 循环引用 因为shared_ptr std::shared_ptr 的引用计数是线程安全的。这意味着你可以在多个线程中安全地拷贝、赋值和销毁 std::shared_ptr。然而&#xff0c;访问或修改 shared_ptr 所指向的对象时&#xff0c;需要…

昇思25天学习打卡营第20天|LSTM+CRF序列标注

学AI还能赢奖品&#xff1f;每天30分钟&#xff0c;25天打通AI任督二脉 (qq.com) LSTMCRF序列标注 概述 序列标注指给定输入序列&#xff0c;给序列中每个Token进行标注标签的过程。序列标注问题通常用于从文本中进行信息抽取&#xff0c;包括分词(Word Segmentation)、词性标…

明日周刊-第15期

赶在周末结束前输出一把&#xff0c;周日的晚上大家要睡个好觉哦。 文章目录 一周热点资源分享言论歌曲推荐 一周热点 科技创新与基础设施建设 深中通道正式通车试运营 时间&#xff1a;6月30日 内容&#xff1a;国家重大工程深中通道正式通车试运营&#xff0c;标志着珠江口东…

吉时利KEITHLEY KI-488驱动和说明

吉时利KEITHLEY KI-488驱动和说明