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,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,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)),}
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_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,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_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,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,cur_err
}
go_nodes := convertToGoMap(&bnodes)
return go_nodes,nil
}
func GetBatchKeys(key string,cur_err
}
defer C.destroy_string_vector(&nodes)
var ret int
if idc == "" {
ret = int(C.qconf_get_batch_keys(c_key,nil))
} else {
c_idc := C.CString(idc)
defer C.free(unsafe.Pointer(c_idc))
ret = int(C.qconf_get_batch_keys(c_key,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: ","-k /dev/mykeyOfMyConf")
}
}
编译执行:
$ go build goqconf_demo.go
./goqconf_demo -k '/dev/mykeyOfMyConf'
key= /dev/mykeyOfMyConf val= http://xxx.xxx/api