统一实现rpc,grpc,tcp,http等协议简单的负载均衡

**

统一实现rpc,grpc,tcp,http等协议简单的负载均衡

**
利用:zookeeper或者etcd来实现.(zookeeper的源码为java,etcd的源码为go)
本文选用zookeeper来实现服务之间的调用负(有机会再发etcd的,不过都很简单,过程都是差不多的)。(部署自行google 不管是哪个系统都容易的很)

实现思路:

程序之所以能调用肯定是分为了serve以及client,那么就得先从这两个方面下手.

serve:

serve注册时,需要将 serveName,以及对应的servePath:port creat到zookeeper的node,node方式为 /goods/域名:port(每一个服务都需要注册)
当serve挂掉时,zookeeper会自动检测serve port是否存在,如果不存在则消除 node 对应的 path。
默认几秒检测忘了,我测试了下三秒以内是肯定会检测出来的,不过这样就会有一个弊端,如果serve调用频繁,这个serve跨掉的三秒内调用了很多次,咋办?这个时候肯定就不能凉拌了,而是可以通过我们的当前serve进行一个实时跟新,zookeeper既然有creat,那必然也有delete,我们可以监控当前程序,写一个监控,监控当前程序是什么状态,当前程序如果要退出之前,我们会进行一个zookeeper node  path 的释放,这样就能实时的跟新zookeeper 存储的 path

client:

例如http类型的调用时,就很简单,直接通过zookeeper获取 node path就行,自己写一个随机或负载的机制,进行调用。
如是长链接tcp等,如上 获取到当前的 path ,用自己的机制进行轮换,不同点是,需要监控zookeeper,如果zookeeper的节点path有变动,则需要作出对应的变化。

这种方式能够实现的负载肯定是很多的,我目前用到的协议连接都是支持的。
简易度:中等吧,如果是http很多人直接走个nginx,或者阿里云的负载就行了,甚至还有更简单的方式。而比较流行的就是 docker+k8s负载了 其他的没去了解,个人觉得zookeeper操作起来,是易于上手的。

提供一段go zookeeper http负载源码:

分为三个模块

一 : common(公用)

package common

import (
	"fmt"
	"github.com/go-zookeeper/zk"
	"time"
)

func GetConnect() (conn *zk.Conn, err error) {
	hosts := []string{"localhost:2181"}
	conn, _, err = zk.Connect(hosts, 5*time.Second)
	if err != nil {
		fmt.Println(err)
		return nil, err
	}

	return
}

func RegistServer(conn *zk.Conn, host string) (err error) {
	//conn.Delete("") 可以自主删除
	_, err = conn.Create("/go_servers/"+host, nil, zk.FlagEphemeral, zk.WorldACL(zk.PermAll))
	return err
}

func GetServerList(conn *zk.Conn) (list []string, err error) {
	list, _, err = conn.Children("/go_servers")
	fmt.Println("list:", list)
	return
}

//watch机制,服务器有断开或者重连,收到消息
func WatchServerList(conn *zk.Conn, path string) (chan []string, chan error) {
	snapshots := make(chan []string)
	errors := make(chan error)

	go func() {
		for {
			snapshot, _, events, err := conn.ChildrenW(path)
			if err != nil {
				errors <- err
				return
			}
			snapshots <- snapshot
			evt := <-events
			if evt.Err != nil {
				errors <- evt.Err
				return
			}
		}
	}()

	return snapshots, errors
}

//watch机制,监听配置文件变化的过程
func WatchGetDat(conn *zk.Conn, path string) (chan []byte, chan error) {
	snapshots := make(chan []byte)
	errors := make(chan error)

	go func() {
		for {
			dataBuf, _, events, err := conn.GetW(path)
			if err != nil {
				errors <- err
				return
			}
			snapshots <- dataBuf
			evt := <-events
			if evt.Err != nil {
				errors <- evt.Err
				return
			}
		}
	}()

	return snapshots, errors
}

func CheckError(err error) {
	if err != nil {
		fmt.Println("err:", err)
		//panic(err)
	}
}

二 : client(客户端模块)

package main

import (
	"core.sincere/common/log"
	request "core.sincere/common/request"
	"core.sincere/test/zookeeper/common"
	"errors"
	"fmt"
	"math/rand"
	"time"
)

var serverList []string

