Golang使用QConf教程

xuduorui · · 760 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xuduorui/article/details/78117520

Golang使用QConf教程

QConf 是一个分布式配置管理工具。 用来替代传统的配置文件,使得配置信息和程序代码分离,同时配置变化能够实时同步到客户端,而且保证用户高效读取配置,这使的工程师从琐碎的配置修改、代码提交、配置上线流程中解放出来,极大地简化了配置管理工作。

关于QConf的详细介绍可以看这里github.com/Qihoo360/QConf

特点

  • 一处修改,所有机器实时同步更新
  • 高效读取配置
  • 安装部署方便,使用简单
  • 服务器宕机、网络中断、集群迁移等异常情况对用户透明
  • 支持c/c++、shell、php、python、lua、java、go、node 等语言

架构

这里写图片描述

所有客户端通过libqconf,与本机的qconf-agent通过共享内存或消息队列通信。

编译goqconf的时候要使用

#cgo LDFLAGS: -lqconf -lm

如果找不到libqconf,通过查看qconf_agent来看qconf-agent所安装的目录

$ ps aux | grep qconf
root      1098  0.1  1.6 600300 131280 ?       Sl   Sep25   5:39 /usr/local/qconf/bin/qconf_agent

比如我的qconf-agent安装目录在:/usr/local/qconf/下,则在cgo后加条件-L/usr/local/qconf/lib
这样可以编译成功,但是在运行时还可能报错,找不到libqconf.so文件,这时需要在/usr/lib或/usr/lib64下创建libqconf的软连接:

$ sudo ln -s /usr/local/qconf/lib/libqconf.so /usr/lib64/libqconf.so
$ ll /usr/lib64/libqconf.so 
lrwxrwxrwx 1 root root 32 Sep 27 13:17 /usr/lib64/libqconf.so -> /usr/local/qconf/lib/libqconf.so

附上golibqconf.go的代码:

package go_qconf

/*
#cgo LDFLAGS: -lqconf -lm
#include <stdlib.h>
#include <stdio.h>
struct string_vector
{
    int count;      // the number of services
    char **data;    // the array of services
};
typedef struct string_vector string_vector_t;
typedef struct qconf_node
{
    char *key;
    char *value;
} qconf_node;
typedef struct qconf_batch_nodes
{
    int count;
    qconf_node *nodes;
} qconf_batch_nodes;
int qconf_init();
int qconf_destroy();
int init_string_vector(string_vector_t *nodes);
int destroy_string_vector(string_vector_t *nodes);
int init_qconf_batch_nodes(qconf_batch_nodes *bnodes);
int destroy_qconf_batch_nodes(qconf_batch_nodes *bnodes);
int qconf_get_conf(const char *path, char *buf, int buf_len, const char *idc);
int qconf_get_allhost(const char *path, string_vector_t *nodes, const char *idc);
int qconf_get_host(const char *path, char *buf, int buf_len, const char *idc);
int qconf_get_batch_conf(const char *path, qconf_batch_nodes *bnodes, const char *idc);
int qconf_get_batch_keys(const char *path, string_vector_t *nodes, const char *idc);
*/
import "C"

import (
    "fmt"
    "reflect"
    "unsafe"
)

type Errno int

func (e Errno) Error() string {
    s := errText[e]
    if s == "" {
        return fmt.Sprintf("unknown errno %d", int(e))
    }
    return s
}

var errText = map[Errno]string{
    -1: "Execute failure!",
    1:  "Error parameter!",
    2:  "Failed to malloc memory!",
    3:  "Failed to set share memory!",
    4:  "Failed to get zookeeper host!",
    5:  "Failed to get idc!",
    6:  "Buffer not enough!",
    7:  "Illegal data type!",
    8:  "Illegal data format!",
    10: "Failed to find key on given idc!",
    11: "Failed to open dump file!",
    12: "Failed to open tmp dump file!",
    13: "Failed to find key in dump!",
    14: "Failed to rename dump!",
    15: "Failed to write dump!",
    16: "Same with the value in share memory!",
    20: "Configure item error : out of range!",
    21: "Configure item error : not number!",
    22: "Configure item error : further characters exists!",
    30: "Configure item error : invalid ip!",
    31: "Configure item error : invalid port!",
    40: "No message exist in message queue!",
    41: "Length of message in the queue is too large!",
    71: "Error hostname!",
}

