go 语言 负载均衡 为反向代理添加负载均衡 拓展ReverseProxy

随机负载

package randomimport ("errors""math/rand"
)type RandomBalance struct {//当前索引curIndex int//存储负载均衡的地址rss []string//观察主体//conf LoadBalanceConf
}func (r *RandomBalance) Add(params ...string) error {if len(params) == 0 {return errors.New("param len 1 at least")}addr := params[0]r.rss = append(r.rss, addr)return nil
}// Next 随机到下一个index
func (r *RandomBalance) Next() string {if len(r.rss) == 0 {return ""}r.curIndex = rand.Intn(len(r.rss))return r.rss[r.curIndex]
}// Get 拿到下一个地址
func (r *RandomBalance) Get(key string) (string, error) {return r.Next(), nil
}

轮询负载

package roundimport "errors"type RoundRobinBalance struct {curIndex intrss      []string//观察主体//conf LoadBalanceConf
}func (r *RoundRobinBalance) Add(params ...string) error {if len(params) == 0 {return errors.New("param len 1 at least")}addr := params[0]r.rss = append(r.rss, addr)return nil
}func (r *RoundRobinBalance) Next() string {if len(r.rss) == 0 {return ""}lens := len(r.rss)if r.curIndex >= lens {r.curIndex = 0}curAddr := r.rss[r.curIndex]r.curIndex = (r.curIndex + 1) % lensreturn curAddr
}func (r *RoundRobinBalance) Get(key string) (string, error) {return r.Next(), nil
}

加权负载

