Golang负载均衡和保活设计原理示例探究

 更新时间:2024年01月24日 11:11:18   作者:绍纳 nullbody笔记  
这篇文章主要为大家介绍了Golang负载均衡和保活设计原理示例探究,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

引言

通过本项目可以学到什么?

负载均衡算法代码实现

调度器如何维护节点Live方案(参考)

loadbalance 负载均衡算法

随机算法,轮询算法,加权轮询算法

随机算法

// 随机
type Rand struct {
	addrs []string
}

func (r *Rand) Add(param []string) error {

	iflen(param) != 1 {
		return ErrParam
	}
	r.addrs = append(r.addrs, param[0])
	returnnil
}

// 随机种子,每次随机返回一个
func (r *Rand) Get() (string, error) {
	iflen(r.addrs) == 0 {
		return"", ErrNoAddr
	}
	rand.Seed(time.Now().UnixNano())
	idx := rand.Intn(len(r.addrs))
	return r.addrs[idx], nil
}

轮询算法

// 轮询
type RoundRobin struct {
	addrs []string

	curIdx int
}

func (r *RoundRobin) Add(param []string) error {
	iflen(param) != 1 {
		return ErrParam
	}
	r.addrs = append(r.addrs, param[0])
	returnnil
}

func (r *RoundRobin) Get() (string, error) {
	iflen(r.addrs) == 0 || r.curIdx >= len(r.addrs) {
		return"", ErrNoAddr
	}
	addr := r.addrs[r.curIdx]
	r.curIdx = (r.curIdx + 1) % len(r.addrs) // 对curIdx每次+1
	return addr, nil
}

加权轮询算法

实现原理

对节点设置权重,权重越大被选中次数越高,节点被选中的次数≈(本节点权重/全部权重) * 总次数。

举例说明:

节点权重为:weight [a=1,b=2,c=5]

节点当前权重为curWeight:初始值为[a=0,b=0,c=0],变化规则为:curWeight + weight

全部权重sumWeight:  代表所有节点初始权重之和 1+2+5=8

第一次请求:

curWeight 为 [a=0+1,b=0+2,c=0+5] ,选中最大的c做为本次输出,之后c节点的权重需要减去sumWeight,调整后 [a=1,b=2,c=5-8]  也就是 [a=1,b=2,c=-3]

第二次请求:

curWeight 为  [a=1+1,b=2+2,c=-3+5]  结果为 [a=2,b=4,c=2],选中最大的b作为本次输出,之后节点权重变更为 [a=2,b=-4,c=2]

第三次请求:

curWeight 为  [a=2+1,b=-4+2,c=2+5]  结果为 [a=3,b=-2,c=7],又轮到c(权重大的好处体现出来了),之后节点权重变更为 [a=3,b=-2,c=-1]

第四次请求:

[a=3,b=-2,c=-1] 加权后[a=4,b=0,c=4],a与c相等,优先选前者输出a

type WeigthRoundRobin struct {
	weightAddrs []*weightAddr
}
type weightAddr struct {
	addr      string// 地址
	weight    int// 权重
	curWeight int// 计算使用
}
func (w *WeigthRoundRobin) Add(param []string) error {
	iflen(param) != 2 {
		return ErrParam
	}
	weight, err := strconv.Atoi(param[1])
	if err != nil {
		return err
	}
	w.weightAddrs = append(w.weightAddrs, &weightAddr{
		addr:      param[0],
		weight:    weight,
		curWeight: 0,
	})
	returnnil
}
func (w *WeigthRoundRobin) Get() (string, error) {
	iflen(w.weightAddrs) == 0 {
		return"", ErrNoAddr
	}
	maxWeight := math.MinInt
	idx := 0
	sumWeight := 0// 权重总和
	for k, weightAddr := range w.weightAddrs {
		sumWeight += weightAddr.weight // 权重总和
		weightAddr.curWeight += weightAddr.weight // 加上权重
		if weightAddr.curWeight > maxWeight {     // 记录最大值
			maxWeight = weightAddr.curWeight
			idx = k
		}
	}
	w.weightAddrs[idx].curWeight -= sumWeight // 减去权重总和
	return w.weightAddrs[idx].addr, nil// 返回最大权重的结果
}

有效服务维护方案(参考)

方案一:heart【心跳】

前置配置:

1.启动docker

docker-compose -f docker-compose-env.yml   up -d zookeeper
docker-compose -f docker-compose-env.yml   up -d kafka

2.修改本地hostsvim /etc/hosts增加 127.0.0.1 kafka

3.kafka常用脚本【可选】 [进入docker中*.sh位于 /opt/kafka/bin目录]