var (
    ErrOther               error = Errno(-1)
    ErrQconfParam          error = Errno(1)
    ErrQconfMem            error = Errno(2)
    ErrQconfTblSet         error = Errno(3)
    ErrQconfGetHost        error = Errno(4)
    ErrQconfGetIdc         error = Errno(5)
    ErrQconfBufNotEnough   error = Errno(6)
    ErrQconfDataType       error = Errno(7)
    ErrQconfDataFormat     error = Errno(8)
    ErrQconfNotFound       error = Errno(10)
    ErrQconfOpenDump       error = Errno(11)
    ErrQconfOpenTmpDump    error = Errno(12)
    ErrQconfNotInDump      error = Errno(13)
    ErrQconfRenameDump     error = Errno(14)
    ErrQconfWriteDump      error = Errno(15)
    ErrQconfSameValue      error = Errno(16)
    ErrQconfOutOfRange     error = Errno(20)
    ErrQconfNotNumber      error = Errno(21)
    ErrQconfOtherCharacter error = Errno(22)
    ErrQconfInvalidIp      error = Errno(30)
    ErrQconfInvalidPort    error = Errno(31)
    ErrQconfNoMessage      error = Errno(40)
    ErrQconfE2Big          error = Errno(41)
    ErrQconfHostname       error = Errno(71)
)

const (
    QCONF_DRIVER_GO_VERSION     = "1.2.2"
    QCONF_CONF_BUF_INIT_MAX_LEN = 2 * 1024
    QCONF_CONF_BUF_MAX_LEN      = 1024 * 1024
    QCONF_CONF_BUF_MULTIPLE     = 8
    QCONF_HOST_BUF_MAX_LEN      = 256

    QCONF_OK                 = 0
    QCONF_ERR_BUF_NOT_ENOUGH = 6
)

func init() {
    ret := C.qconf_init()
    if QCONF_OK != ret {
        panic(ret)
    }
}

func convertToGoSlice(nodes *C.string_vector_t) []string {
    length := int((*nodes).count)
    hdr := reflect.SliceHeader{
        Data: uintptr(unsafe.Pointer((*nodes).data)),
        Len:  length,
        Cap:  length,
    }
    charp_nodes := *(*[]*C.char)(unsafe.Pointer(&hdr))
    go_nodes := []string{}
    for i := 0; i < length; i++ {
        go_host := C.GoString(charp_nodes[i])
        go_nodes = append(go_nodes, go_host)
    }
    return go_nodes
}

func convertToGoMap(bnodes *C.qconf_batch_nodes) map[string]string {
    length := int((*bnodes).count)
    hdr := reflect.SliceHeader{
        Data: uintptr(unsafe.Pointer((*bnodes).nodes)),
        Len:  length,
        Cap:  length,
    }
    qconf_nodes := *(*[]C.qconf_node)(unsafe.Pointer(&hdr))
    go_nodes := map[string]string{}
    for i := 0; i < length; i++ {
        go_key := C.GoString(qconf_nodes[i].key)
        go_value := C.GoString(qconf_nodes[i].value)
        go_nodes[go_key] = go_value
    }
    return go_nodes
}

func GetConf(key string, idc string) (string, error) {
    c_key := C.CString(key)
    defer C.free(unsafe.Pointer(c_key))
    var ret int
    var c_ptr_value *C.char
    slice_length := QCONF_CONF_BUF_INIT_MAX_LEN

    for ret = QCONF_ERR_BUF_NOT_ENOUGH; ret == QCONF_ERR_BUF_NOT_ENOUGH && slice_length <= QCONF_CONF_BUF_MAX_LEN; slice_length *= QCONF_CONF_BUF_MULTIPLE {
        c_value := make([]C.char, slice_length)
        c_ptr_value = (*C.char)(unsafe.Pointer(&(c_value[0])))

        if idc == "" {
            ret = int(C.qconf_get_conf(c_key, c_ptr_value, C.int(slice_length), nil))
        } else {
            c_idc := C.CString(idc)
            defer C.free(unsafe.Pointer(c_idc))
            ret = int(C.qconf_get_conf(c_key, c_ptr_value, C.int(slice_length), c_idc))
        }
    }
    if QCONF_OK != ret {
        cur_err := Errno(ret)
        return "", cur_err
    }
    go_value := C.GoString(c_ptr_value)
    return go_value, nil
}