package weightimport ("errors""strconv"
)type WeightRoundRobinBalance struct {curIndex intrss      []*WeightNodersw      []int//观察主体//conf LoadBalanceConf}type WeightNode struct {addr            stringweight          int //权重值currentWeight   int //节点当前权重effectiveWeight int //有效权重
}func (r *WeightRoundRobinBalance) Add(params ...string) error {if len(params) != 2 {return errors.New("param len need 2")}parInt, err := strconv.ParseInt(params[1], 10, 64)if err != nil {return err}node := &WeightNode{addr: params[0], weight: int(parInt)}node.effectiveWeight = node.weightr.rss = append(r.rss, node)return nil
}func (r *WeightRoundRobinBalance) Next() string {total := 0var best *WeightNodefor i := 0; i < len(r.rss); i++ {w := r.rss[i]//step 1 统计所以有效权重之和total += w.effectiveWeight//step 2 变更节点 临时权重w.currentWeight += w.effectiveWeight//step 3 有效权重默认与权重相同,通讯异常时-1, 通讯成功+1,直到恢复到weight大小if w.effectiveWeight < w.weight {w.effectiveWeight++}//step 4 选择最大临时权重点节点if best == nil || w.currentWeight > best.currentWeight {best = w}}if best == nil {return ""}//step 5 变更临时权重为 临时权重-有效权重之和best.currentWeight -= totalreturn best.addr
}
func (r *WeightRoundRobinBalance) Get(key string) (string, error) {return r.Next(), nil
}

在这里插入图片描述
节点有效权重:故障一次,权重减一 为了检测服务器故障准备的
在这里插入图片描述

加权轮询过程

在这里插入图片描述

一致性哈希负载

package consist_hashimport ("errors""hash/crc32""sort""strconv""sync"
)type Hash func(data []byte) uint32type UInt32Slice []uint32func (s UInt32Slice) Len() int {return len(s)
}func (s UInt32Slice) Less(i, j int) bool {return s[i] < s[j]
}func (s UInt32Slice) Swap(i, j int) {s[i], s[j] = s[j], s[i]
}type ConsistentHashBanlance struct {mux      sync.RWMutexhash     Hashreplicas int               //复制因子keys     UInt32Slice       //已排序的节点hash切片hashMap  map[uint32]string //节点哈希和Key的map,键是hash值,值是节点key//观察主体//conf LoadBalanceConf
}func NewConsistentHashBanlance(replicas int, fn Hash) *ConsistentHashBanlance {m := &ConsistentHashBanlance{replicas: replicas,hash:     fn,hashMap:  make(map[uint32]string),}if m.hash == nil {//最多32位,保证是一个2^32-1环m.hash = crc32.ChecksumIEEE}return m
}// 验证是否为空
func (c *ConsistentHashBanlance) IsEmpty() bool {return len(c.keys) == 0
}// Add 方法用来添加缓存节点,参数为节点key,比如使用IP
func (c *ConsistentHashBanlance) Add(params ...string) error {if len(params) == 0 {return errors.New("param len 1 at least")}addr := params[0]c.mux.Lock()defer c.mux.Unlock()// 结合复制因子计算所有虚拟节点的hash值,并存入m.keys中,同时在m.hashMap中保存哈希值和key的映射for i := 0; i < c.replicas; i++ {hash := c.hash([]byte(strconv.Itoa(i) + addr))c.keys = append(c.keys, hash)c.hashMap[hash] = addr}// 对所有虚拟节点的哈希值进行排序,方便之后进行二分查找sort.Sort(c.keys)return nil
}// Get 方法根据给定的对象获取最靠近它的那个节点
func (c *ConsistentHashBanlance) Get(key string) (string, error) {if c.IsEmpty() {return "", errors.New("node is empty")}hash := c.hash([]byte(key))// 通过二分查找获取最优节点,第一个"服务器hash"值大于"数据hash"值的就是最优"服务器节点"idx := sort.Search(len(c.keys), func(i int) bool { return c.keys[i] >= hash })// 如果查找结果 大于 服务器节点哈希数组的最大索引,表示此时该对象哈希值位于最后一个节点之后,那么放入第一个节点中if idx == len(c.keys) {idx = 0}c.mux.RLock()defer c.mux.RUnlock()return c.hashMap[c.keys[idx]], nil
}//func (c *ConsistentHashBanlance) SetConf(conf LoadBalanceConf) {
//	c.conf = conf
//}
//
//func (c *ConsistentHashBanlance) Update() {
//	if conf, ok := c.conf.(*LoadBalanceZkConf); ok {
//		fmt.Println("Update get conf:", conf.GetConf())
//		c.keys = nil
//		c.hashMap = nil
//		for _, ip := range conf.GetConf() {
//			c.Add(strings.Split(ip, ",")...)
//		}
//	}
//	if conf, ok := c.conf.(*LoadBalanceCheckConf); ok {
//		fmt.Println("Update get conf:", conf.GetConf())
//		c.keys = nil
//		c.hashMap = map[uint32]string{}
//		for _, ip := range conf.GetConf() {
//			c.Add(strings.Split(ip, ",")...)
//		}
//	}
//}

请求访问指定的IP

  • 单调性
  • 平衡性 可以引入虚拟结点 拷贝服务器结点
  • 分散性
    在这里插入图片描述
    在这里插入图片描述

为反向代理添加负载均衡

使用工厂方法

// LoadBalanceFactory 负载均衡的工厂模式
func LoadBalanceFactory(lbType LbType) LoadBalance {switch lbType {case LbRandom:return &random.RandomBalance{}case LbRoundRobin:return &round.RoundRobinBalance{}case LbWeightRoundRobin:return &weight.WeightRoundRobinBalance{}case LbConsistentHash:return consist_hash.NewConsistentHashBanlance(10, nil)default:return &random.RandomBalance{}}
}

使用接口统一封装

type LoadBalance interface {Add(...string) errorGet(string) (string, error)// Update 后期服务发现补充//Update()
}
package mainimport ("bytes""gateway-detail/load_balance/lb_factory""io""log""net""net/http""net/http/httputil""net/url""strconv""strings""time"
)var (addr      = "127.0.0.1:2224"transport = &http.Transport{DialContext: (&net.Dialer{Timeout:   30 * time.Second, //连接超时KeepAlive: 30 * time.Second, //长连接超时时间}).DialContext,MaxIdleConns:          100,              //最大空闲连接IdleConnTimeout:       90 * time.Second, //空闲超时时间TLSHandshakeTimeout:   10 * time.Second, //tls握手超时时间ExpectContinueTimeout: 1 * time.Second,  //100-continue状态码超时时间}
)func NewMultipleHostsReverseProxy(lb lb_factory.LoadBalance) *httputil.ReverseProxy {// 请求协调者director := func(req *http.Request) {nextAddr, err := lb.Get(req.RemoteAddr)if err != nil {log.Println(err.Error())log.Fatal("get next addr fail")}target, err := url.Parse(nextAddr)if err != nil {log.Fatal(err)}//改造reqtargetQuery := target.RawQueryreq.URL.Scheme = target.Schemereq.URL.Host = target.Hostreq.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)if targetQuery == "" || req.URL.RawQuery == "" {req.URL.RawQuery = targetQuery + req.URL.RawQuery} else {req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery}if _, ok := req.Header["User-Agent"]; !ok {req.Header.Set("User-Agent", "user-agent")}}//更改内容modifyFunc := func(resp *http.Response) error {//请求以下命令:curl 'http://127.0.0.1:2002/error'if resp.StatusCode != 200 {//获取内容}//追加内容oldPayload, err := io.ReadAll(resp.Body)if err != nil {return err}newPayload := []byte("Hello :" + string(oldPayload))resp.Body = io.NopCloser(bytes.NewBuffer(newPayload))resp.ContentLength = int64(len(newPayload))resp.Header.Set("Content-Length", strconv.FormatInt(int64(len(newPayload)), 10))return nil}//错误回调 :关闭real_server时测试,错误回调//范围:transport.RoundTrip发生的错误、以及ModifyResponse发生的错误errFunc := func(w http.ResponseWriter, r *http.Request, err error) {//todo 如果是权重的负载则调整临时权重http.Error(w, "ErrorHandler error:"+err.Error(), 500)}return &httputil.ReverseProxy{Director: director, Transport: transport, ModifyResponse: modifyFunc, ErrorHandler: errFunc}
}
func singleJoiningSlash(a, b string) string {aslash := strings.HasSuffix(a, "/")bslash := strings.HasPrefix(b, "/")switch {case aslash && bslash:return a + b[1:]case !aslash && !bslash:return a + "/" + b}return a + b
}func main() {rb := lb_factory.LoadBalanceFactory(lb_factory.LbConsistentHash)if err := rb.Add("http://127.0.0.1:2003/base", "10"); err != nil {log.Println(err)}if err := rb.Add("http://127.0.0.1:2004/base", "50"); err != nil {log.Println(err)}proxy := NewMultipleHostsReverseProxy(rb)log.Println("Starting httpserver at " + addr)log.Fatal(http.ListenAndServe(addr, proxy))
}

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

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

相关文章

什么是WhatsApp群发,WhatsApp协议,WhatsApp云控

那么WhatsApp群控云控可以做什么呢&#xff1f; 1、获客 自动化引流&#xff0c;强大的可控性&#xff0c;产品快速拓客 2、导流 一键式傻瓜化自动加好友&#xff0c;群发&#xff0c;朋友圈营销 3、群控 一键式拉群好友&#xff0c;建群&#xff0c;进群 …

Android 12修改usb tp触摸唤醒

前言 Android 12系统休眠时&#xff0c;需要不管接什么型号usb tp都能够触摸唤醒。 Android12系统中&#xff0c;usb tp要能够触摸唤醒&#xff0c;需要在frameworks/native/services/inputflinger/reader/mapper/TouchInputMapper.cpp中将mParameters.wake赋值为true&#xff…

Spring Boot2.7生成用于登录的图片验证码

先在 pom.xml 注入依赖 <dependency><groupId>com.github.penggle</groupId><artifactId>kaptcha</artifactId><version>2.3.2</version> </dependency>然后 需要在配置文件中声明一下DefaultKaptcha 的 bean对象 然后 我们…

Python之网络编程

一、网络编程 互联网时代,现在基本上所有的程序都是网络程序,很少有单机版的程序了。 网络编程就是如何在程序中实现两台计算机的通信。 Python语言中,提供了大量的内置模块和第三方模块用于支持各种网络访问,而且Python语言在网络通信方面的优点特别突出,远远领先其他语…

解决MySQL8.0本地计算机上的MySQL服务启动后停止没有报告任何错误

1.启动MySQL的错误信息如下 &#xff08;1&#xff09;“本地计算机上的MySQL服务启动后停止。某些服务在未由其他服务或程序使用时将自动停止。” &#xff08;2&#xff09;又在PowerShell中运行"net start MySQL"&#xff0c;服务启动失败。“MySQL 服务无法启…

数据分享|R语言生态学种群空间点格局分析:聚类泊松点过程对植物、蚂蚁巢穴分布数据可视化...

全文链接 :https://tecdat.cn/?p33676 点模式分析&#xff08;点格局分析&#xff09;是一组用于分析空间点数据的技术。在生态学中&#xff0c;这种类型的分析可能在客户的几个情境下出现&#xff0c;但对数据生成方式做出了特定的假设&#xff0c;因此让我们首先看看哪些生态…

【LeetCode】——双指针(快慢指针)/多指针

个人主页 代码仓库 C语言专栏 初阶数据结构专栏 Linux专栏 前言 大家好&#xff01;这是新开的LeetCode刷题专栏&#xff0c;这个专栏不只是随便的拿一些我练过的题讲解&#xff0c;而是总结我在刷题中的一些方法适用于一大类的题&#xff0c;是给大家提供这一大类题的解题…

爬虫使用Selenium生成Cookie

在爬虫的世界中&#xff0c;有时候我们需要模拟登录来获取特定网站的数据&#xff0c;而使用Selenium登录并生成Cookie是一种常见且有效的方法。本文将为你介绍如何使用Selenium进行登录&#xff0c;并生成Cookie以便后续的爬取操作。让我们一起探索吧&#xff01; 一、Seleni…

深度学习自学笔记一:神经网络和深度学习

神经网络是一种模拟人脑神经元之间相互连接的计算模型&#xff0c;它由多个节点&#xff08;或称为神经元&#xff09;组成&#xff0c;并通过调整节点之间的连接权重来学习和处理数据。深度学习则是指利用深层次的神经网络进行学习和建模的机器学习方法。 假设有一个数据集&a…

企业架构LNMP学习笔记53

PHP扩展安装&#xff1a; server01和server03上安装redis扩展&#xff1a; 解压编译安装&#xff1a; shell > tar xvf redis-4.3.0.tgz shell > cd redis-4.3.0 shell > phpize shell > ./configure && make && make install 配置文件php.ini&…

HTML5day02综合案例2

案例展示 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>注册信息</title> </head> &l…

记一次MySQL安装过程中遇到的问题

由于太久没用MySQL&#xff0c;今天在重装MySQL时遇到一个问题&#xff0c;被卡了近2个小时。。。。。。 由于我本人原先安装过MySQL&#xff0c;所以在重装的时候必须要先卸载原先的MySQL。 下面先给出正确的卸载流程&#xff08;作者就是在卸载的时候操作失误导致安装过程被…

使用python处理MNIST数据集

文章目录 一. MNIST数据集1.1 什么是MNIST数据集1.2MNIST数据集文件格式1.3使用python访问MNIST数据集文件内容 附录程序源码 一. MNIST数据集 1.1 什么是MNIST数据集 MNIST数据集是入门机器学习/识别模式的最经典数据集之一。最早于1998年Yan Lecun在论文:[Gradient-based l…

MyBatis基础之SqlSession

SqlSession 线程安全问题 当你翻看 SqlSession 的源码时&#xff0c;你会发现它只是一个接口。我们通过 MyBatis 操作数据库&#xff0c;实际上就是通过 SqlSession 获取一个 JDBC 链接&#xff0c;然后操作数据库。 SqlSession 接口有 3 个实现类&#xff1a; #实现类1Defa…

【Verilog语法】比较不同计数器的运算方式,其中有一个数是延迟打一拍的效果,目的是使得两个计数器的结果相同。

比较不同计数器的运算方式&#xff0c;其中有一个数是延迟打一拍的效果&#xff0c;目的是使得两个计数器的结果相同。 1&#xff0c;第一种2&#xff0c;第二种3&#xff0c;第三种 第三种方案&#xff0c;完成实现。 1&#xff0c;第一种 &#xff08;1&#xff09;RTL modu…

索引(含B树、B+树)

1、索引&#xff08;index&#xff09; 索引是在数据库表的字段上添加的&#xff0c;是为了提高查询效率存在的一种机制。 一张表的一个字段可以添加一个索引&#xff0c;当然&#xff0c;多个字段联合起来也可以添加索引。 索引相当于一本书的目录&#xff0c;是为了缩小扫描…

数据仓库整理

数仓 olap vs oltp OLTP主要用于支持日常的业务操作&#xff0c;如银行交易、电子商务等&#xff0c;强调数据的准确性、实时性和并发性。OLAP主要用于支持复杂的数据分析&#xff0c;如数据仓库、决策支持等&#xff0c;强调数据的维度、聚合和可视化。 将OLTP数据库的数据…

Intellij idea 2023 年下载、安装教程、亲测可用

文章目录 1 下载与安装IDEA2 常用设置设置 Java JDK 版本自动导入包、移除包IDEA 自动生成 author 注释签名java.io.File 类无法自动提示导入&#xff1f;高亮显示与选中字符串相同的内容IDEA 配置 MavenIDEA 连接 Mysql 数据库 3 参考文章 1 下载与安装IDEA 首先先到官网下载…

硬件知识积累 网口接口 百兆,千兆,万兆 接口介绍与定义 (RJ45 --简单介绍)

1. 百兆网口 1.1百兆网的定义 百兆网的意思是是100Mb/S&#xff0c;中文叫做100兆位/秒。 1.2百兆网口的常用连接器 1.1.1 一般百兆网口的连接器一般是RJ45 下面是 实物图&#xff0c; 原理图&#xff0c;封装图。 1.3 百兆网口连接线的介绍 1.3.1 百兆需要使用的线的定义 百…

基于Vue+ELement搭建登陆注册页面实现后端交互

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的专栏《ELement》。&#x1f3af;&#x1f3af; &#x1…