# 创建topic
./kafka-topics.sh --create --zookeeper zookeeper:2181 --replication-factor 1 --partitions 3 --topic easy_topic
# 列出所有topic
./kafka-topics.sh --list --zookeeper zookeeper:2181
# 从头开始消费
./kafka-console-consumer.sh --bootstrap-server kafka:9092 --from-beginning --topic easy_topic
# 消费组【从最新的消息消费】
./kafka-console-consumer.sh --bootstrap-server kafka:9092 --consumer-property group.id=testGroup --topic easy_topic
# 从最新开始消费
./kafka-console-consumer.sh --bootstrap-server kafka:9092 --topic easy_topic
# 生产消息
./kafka-console-producer.sh --broker-list kafka:9092 --topic easy_topic

调度器订阅kafka消息,同时维护一个有效服务,然后按照负载均衡策略分发请求。

func main() {

	// 服务负责发送心跳
	go heart.RunHeartBeat()
	// 调度器负责接收心跳
	go heart.ListenHeartbeat()

	// 利用负载均衡获取

	lb := loadbalance.LoadBalanceFactory(loadbalance.BalanceTypeRand)
	gofunc() {
		for {
			time.Sleep(5 * time.Second)
			for _, v := range heart.GetAddrList() {
				lb.Add([]string{v})
			}
			fmt.Println(lb.Get())
		}

	}()

	sigusr1 := make(chan os.Signal, 1)
	signal.Notify(sigusr1, syscall.SIGTERM)
	<-sigusr1
}

方案二:health【健康检查】

通过HTTP请求服务,维护活跃节点

func main() {
	health.AddAddr("https://www.sina.com.cn/", "https://www.baidu.com/", "http://www.aajklsdfjklsd")
	go health.HealthCheck()

	time.Sleep(50 * time.Second)

	alist := health.GetAliveAddrList()
	for i := 0; i < len(alist); i++ {
		fmt.Println(alist[i])
	}

	var block = make(chanbool)
	<-block
}

以上就是Golang负载均衡和保活设计原理示例探究的详细内容,更多关于Golang负载均衡保活设计的资料请关注脚本之家其它相关文章!

相关文章

  • go语言yaml转map、map遍历的实现

    go语言yaml转map、map遍历的实现

    本文主要介绍了go语言yaml转map、map遍历的实现,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-09-09
  • Golang 实现 Redis系列(六)如何实现 pipeline 模式的 redis 客户端

    Golang 实现 Redis系列(六)如何实现 pipeline 模式的 redis 客户端

    pipeline 模式的 redis 客户端需要有两个后台协程负责 tcp 通信,调用方通过 channel 向后台协程发送指令,并阻塞等待直到收到响应,本文是使用 golang 实现 redis 系列的第六篇, 将介绍如何实现一个 Pipeline 模式的 Redis 客户端。
    2021-07-07
  • 一文初探Go语言中的reflect反射包

    一文初探Go语言中的reflect反射包

    这篇文章主要和大家分享一下Go语言中的reflect反射包,文中的示例代码讲解详细,对我们学习Go语言有一定的帮助,需要的小伙伴可以参考一下
    2022-12-12
  • Gin 路由与路由文件分组的实现实例

    Gin 路由与路由文件分组的实现实例

    本文主要介绍了Gin 路由与路由文件分组的实现实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-11-11
  • Go1.16新特性embed打包静态资源文件实现

    Go1.16新特性embed打包静态资源文件实现

    这篇文章主要为大家介绍了Go 1.16新特性embed打包静态资源文件的实现示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07
  • Go语言中定时器cron的基本使用教程

    Go语言中定时器cron的基本使用教程

    这篇文章主要给大家介绍了关于Go语言中定时器cron使用的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2018-01-01
  • 通过Golang实现linux命令ls命令(命令行工具构建)

    通过Golang实现linux命令ls命令(命令行工具构建)

    这篇文章主要为大家详细介绍了如何通过Golang实现一个linux命令ls命令(命令行工具构建),文中的示例代码讲解详细,具有一定的学习价值,感兴趣的可以了解一下
    2023-01-01
  • Golang Gorm 更新字段save、update、updates

    Golang Gorm 更新字段save、update、updates

    在gorm中,批量更新操作可以通过使用Update方法来实现,本文主要介绍了Golang Gorm 更新字段save、update、updates,具有一定的参考价值,感兴趣的可以了解一下
    2023-12-12
  • Go语言sync.Map详解及使用场景

    Go语言sync.Map详解及使用场景

    Go语言中的sync.Map是一个高效的并发安全映射结构,适用于高并发读多写少的场景,它通过读写分离、无锁读取路径、写入时的锁保护等机制,提高了读取性能并减少了锁竞争,sync.Map不需要手动管理锁,降低了编程复杂性,适合需要简单并发访问的场合
    2024-10-10
  • Go指针的具体使用

    Go指针的具体使用

    本文主要介绍了Go指针的具体使用,包括使用指针、空指针、指针数组、指向指针的指针等,具有一定的参考价值,感兴趣的可以了解一下
    2023-11-11

最新评论