func GetHost(key string, idc string) (string, error) {
    c_key := C.CString(key)
    defer C.free(unsafe.Pointer(c_key))
    var c_host [QCONF_HOST_BUF_MAX_LEN]C.char
    c_ptr_host := (*C.char)(unsafe.Pointer(&(c_host[0])))

    var ret int
    if idc == "" {
        ret = int(C.qconf_get_host(c_key, c_ptr_host, QCONF_HOST_BUF_MAX_LEN, nil))
    } else {
        c_idc := C.CString(idc)
        defer C.free(unsafe.Pointer(c_idc))
        ret = int(C.qconf_get_host(c_key, c_ptr_host, QCONF_HOST_BUF_MAX_LEN, c_idc))
    }
    if QCONF_OK != ret {
        cur_err := Errno(ret)
        return "", cur_err
    }
    go_host := C.GoString(c_ptr_host)
    return go_host, nil
}

func GetAllHost(key string, idc string) ([]string, error) {
    c_key := C.CString(key)
    defer C.free(unsafe.Pointer(c_key))
    var nodes C.string_vector_t
    init_ret := C.init_string_vector(&nodes)
    if QCONF_OK != init_ret {
        cur_err := Errno(init_ret)
        return nil, cur_err
    }
    defer C.destroy_string_vector(&nodes)

    var ret int
    if idc == "" {
        ret = int(C.qconf_get_allhost(c_key, &nodes, nil))
    } else {
        c_idc := C.CString(idc)
        defer C.free(unsafe.Pointer(c_idc))
        ret = int(C.qconf_get_allhost(c_key, &nodes, c_idc))
    }
    if QCONF_OK != ret {
        cur_err := Errno(ret)
        return nil, cur_err
    }

    go_nodes := convertToGoSlice(&nodes)
    return go_nodes, nil
}

func GetBatchConf(key string, idc string) (map[string]string, error) {
    c_key := C.CString(key)
    defer C.free(unsafe.Pointer(c_key))
    var bnodes C.qconf_batch_nodes
    init_ret := C.init_qconf_batch_nodes(&bnodes)
    if QCONF_OK != init_ret {
        cur_err := Errno(init_ret)
        return nil, cur_err
    }
    defer C.destroy_qconf_batch_nodes(&bnodes)

    var ret int
    if idc == "" {
        ret = int(C.qconf_get_batch_conf(c_key, &bnodes, nil))
    } else {
        c_idc := C.CString(idc)
        defer C.free(unsafe.Pointer(c_idc))
        ret = int(C.qconf_get_batch_conf(c_key, &bnodes, c_idc))
    }
    if QCONF_OK != ret {
        cur_err := Errno(ret)
        return nil, cur_err
    }

    go_nodes := convertToGoMap(&bnodes)
    return go_nodes, nil
}

func GetBatchKeys(key string, idc string) ([]string, error) {
    c_key := C.CString(key)
    defer C.free(unsafe.Pointer(c_key))
    var nodes C.string_vector_t
    init_ret := C.init_string_vector(&nodes)
    if QCONF_OK != init_ret {
        cur_err := Errno(init_ret)
        return nil, cur_err
    }
    defer C.destroy_string_vector(&nodes)

    var ret int
    if idc == "" {
        ret = int(C.qconf_get_batch_keys(c_key, &nodes, nil))
    } else {
        c_idc := C.CString(idc)
        defer C.free(unsafe.Pointer(c_idc))
        ret = int(C.qconf_get_batch_keys(c_key, &nodes, c_idc))
    }
    if QCONF_OK != ret {
        cur_err := Errno(ret)
        return nil, cur_err
    }

    go_nodes := convertToGoSlice(&nodes)
    return go_nodes, nil
}

func Version() (string, error) {
    return QCONF_DRIVER_GO_VERSION, nil
}

goqconf_demo.go代码

package main

import (
    "fmt"
    "flag"
    "os"

    "./go_qconf"
)

func main() {
    idc := "test" //"corp"

    key := flag.String("k", "", "key of witch you get")
    reqVer := flag.Bool("v", false, "show version")
    flag.Parse()

    if *reqVer {
        ver,_ := go_qconf.Version()
        fmt.Println("qconf version: ", ver)
        return
    }   

    if len(*key) > 0 { 
        value, e := go_qconf.GetConf(*key, idc)
        if e != nil {
            fmt.Println("get error:", e)
            e = new(Error)
        } else {
            fmt.Println("key=", *key, "val=", value)
        }   
    } else {
        fmt.Println("Usage: ", os.Args[0] , " -k $key")
        fmt.Println("like: ", os.Args[0], "-k /dev/mykeyOfMyConf")
    }   

}

编译执行:

$ go build goqconf_demo.go
./goqconf_demo -k '/dev/mykeyOfMyConf'
key= /dev/mykeyOfMyConf val= http://xxx.xxx/api

有疑问加站长微信联系(非本文作者)

本文来自:CSDN博客

感谢作者:xuduorui

查看原文:Golang使用QConf教程

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

760 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传