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

news2024/9/28 7:16:21

随机负载

package random

import (
	"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 round

import "errors"

type RoundRobinBalance struct {
	curIndex int
	rss      []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) % lens
	return curAddr
}

func (r *RoundRobinBalance) Get(key string) (string, error) {
	return r.Next(), nil
}

加权负载

package weight

import (
	"errors"
	"strconv"
)

type WeightRoundRobinBalance struct {
	curIndex int
	rss      []*WeightNode
	rsw      []int
	//观察主体
	//conf LoadBalanceConf

}

type WeightNode struct {
	addr            string
	weight          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.weight
	r.rss = append(r.rss, node)
	return nil
}

func (r *WeightRoundRobinBalance) Next() string {
	total := 0
	var best *WeightNode
	for 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 -= total
	return best.addr
}
func (r *WeightRoundRobinBalance) Get(key string) (string, error) {
	return r.Next(), nil
}

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

加权轮询过程

在这里插入图片描述

一致性哈希负载

package consist_hash

import (
	"errors"
	"hash/crc32"
	"sort"
	"strconv"
	"sync"
)

type Hash func(data []byte) uint32

type UInt32Slice []uint32

func (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.RWMutex
	hash     Hash
	replicas 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) error
	Get(string) (string, error)

	// Update 后期服务发现补充
	//Update()
}
package main

import (
	"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)
		}
		//改造req
		targetQuery := target.RawQuery
		req.URL.Scheme = target.Scheme
		req.URL.Host = target.Host
		req.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.coloradmin.cn/o/1026098.html

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

相关文章

华为OD机试 - 靠谱的车 - 逻辑分析(Java 2023 B卷 100分)

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路五、Java算法源码六、效果展示1、输入2、输出3、说明 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷&#…

【Phoenix】phoenix实现每个Primarykey主键保留N版本数据,CDC数据记录为Changelog格式

一、背景&#xff1a; CDC数据中包含了&#xff0c;数据的变更过程。当CDC写入传统数据库最终每一个primary key下会保存一条数据。当然可以使用特殊手段保存多分记录但是显然造成了数据膨胀。 另外数据湖Hudi(0.13.1)是不支持保存所有Changelog其Compaction机制会清除所有旧版…

语义分割笔记(二):DeepLab V3对图像进行分割(自定义数据集从零到一进行训练、验证和测试)

文章目录 一、语义分割介绍1.1 语义分割和实例分割的区别1.2 DeepLab系列对比 二、代码下载2.1 代码测试2.2 视频学习 三、数据集准备3.1 Json转png3.2 数据集划分 四、模型训练五、模型测试六、模型评估 一、语义分割介绍 语义分割是计算机视觉中的一项技术&#xff0c;旨在将…

在windows下持续ping ip,将返回结果及时间记录到文件中

在纯英文路径下创建文件ping.txt 在txt中写入 Dim args, flag, unsuccOut args"" otherout"" flag0If WScript.Arguments.count 0 Then WScript.Echo "Usage: cscript tping.vbs [-t] [-a] [-n count] [-l size] [-f] [-i TTL] [-v TOS]" WScr…

java中的自定义对象排序

对于数组排序我们知道有Arrays.sort()方法&#xff0c;但是如果遇到想要对一个对象数组中的某个属性进行排序&#xff0c;我们该如何去做呢&#xff1f; 以给学生成绩排序为例&#xff0c;首先创建一个存储学生对象的数组。 package JAVA_API;public class Sort_oop {public …

解密Java多线程中的锁机制:CAS与Synchronized的工作原理及优化策略

目录 CAS什么是CASCAS的应用ABA问题异常举例 Synchronized 原理基本特征加锁过程偏向锁轻量级锁重量级锁 其他优化操作锁消除锁粗化 CAS 什么是CAS CAS: 全称Compare and swap&#xff0c;字面意思:”比较并交换“&#xff0c;CAS涉及如下操作&#xff1a; 假设内存中的原数据…

7、DVWA——SQL盲注

文章目录 一、概述二、low2.1 通关思路&#xff08;布尔盲注&#xff09;&#xff08;1&#xff09;判断是否存在SQL注入漏洞&#xff08;2&#xff09;判断属于数字型注入还是字符型注入&#xff08;3&#xff09;判断结果集中的字段数&#xff08;4&#xff09;猜数据库名长度…

