go ReverseProxy 实现http代理负载均衡

一、构建两个应用服

9091 和 9092端口

package main

import (
	"encoding/base64"
	"log"
	"net/http"
	"os"
	"os/signal"
	"strings"
)

type web1handler struct {}
func(web1handler) ServeHTTP (writer http.ResponseWriter,request *http.Request) {
	//请求头是否有给到 Authorization 信息
	auth := request.Header.Get("Authorization")
	if auth == "" {
		writer.Header().Set("WWW-Authenticate",`Basic realm="您必须输入用户名和密码"`)
		writer.WriteHeader(http.StatusUnauthorized)
		return
	}
	// auth中取出用户和密码信息 "Basic xxxxxx="
	auth_list := strings.Split(auth," ") //将字符串分割
	if len(auth_list) == 2 && auth_list[0] == "Basic" {
		res,err := base64.StdEncoding.DecodeString(auth_list[1]) //base64解码
		if err == nil && string(res) == "leo:123" {
			writer.Write([]byte("<h1>web1</h1>"))
			return
		}
	}
	writer.Write([]byte("用户名和密码错误"))
}



type web2handler struct {}
func(web2handler) ServeHTTP(writer http.ResponseWriter,request *http.Request) {
	writer.Write([]byte("web2"))
}

func main() {
	c := make(chan os.Signal)
	go func() {
		http.ListenAndServe(":9091",web1handler{})
	}()
	go func() {
		http.ListenAndServe(":9092",web2handler{})
	}()
	signal.Notify(c,os.Interrupt)
	s := <-c
	log.Println(s)
}

二、代理服网关代码

1.定义下游服务列表的获取,负载均衡算法 /proxy/util/util.go

注意:LB.AddServer(NewHttpServer("http://localhost:9091",5))  ,下游服的ip地址+端口根据实际的下游服务定义。实际项目中获取下游应用服信息是需要去 服务注册与发现中获取到能调用的应用服列表元数据,将元数据再从列表中根据算法来做到负载。或者就用服务注册与发现的负载算法获取到一个可调用的应用服元数据直接进行代理访问。

package util

import (
	"fmt"
	"hash/crc32"
	"math/rand"
	"time"
)

var LB *LoadBalance
var ServerIndices []int
func init() {
	LB = NewLoadBalance()
	LB.AddServer(NewHttpServer("http://localhost:9091",5))
	LB.AddServer(NewHttpServer("http://localhost:9092",15))
	for index,server := range LB.Servers {
		if server.Weight > 0 {
			for i:=0;i<server.Weight;i++{
				ServerIndices = append(ServerIndices,index)
			}
		}
	}
	fmt.Println(ServerIndices)
}

//HttpServer 目标server类
type HttpServer struct {
	Host string
	Weight int
}
// NewHttpServer 生成一个目标server类
func NewHttpServer(host string,weight int) *HttpServer {
	return &HttpServer{Host:host,Weight: weight}
}

//LoadBalance 负载均衡类
type LoadBalance struct {
	Servers []*HttpServer //应用服列表
	CurIndex int //当前访问的目标应用服
}

func NewLoadBalance() *LoadBalance {
	return &LoadBalance{Servers: make([]*HttpServer,0)}
}
// AddServer 添加一个目标server类
func (this *LoadBalance) AddServer(server *HttpServer) {
	this.Servers = append(this.Servers,server)
}
// SelectByRand 随机(负载分发规则)
func(this *LoadBalance)SelectByRand() *HttpServer {
	rand.Seed(time.Now().UnixNano()) //随机种子
	index := rand.Intn(len(this.Servers)) //servers长度
	return this.Servers[index]
}

// SelectByWeightRand 随机加权(负载分发规则)
func(this *LoadBalance)SelectByWeightRand() *HttpServer {
	rand.Seed(time.Now().UnixNano()) //随机种子
	index := rand.Intn(len(ServerIndices)) //ServerIndices切片里随机取到一个key
	return this.Servers[ServerIndices[index]]
}
// SelectByWeightRand2 升级版-随机加权(负载分发规则)
func(this *LoadBalance)SelectByWeightRand2() *HttpServer {
	rand.Seed(time.Now().UnixNano()) //随机种子
	sumList := make([]int,len(this.Servers))
	sum := 0
	for i := 0; i < len(this.Servers); i++ {
		sum += this.Servers[i].Weight
		sumList[i]=sum
	}
	rad := rand.Intn(sum)
	for index,value := range sumList {
		if rad < value {
			return this.Servers[index]
		}
	}
	return this.Servers[0] //不会执行到这一步
}

