需要添加支持用户自定义的算法,这样怎么实现呢?可以通过添加一个调度算法管理的模块,对系统提供的,自定义的调度算法进行管理。并且动态地添加支持。
实现一个支持负载均衡管理的类:(如果VSC保存时候,没有自动调整格式,那就是代码有问题了,struct初始化时候每一行需要 逗号 "," 。 不然回报错:“syntax error: unexpected newline, expecting comma or } ”。运行main.go之前,先进行go build,这样子才会去执行init函数注册到BalanceMgr里面。不然不会调用init函数。
package balance
import "fmt"
//定义一个map,动态管理所有的调度算法。定义一个map,判断是什么算法。
//value 放接口,这样可以存很多
type BalanceMgr struct {
allBalancer map[string]Balancer
}
//对map进行初始化
var mgr = BalanceMgr{
allBalancer: make(map[string]Balancer),
}
// func RegisterBalancer(name string, b Balancer) {}
// 给负载均衡实例添加注册方法,注册到map里面。以key:value的方式存。
//这个函数是私有的,小写
func (p *BalanceMgr) registerBalancer(name string, b Balancer) {
p.allBalancer[name] = b
}
//通过mgr调用 registerBalancer(name string, b Balancer)
func RegisterBalancer(name string, b Balancer) {
mgr.registerBalancer(name, b)
}
func DoBalance(name string, insts []*Instance) (inst *Instance, err error) {
balancer, ok := mgr.allBalancer[name]
if !ok {
//利用fmt包中的错误格式化方法输出
err = fmt.Errorf("Not found %s balancer", name)
return
}
fmt.Printf("use %s balance\n",name)
inst, err = balancer.DoBalance(insts)
return
}
实现了mgr 这个调度算法的管理类,同时,系统默认的方法,通过init() 函数实现注册。
random.go 添加以下init函数
//利用init函数注册方法到mgr
func init() {
RegisterBalancer("random",&RandomBalance{})
}
roundrobin.go 添加以下init函数
//利用init函数注册方法到mgr
func init() {
RegisterBalancer("randomrobin", &RandomRobinBalance{})
}
调整main.go,通过mgr实现。
package main
import (
"fmt"
"go_dev/day6/homework/work02/balance"
"math/rand"
"os"
"time"
)
func main() {
//[<nil> ... <nil> 0xc0000044c0...0xc0000045e0] 会出现10个nil
//insts := make([]*balance.Instance, 10)
var insts []*balance.Instance
for i := 0; i < 10; i++ {
host := fmt.Sprintf("192.168.%d.%d", rand.Intn(255), rand.Intn(255))
one := balance.NewInstance(host, 8080)
insts = append(insts, one)
}
//默认是random
var balancerName = "random"
//如果有输入参数,就选择输入的参数,os.Args代表用户输入参数
if len(os.Args) > 1 {
balancerName = os.Args[1]
}
for {
//调用了mgr里面的Dobalance,这样子就实现了,指定了用什么方法,Dobalance帮忙处理
inst, err := balance.DoBalance(balancerName, insts)
if err != nil {
fmt.Println("do balance rr err:", err)
time.Sleep(1 * time.Second)
continue
}
fmt.Println(inst)
time.Sleep(1 * time.Second)
}
}
运行结果:
#不指定参数用默认的
PS F:\go\src\go_dev\day6\homework\work02> go run .\main\main.go
RegisterBalancer random
RegisterBalancer randomrobin
use random balance
192.168.119.151:8080
use random balance
192.168.42.59:8080
#指定了参数
PS F:\go\src\go_dev\day6\homework\work02> go run .\main\main.go randomrobin
RegisterBalancer random
RegisterBalancer randomrobin
use randomrobin balance
192.168.86.132:8080
PS F:\go\src\go_dev\day6\homework\work02>
#报错的地方注意加sleep,不然会刷频,很快就会把日志打满
PS F:\go\src\go_dev\day6\homework\work02> go run .\main\main.go roundomrobin
RegisterBalancer random
RegisterBalancer randomrobin
do balance rr err: Not found roundomrobin balancer
do balance rr err: Not found roundomrobin balancer
下面,自己实现一个调度算法,利用提供的registerBalancer 进行注册。
调整DoBalance,让接口成为可以接收可变长参数的接口,接口里面的函数有点像是func type,不需要参数,直接写类型就好。
type Balancer interface {
DoBalance([]*Instance, ...string) (*Instance, error)
}
实现一个自定义的balance:
package main
import (
"fmt"
"go_dev/day6/homework/work02/balance"
"hash/crc32"
"math/rand"
)
func init() {
//注册random方法
fmt.Println("RegisterBalancer hash")
balance.RegisterBalancer("hash", &HashBalance{})
}
type HashBalance struct {
key string
}
//一致性哈希,不同的用户对应不同的后端
func (p *HashBalance) DoBalance(insts []*balance.Instance, key ...string) (inst *balance.Instance, err error) {
var defKey string = fmt.Sprintf("%d", rand.Int())
if len(key) > 0 {
defKey = key[0]
return
}
lens := len(insts)
if lens == 0 {
err = fmt.Errorf("No backend instance")
return
}
//crcTable := crc64.MakeTable(crc64.ECMA)
crcTable := crc32.MakeTable(crc32.IEEE)
hashVal := crc32.Checksum([]byte(defKey), crcTable)
index := int(hashVal) % lens //int(hashVal) int64转int 会出现负数
inst = insts[index]
return
}
运行结果:
PS F:\go\src\go_dev\day6\homework> .\main.exe
RegisterBalancer random
RegisterBalancer randomrobin
RegisterBalancer hash
use random balance
192.168.119.151:8080
use random balance
192.168.42.59:8080
PS F:\go\src\go_dev\day6\homework> .\main.exe randomrobin
RegisterBalancer random
RegisterBalancer randomrobin
RegisterBalancer hash
use randomrobin balance
192.168.86.132:8080
use randomrobin balance
192.168.122.254:8080
PS F:\go\src\go_dev\day6\homework> .\main.exe hash
RegisterBalancer random
RegisterBalancer randomrobin
RegisterBalancer hash
use hash balance
192.168.167.131:8080
use hash balance
192.168.122.254:8080
use hash balance
192.168.88.239:8080
PS F:\go\src\go_dev\day6\homework>
本次作业文件结构如下:
F:.
├─balance
│ balance.go
│ instance.go
│ mgr.go
│ random.go
│ roundrobin.go
│
└─main
hash.go
main.go
main.go:
package main
import (
"fmt"
"go_dev/day6/homework/work02/balance"
"math/rand"
"os"
"time"
)
func main() {
//[<nil> ... <nil> 0xc0000044c0...0xc0000045e0] 会出现10个nil
//insts := make([]*balance.Instance, 10)
var insts []*balance.Instance
for i := 0; i < 10; i++ {
host := fmt.Sprintf("192.168.%d.%d", rand.Intn(255), rand.Intn(255))
one := balance.NewInstance(host, 8080)
insts = append(insts, one)
}
//默认是random
var balancerName = "random"
//如果有输入参数,就选择输入的参数,os.Args代表用户输入参数
if len(os.Args) > 1 {
balancerName = os.Args[1]
}
for {
//调用了mgr里面的Dobalance,这样子就实现了,指定了用什么方法,Dobalance帮忙处理
inst, err := balance.DoBalance(balancerName, insts)
if err != nil {
fmt.Println("do balance err:", err)
time.Sleep(1 * time.Second)
continue
}
fmt.Println(inst)
time.Sleep(1 * time.Second)
}
}
balance.go:
package balance
//定义一个接口,由具体的调度算法去实现 DoBalance([]*Instance) (*Instance, error)
type Balancer interface {
DoBalance([]*Instance, ...string) (*Instance, error)
}
instance.go:
package balance
import "strconv"
type Instance struct {
host string
port int
}
//为了封装,写一个构造函数,这样instance就不需要对外暴露,也没需求
func NewInstance(host string, port int) *Instance {
return &Instance{
host: host,
port: port,
}
}
//提供访问接口
func (p *Instance) GetHost() string {
return p.host
}
//提供访问接口
func (p *Instance) GetPort() int {
return p.port
}
//实现String接口
func (p *Instance) String() string {
return p.host + ":" + strconv.Itoa(p.port)
}
mgr.go:
package balance
import "fmt"
//定义一个map,动态管理所有的调度算法。定义一个map,判断是什么算法。
//value 放接口,这样可以存很多
type BalanceMgr struct {
allBalancer map[string]Balancer
}
//对map进行初始化
var mgr = BalanceMgr{
allBalancer: make(map[string]Balancer),
}
// func RegisterBalancer(name string, b Balancer) {}
// 给负载均衡实例添加注册方法,注册到map里面。以key:value的方式存。
//这个函数是私有的,小写
func (p *BalanceMgr) registerBalancer(name string, b Balancer) {
p.allBalancer[name] = b
}
//通过mgr调用 registerBalancer(name string, b Balancer)
func RegisterBalancer(name string, b Balancer) {
mgr.registerBalancer(name, b)
}
func DoBalance(name string, insts []*Instance) (inst *Instance, err error) {
balancer, ok := mgr.allBalancer[name]
if !ok {
//利用fmt包中的错误格式化方法输出
err = fmt.Errorf("Not found %s balancer", name)
return
}
fmt.Printf("use %s balance\n", name)
inst, err = balancer.DoBalance(insts)
return
}
random.go:
package balance
import (
"errors"
"fmt"
"math/rand"
)
//利用init函数注册方法到mgr,调用这个函数时候,才会执行这一步
func init() {
//注册random方法
fmt.Println("RegisterBalancer random")
RegisterBalancer("random", &RandomBalance{})
}
type RandomBalance struct {
}
func (p *RandomBalance) DoBalance(insts []*Instance, key ...string) (inst *Instance, err error) {
if len(insts) == 0 {
err = errors.New("No instancd")
return
}
lens := len(insts)
index := rand.Intn(lens)
inst = insts[index]
return
}
roundrobin.go:
package balance
import (
"errors"
"fmt"
)
//利用init函数注册方法到mgr
func init() {
//注册randomrobin方法
fmt.Println("RegisterBalancer randomrobin")
RegisterBalancer("randomrobin", &RandomRobinBalance{})
}
type RandomRobinBalance struct {
curIndex int
}
func (p *RandomRobinBalance) DoBalance(insts []*Instance, key ...string) (inst *Instance, err error) {
if len(insts) == 0 {
err = errors.New("No instancd")
return
}
lens := len(insts)
if p.curIndex >= lens {
p.curIndex = 0
}
inst = insts[p.curIndex]
p.curIndex = (p.curIndex + 1) % lens //取余数方法很棒
return
}
接下来,还需要梳理下这个项目的调用链,理清之间的关系。