**
**
利用:zookeeper或者etcd来实现.(zookeeper的源码为java,etcd的源码为go)
本文选用zookeeper来实现服务之间的调用负(有机会再发etcd的,不过都很简单,过程都是差不多的)。(部署自行google 不管是哪个系统都容易的很)
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操作起来,是易于上手的。
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)
}
}
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
}
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的是输出到客户端的
}