// SelectByIpHASH hash(负载分发规则)
func(this *LoadBalance)SelectByIpHASH(ip string) *HttpServer {
	//将ip用hash算法转化为int32,转int取模 下游服务器台数,则根据客户端ip固定访问某台应用服
	index := int(crc32.ChecksumIEEE([]byte(ip))) % len(this.Servers)
	return this.Servers[index]
}
// RoundRobin 简单轮询算法 (负载分发规则)
func(this *LoadBalance) RoundRobin() *HttpServer{
	server := this.Servers[this.CurIndex]
	this.CurIndex = (this.CurIndex+1)%len(this.Servers)
	CurIndex 指到下一个应用服
	//this.CurIndex++
	如果超出边界,则调整到开始位置
	//if this.CurIndex >= len(this.Servers) {
	//	this.CurIndex = 0
	//}
	return server
}
// RoundRobinByWeight 加权轮询算法 (负载分发规则)
func(this *LoadBalance) RoundRobinByWeight() *HttpServer{
	server := this.Servers[ServerIndices[this.CurIndex]]
	this.CurIndex = (this.CurIndex+1)%len(this.Servers)
	return server
}

2.main包中,获取下游应用服地址,根据算法选一个调用。/proxy/myproxy/main.go

package main

import (
	"go_code/proxy/util"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
)

type ProxyHandler struct {}

func (* ProxyHandler) ServeHTTP(w http.ResponseWriter,r *http.Request) {
	defer func() {
		if err := recover(); err != nil {
			w.WriteHeader(500)
			log.Println(err)
		}
	}()

	lb := util.LB
	//serveUrl,_ := url.Parse(lb.SelectByRand().Host) //随机算
	serveUrl,_ := url.Parse(lb.SelectByWeightRand().Host) //加权随机算
	//serveUrl,_ := url.Parse(lb.SelectByIpHASH(r.RemoteAddr).Host) //hash算法
	proxy := httputil.NewSingleHostReverseProxy(serveUrl) //获得代理对象
	proxy.ServeHTTP(w,r) //处理请求和返回
}


func main() {
	http.ListenAndServe(":8080",&ProxyHandler{})
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 Go 中,可以使用 select 语句实现负载均衡。具体地,可以将请求分发给多个处理器进行处理,以实现负载均衡的效果。以下是一个简单的示例代码: ```go func main() { numWorkers := 3 jobs := make(chan int, 5) results := make(chan int, 5) for i := 0; i < numWorkers; i++ { go worker(i, jobs, results) } for i := 0; i < 5; i++ { jobs <- i } close(jobs) for i := 0; i < 5; i++ { fmt.Println(<-results) } } func worker(id int, jobs <-chan int, results chan<- int) { for j := range jobs { fmt.Println("worker", id, "processing job", j) time.Sleep(time.Second) results <- j * 2 } } ``` 在上述代码中,我们首先创建了三个处理器(即 worker),并将它们分别放入 goroutine 中运行。然后,我们创建了两个通道,jobs 用于传递任务,results 用于传递任务处理结果。接下来,我们往 jobs 通道中发送了 5 个任务,然后关闭了 jobs 通道,以便告诉 worker 程序已经发送完毕。最后,我们从 results 通道中读取处理结果并打印出来。 在 worker 函数中,我们使用 range 关键字迭代 jobs 通道中的任务。每当有新的任务到来时,worker 就会输出该任务的编号,并将处理结果发送到 results 通道中。由于使用了 time.Sleep(time.Second) 语句,因此每个任务的处理时间为 1 秒钟。 在主函数中,我们使用 select 语句来实现负载均衡。具体地,select 会等待 jobs 和 results 通道中的数据,并分别将它们分发给空闲的 worker 进行处理。当某个 worker 完成一个任务时,会将处理结果发送到 results 通道中,然后再次进入 select 语句等待新的任务。 总之,通过使用 select 语句实现负载均衡,可以使得多个处理器能够协同处理任务,提高系统的并发处理能力和性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值