Реализация приложения Zookeeper — Центр конфигурации

ZooKeeper распределенный

вперед отисходный адрес

1. Цель

Нищенский вариант центра конфигурации самообновления, после обновления конфигурации его можно обновить на каждом сервере

2. Архитектура

3. Роль

  • config-web: фон конфигурации, в основном используется для управления конфигурацией, добавления и изменения конфигурации
  • config-agent: следите за конфигурацией и автоматически загружайте последний файл на локальный сервер при обнаружении изменений.
  • config-sdk: SDK для бизнес-интеграции, используемый для чтения конфигурации.

3.1 фон конфигурации config-web

  1. Постоянным хранилищем является MySQL, также можно добавить слой кеша Redis, установить уникальный бизнес-ключ, соответствующий ZNode в ZK
    • Для операции настройки узла его необходимо в конечном итоге разместить на диске и постоянно хранить в MySQL.
  2. После успешного создания постоянного хранилища запишите настроенное содержимое в кластер ZK.

Ниже приведен код узла создания, такой же, как и набор, это простая операция.

package main

import (
	"fmt"
	. "go-zk/connect"

	"github.com/samuel/go-zookeeper/zk"
)

func main()  {
	conn := Connect()
	defer conn.Close()

	flags := int32(zk.FlagSequence)
	acl := zk.WorldACL(zk.PermAll)

	// create node
	path := PathConfig.ZNodePath +"/"+ "huodong-"
	data := []byte(`{"num":6.13,"strs":["a","b"]}`)
	createPath, err := conn.Create(path, data, flags, acl)
	if err != nil {
		panic(err)
	}
}

3.2 мониторинг агента конфигурации

  1. Благодаря характеристикам ZK он может поддерживать согласованность кластера и предоставлять механизм мониторинга, который может обеспечивать обратные вызовы при изменении содержимого узла.
  2. Контролируйте соответствующий бизнес-узел в config-agent
  3. При мониторинге изменений будут уведомления.Например, при изменении содержимого узла содержимое узла получается, а затем помещается на диск, либо сохраняется в памяти.
package main

import (
	"fmt"
	"github.com/samuel/go-zookeeper/zk"
	. "go-zk/connect"
	"os"
	"sync"
)
type Watch struct {

}

func (this *Watch)ZkChildrenWatch(c *zk.Conn, path string)  {
	for {
		v, _, get_ch, err := c.ChildrenW(path)
		if err != nil {
			fmt.Println(err)
		}

		fmt.Printf("value of path[%s]=[%s].\n", path, v)

		for {
			select {
			case ch_event := <-get_ch:
				{
					fmt.Printf("%+v\n", ch_event)

					if ch_event.Type == zk.EventNodeCreated {
						fmt.Printf("has new node[%d] create\n", ch_event.Path)
					} else if ch_event.Type == zk.EventNodeDeleted {
						fmt.Printf("has node[%s] detete\n", ch_event.Path)
					} else if ch_event.Type == zk.EventNodeDataChanged {
						this.Callback(c, ch_event.Path)
					} else if ch_event.Type == zk.EventNodeChildrenChanged {
						fmt.Printf("children node change%+v\n", ch_event.Path)
					}
				}
			}
			break
		}
	}
}

func (this *Watch)ZkNodeWatch(c *zk.Conn, path string)  {
	for {
		v, _, get_ch, err := c.GetW(path)
		if err != nil {
			fmt.Println(err)
		}

		fmt.Printf("value of path[%s]=[%s].\n", path, v)

		for {
			select {
			case ch_event := <-get_ch:
				{
					if ch_event.Type == zk.EventNodeCreated {
						fmt.Printf("has new node[%d] create\n", ch_event.Path)
					} else if ch_event.Type == zk.EventNodeDeleted {
						fmt.Printf("has node[%s] detete\n", ch_event.Path)
					} else if ch_event.Type == zk.EventNodeDataChanged {
						this.Callback(c, ch_event.Path)
					}
				}
			}
			break
		}
	}
}

func (this *Watch)Callback(c *zk.Conn, path string) {
	data, _, err := c.Get(path)
	if err != nil {
		fmt.Println(err)
	}

	// create file
	fileName := PathConfig.LocalPath + path + ".json"
	os.Create(fileName)
	f, err := os.OpenFile(fileName, os.O_WRONLY|os.O_TRUNC, 0600)
	defer f.Close()
	if err != nil {
		fmt.Println(err.Error())
	} else {
		_,err=f.Write([]byte(data))
		fmt.Println(err)
		return
	}

	fmt.Print("Write File OK !!!")
}

func main()  {
	conn := Connect()

	// 监听所有子节点变化
	children, _, err := conn.Children(PathConfig.ZNodePath)
	if err != nil {
		panic(err)
	}
	fmt.Printf("%+v\n", children)

	w := Watch{}

	var wg sync.WaitGroup
	wg.Add(1)
	go func(path string) {
		w.ZkChildrenWatch(conn, path)
	}(PathConfig.ZNodePath)
	wg.Wait()



	// 监听节点内容变化
	//var wg sync.WaitGroup
	//wg.Add(len(children))
	//
	//for _, path := range children{
	//	path = PathConfig.ZNodePath + "/" + path
	//	go func(path string) {
	//		defer wg.Done()
	//		log.Print("Zookeeper Watcher Starting, ", path)
	//		w.ZkNodeWatch(conn, path)
	//	}(path)
	//}
	//wg.Wait()

}

3.3 конфигурация загрузки клиента config-sdk

Есть много способов прочитать конфигурацию, две идеи:

  1. Непосредственное чтение файлов, прямое чтение бизнес-сторонами, .json, .ini, .toml и т. д.
  2. SDK можно комбинировать с config-agent.Если конфигурация загрузки не может прочитать файл, используйте агент, чтобы снова активно вытащить файл на локальный сервер, чтобы реализовать ленивую загрузку файла.

В-четвертых, отображение эффекта

# This is Zookeeper config file.

title = "Zookeeper config file"

[zookeeper]
servers = ["10.00.85.70:2181", "10.00.80.191:2181", "10.00.97.239:2181"]
port = 2181
session_timeout = 500
enabled = true

[path]
znode_path = "/huodong/conf"
local_path = "/tmp/zookeeper"
  1. Из-за локального теста он не был развёрнут на сервер из-за неполадок, меняем locah_path на "/tmp/zookeeper", "/tmp/zookeeper1", "/tmp/zookeeper2" и запускаем три процесса

  2. Подключитесь к серверу zk и измените содержимое узлаset /huodong/conf/huodong-0000000001 '{"num":6.13,"strs":["a","b"]}'

  3. Глядя на локальный файл, будет создан соответствующий файл конфигурации.

Столбец:

один,Знакомство с основами зоопарка

два,Строительство кластера Zookeeper

три,Реализация приложения zookeeper — центр конфигурации