func main() {
	conn, err := common.GetConnect()
	if err != nil {
		fmt.Printf(" connect zk error: %s \n ", err)
		return
	}
	defer conn.Close()
	serverList, err = common.GetServerList(conn)
	if err != nil {
		fmt.Printf(" get server list error: %s \n", err)
		return
	}

	count := len(serverList)
	if count == 0 {
		err = errors.New("server list is empty \n")
		return
	}

	//用来实时监听服务的上线与下线功能,serverList时刻保持最新的在线服务
	//获取最新地址
	snapshots, errors := common.WatchServerList(conn, "/go_servers")
	go func() {
		for {
			select {
			case serverList = <-snapshots:
				fmt.Printf("1111:%+v\n", serverList)
			case err := <-errors:
				fmt.Printf("2222:%+v\n", err)
			}
		}
	}()

	configs, errors := common.WatchGetDat(conn, "/config")
	go func() {
		for {
			select {
			case configData := <-configs:
				fmt.Printf("333:%+v\n", string(configData))
			case err := <-errors:
				fmt.Printf("4444:%+v\n", err)
			}
		}
	}()

	//for {
	//	time.Sleep(1 * time.Second)
	//}

	for i := 0; i < 100; i++ {
		fmt.Println("start Client :", i)


		startClient()

		time.Sleep(1 * time.Second)
	}
}

func startClient() {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("err:", err)
		}
	}()
	// service := "127.0.0.1:8899"
	//获取地址
	serverHost, err := getServerHost()
	if err != nil {
		fmt.Printf("get server host fail: %s \n", err)
		return
	}
	//serverHost := "127.0.0.1:8899" 此处是自己封住的包,就不做过多的解释了 自己去发http请求
	fmt.Println("connect host: " + serverHost)
	h := request.NewHttpSend(request.GetUrlBuild("http://"+serverHost+"/api",nil))
	_, err = h.Get()
	if err != nil {
		log.Error("请求错误:", err)
	} else {
		log.Error("正常返回")
	}

	//tcpAddr, err := net.ResolveTCPAddr("tcp4", serverHost)
	//checkError(err)
	//conn, err := net.Dial("tcp", serverHost)
	//common.CheckError(err)
	//defer conn.Close()
	//fmt.Println("connect ok")
	//_, err = conn.Write([]byte("timestamp"))
	//common.CheckError(err)
	//fmt.Println("write ok")
	// result, err := ioutil.ReadAll(conn)
	// checkError(err)
	// fmt.Println("recv:", string(result))

	return
}

func getServerHost() (host string, err error) {
	//随机选中一个返回
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	host = serverList[r.Intn(3)]
	return
}

三 :serve(服务端模块)

package main

/**
客户端doc地址:github.com/samuel/go-zookeeper/zk
**/
import (
	"core.sincere/test/zookeeper/common"
	"fmt"
	"net/http"
)

func main() {
	go starServer("127.0.0.1:9897")
	go starServer("127.0.0.1:9898")
	go starServer("127.0.0.1:9899")

	a := make(chan bool, 1)
	<-a
}

func starServer(port string) {

	//engin := gin.Default()
	//engin.GET("/api", func(context *gin.Context) {
	//	fmt.Println("因为懒 就不多写了")
	//})

	//注册zk节点q
	conn, err := common.GetConnect()
	if err != nil {
		fmt.Printf(" connect zk error: %s ", err)
		return
	}
	defer conn.Close()
	//当注册时 首先需要创建一级节点 go_servers
	//节点必须一节一节创建,不能直接就创建二级
	err = common.RegistServer(conn, port)
	if err != nil {
		fmt.Printf(" regist node error: %s ", err)
		return
	}

	mux1 := http.NewServeMux()
	mux1.HandleFunc("/api", sayhelloName)
	http.ListenAndServe(port, mux1)

	需要自己开服务,zookeeper会自动检测
	//http.HandleFunc("/hello", sayhelloName) //设置访问的路由
	//err = http.ListenAndServe(port, nil) //设置监听的端口
	//if err != nil {
	//	log.Fatal("ListenAndServe: ", err)
	//}
	//阻塞程序
	//select {}

}

func sayhelloName(w http.ResponseWriter, r *http.Request) {
	r.ParseForm() //解析参数,默认是不会解析的
	//fmt.Println(r.Form) //这些信息是输出到服务器端的打印信息
	//fmt.Println("path", r.URL.Path)
	//fmt.Println("scheme", r.URL.Scheme)
	//fmt.Println(r.Form["url_long"])
	//for k, v := range r.Form {
	//	fmt.Println("key:", k)
	//	fmt.Println("val:", strings.Join(v, ""))
	//}
	fmt.Println("因为懒就没写什么了:",r.URL, r.Host)
	fmt.Fprintf(w, "Hello Wrold!") //这个写入到w的是输出到客户端的
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值