常见的排序算法及时间空间复杂度

排序算法是计算机科学中的基本算法之一&#xff0c;它用于将一组数据按照某种顺序进行排列。下面是一些常见的排序算法&#xff0c;以及它们的思想和时间空间复杂度&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢…

c++qt day11

通过代码实现电子钟表 头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include<QPaintEvent> #include<QDebug> #include<QPainter> #include<QFont> #include<QTime> #include<QTimer>QT_BEGIN_NAMESPACE namespa…

pytorch的卷积层池化层和非线性变化 和机器学习线性回归

卷积层&#xff1a; 两个输出的情况 就会有两个通道 可以改变通道数的 最简单的神经网络结构&#xff1a; nn.Mudule就是继承父类 super执行的是 先执行父类函数里面的 forward执行的就是前向网络&#xff0c;就是往前推进的&#xff0c;当然也有反向转播&#xff0c;那就是…

基于Java的电影院管理系统设计与实现

前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f447;&#x1f3fb;…

百度收录和权重怎么提升-网站如何获得百度权重

你是否一直苦恼于网站权重的低迷&#xff1f;不知道如何开始提升网站权重&#xff0c;缺乏优质内容更新网站。不清楚如何进行关键词优化来提升网站排名和权重。SEO是一个需要持续投入时间和资源的过程。每个网站的情况都会有所不同&#xff0c;因此所花费的时间也会有所差异。然…

蓝桥杯 题库 简单 每日十题 day6

01 删除字符 题目描述 给定一个单词&#xff0c;请问在单词中删除t个字母后&#xff0c;能得到的字典序最小的单词是什么&#xff1f; 输入描述 输入的第一行包含一个单词&#xff0c;由大写英文字母组成。 第二行包含一个正整数t。 其中&#xff0c;单词长度不超过100&#x…

网络编程day03(UDP中的connect函数、tftp)

今日任务&#xff1a;tftp的文件上传下载&#xff08;服务端已经准备好&#xff09; 服务端&#xff08;已上传&#xff09; 客户端&#xff1a; 代码&#xff1a; #include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/types.h…

全球南方《乡村振兴战略下传统村落文化旅游设计》许少辉八一新枝——2023学生开学季辉少许

全球南方《乡村振兴战略下传统村落文化旅游设计》许少辉八一新枝——2023学生开学季辉少许

QT实现简易时钟

头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QPaintEvent> #include <QDebug> #include <QPainter> #include <QTimerEvent> #include <QTimer> #include <QTime>QT_BEGIN_NAMESPACE namespace Ui { cl…

51单片机项目(13)——基于51单片机的智能台灯protues仿真

本次设计&#xff0c;使用protues软件进行仿真&#xff0c;详情如下&#xff1a; 1.输入部分:由热释电红外传感器、光敏传感器、超声波测距传感器所构成的子电路组成。 2.输出模块:由1602液晶显示及其蜂鸣器报警系统组成。 3.中央处理器:主要有AT89C52单片机构成。 4.工作过…

C++ PrimerPlus 复习 第八章 函数探幽

第一章 命令编译链接文件 make文件 第二章 进入c 第三章 处理数据 第四章 复合类型 &#xff08;上&#xff09; 第四章 复合类型 &#xff08;下&#xff09; 第五章 循环和关系表达式 第六章 分支语句和逻辑运算符 第七章 函数——C的编程模块&#xff08;上&#xff…

一、Stable Diffusion WebUI 安装

Mac 配置 类别配置机型Macbook pro m2核总数12 核中央处理器、38 核图形处理器和 16 核神经网络引擎内存64 G系统Sonoma 安装 Homebrew 打开终端执行&#xff08;使用了国内镜像源安装&#xff09; /bin/bash -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/r…

深入理解HttpSecurity的设计

文章目录 HttpSecurity的应用HttpSecurity的类图结构SecurityBuilder接口AbstractConfiguredSecurityBuilderadd方法doBuild方法 HttpSecurity HttpSecurity的应用 在上文介绍了基于配置文件的使用方式以及实现细节&#xff0c;如下&#xff1a; 也就是在配置文件中通过 secur…