配置_配置管理 - CSDN
  • 神经网络学习小记录42——windows下的tensorflow-gpu=1.13.2环境配置学习前言环境内容Anaconda安装下载Cudnn和CUDA配置tensorflow环境安装VSCODE 学习前言 好多人问环境怎么配置,还是出个教程吧。 环境内容 ...

    神经网络学习小记录42——windows下的tensorflow-gpu=1.13.2环境配置

    学习前言

    好多人问环境怎么配置,还是出个教程吧。
    在这里插入图片描述

    环境内容

    tensorflow-gpu:1.13.2
    keras:2.1.5
    numpy:1.17.4

    Anaconda安装

    最新版本的Anaconda没有VSCODE,如果大家为了安装VSCODE方便可以直接安装旧版的Anaconda,百度网盘连接如下。也可以装新版然后分开装VSCODE。
    链接: https://pan.baidu.com/s/12tW0Oad_Tqn7jNs8RNkvFA
    提取码: i83n

    取网上搜索Anaconda的官网:https://www.anaconda.com/distribution/
    在这里插入图片描述
    下载左边的python3.7版本,64位的,下载完成后打开:
    在这里插入图片描述
    选择安装的位置,可以不安装在C盘。
    在这里插入图片描述
    我选择了Add Anaconda to my PATH environment variable,我用起来觉得更好用。
    在这里插入图片描述
    等待安装完之后,就安装了Anaconda了。

    下载Cudnn和CUDA

    我这里使用的是tensorflow-gpu=1.13.2,因此会用到cuda10.0,与cuda10.0对应的cudnn是7.4.1.5,这个组合我实验过了,绝对是可以用的。

    cuda10.0官网的地址是:
    cuda10.0官网地址
    cudnn官网的地址是:需要大家进去后寻找7.4.1.5。
    cudnn官网地址

    官网下载是比较慢的,可以在百度云上下载,我给大家分享一手。

    链接: https://pan.baidu.com/s/1znYSRDtLNFLufAuItOeoyQ
    提取码: 8ggr

    下载完之后得到这两个文件。

    在这里插入图片描述
    在这里插入图片描述
    下载好之后可以打开cuda_10的exe文件进行安装。
    在这里插入图片描述
    这里选择自定义。
    不
    然后直接点下一步就行了。
    在这里插入图片描述
    安装完后在C盘这个位置可以找到根目录。
    C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.0
    然后大家把Cudnn的内容进行解压。
    在这里插入图片描述
    把这里面的内容直接复制到C盘的根目录下就可以了。
    在这里插入图片描述

    配置tensorflow环境

    Win+R启动cmd,在命令提示符内输入以下命令:

    conda create –n tensorflow-gpu python=3.6
    
    activate tensorflow-gpu 
    
    pip install tensorflow-gpu==1.13.2
    
    pip install keras==2.1.5
    

    需要注意的是,如果在pip中下载安装比较慢可以换个源,可以到用户文件夹下,创建一个pip文件夹,然后在pip文件夹里创建一个txt文件。
    在这里插入图片描述
    修改txt文件的内容,并且把后缀改成ini

    [global]
    index-url = http://pypi.mirrors.ustc.edu.cn/simple
    [install]
    use-mirrors =true
    mirrors =http://pypi.mirrors.ustc.edu.cn/simple/
    trusted-host =pypi.mirrors.ustc.edu.cn
    

    在这里插入图片描述
    在这里插入图片描述
    全部安装完成之后重启电脑。

    安装VSCODE

    我个人喜欢VSCODE……所以就安装它啦。
    打开anaconda,切换环境。
    在这里插入图片描述
    安装VSCODE,安装完就可以launch一下了,之后就可以把VScode固定到任务栏上,方便打开。
    在这里插入图片描述
    最新版本的Anaconda没有VSCODE因此可以直接百度VSCODE进行安装。

    安装完成后在左下角更改自己的环境就行了。
    在这里插入图片描述

    展开全文
  • 路由器配置实例--100例 Cisco 与 Intel路由器的对连配置实例 Cisco路由器VOIP 配置解析 Cisco路由器安全配置方案 Cisco路由器安全配置简易方案 Cisco路由器的安全配置 CISCO路由器配置基础 Cisco路由器配置信息在...
  • Vim的终极配置方案,完美的写代码界面! ——.vimrc

    万次阅读 多人点赞 2019-03-19 08:41:52
    先秀一下我的Vim界面 语法补全我用的是YouCompleteMe, 有目录树插件,taglist插件等。 通过插件管理器Vundle来进行安装,具体方法自行百度。...背景可以通过换张自己喜爱的壁纸,然后调终端的透明度,就可以对着...

    先秀一下我的Vim界面

     

    语法补全我用的是YouCompleteMe, 有目录树插件,taglist插件等。

    通过插件管理器Vundle来进行安装,具体方法自行百度。

     

    背景可以通过换张自己喜爱的壁纸,然后调终端的透明度,就可以对着自己喜爱的场景编程啦~

    有语法高亮,语句补全,显示行号,自动缩进等等功能。

    还有创建源文件自动添加头文件的功能,例如写一个.c程序

    自动添加了写在配置文件里的信息,包括作者名,邮箱,时间和程序的头文件等。

     

     

     

    配置

    如果你需要配置vim,只需在Home目录创建一个~/.vimrc文件即可以配置vim了,如需安装插件,在~/.vim目录下创建一个bundle文件夹,插件装在里面。(我通过Vundle管理插件,自行百度Vundle怎么使用),可以参考我的vimrc配置文件:

    含有完整的注释

    set nocompatible
    filetype on
    
    set rtp+=~/.vim/bundle/Vundle.vim
    call vundle#begin()
    
    
    " 这里根据自己需要的插件来设置,以下是我的配置 "
    "
    " YouCompleteMe:语句补全插件
    set runtimepath+=~/.vim/bundle/YouCompleteMe
    autocmd InsertLeave * if pumvisible() == 0|pclose|endif "离开插入模式后自动关闭预览窗口"
    let g:ycm_collect_identifiers_from_tags_files = 1           " 开启 YCM基于标签引擎
    let g:ycm_collect_identifiers_from_comments_and_strings = 1 " 注释与字符串中的内容也用于补全
    let g:syntastic_ignore_files=[".*\.py$"]
    let g:ycm_seed_identifiers_with_syntax = 1                  " 语法关键字补全
    let g:ycm_complete_in_comments = 1
    let g:ycm_confirm_extra_conf = 0                            " 关闭加载.ycm_extra_conf.py提示
    let g:ycm_key_list_select_completion = ['<c-n>', '<Down>']  " 映射按键,没有这个会拦截掉tab, 导致其他插件的tab不能用.
    let g:ycm_key_list_previous_completion = ['<c-p>', '<Up>']
    let g:ycm_complete_in_comments = 1                          " 在注释输入中也能补全
    let g:ycm_complete_in_strings = 1                           " 在字符串输入中也能补全
    let g:ycm_collect_identifiers_from_comments_and_strings = 1 " 注释和字符串中的文字也会被收入补全
    let g:ycm_global_ycm_extra_conf='~/.vim/bundle/YouCompleteMe/third_party/ycmd/cpp/ycm/.ycm_extra_conf.py'
    let g:ycm_show_diagnostics_ui = 0                           " 禁用语法检查
    inoremap <expr> <CR> pumvisible() ? "\<C-y>" : "\<CR>"             " 回车即选中当前项
    nnoremap <c-j> :YcmCompleter GoToDefinitionElseDeclaration<CR>     " 跳转到定义处
    let g:ycm_min_num_of_chars_for_completion=2                 " 从第2个键入字符就开始罗列匹配项
    "
    
    
    
    " github 仓库中的插件 "
    Plugin 'VundleVim/Vundle.vim'
    
    
    Plugin 'vim-airline/vim-airline'
    "vim-airline配置:优化vim界面"
    "let g:airline#extensions#tabline#enabled = 1
    " airline设置
    " 显示颜色
    set t_Co=256
    set laststatus=2
    " 使用powerline打过补丁的字体
    let g:airline_powerline_fonts = 1
    " 开启tabline
    let g:airline#extensions#tabline#enabled = 1
    " tabline中当前buffer两端的分隔字符
    let g:airline#extensions#tabline#left_sep = ' '
    " tabline中未激活buffer两端的分隔字符
    let g:airline#extensions#tabline#left_alt_sep = ' '
    " tabline中buffer显示编号
    let g:airline#extensions#tabline#buffer_nr_show = 1
    " 映射切换buffer的键位
    nnoremap [b :bp<CR>
    nnoremap ]b :bn<CR>
    " 映射<leader>num到num buffer
    map <leader>1 :b 1<CR>
    map <leader>2 :b 2<CR>
    map <leader>3 :b 3<CR>
    map <leader>4 :b 4<CR>
    map <leader>5 :b 5<CR>
    map <leader>6 :b 6<CR>
    map <leader>7 :b 7<CR>
    map <leader>8 :b 8<CR>
    map <leader>9 :b 9<CR>
    
    
    
    " vim-scripts 中的插件 "
    Plugin 'taglist.vim'
    "ctags 配置:F3快捷键显示程序中的各种tags,包括变量和函数等。
    map <F3> :TlistToggle<CR>
    let Tlist_Use_Right_Window=1
    let Tlist_Show_One_File=1
    let Tlist_Exit_OnlyWindow=1
    let Tlist_WinWidt=25
    
    Plugin 'The-NERD-tree'
    "NERDTree 配置:F2快捷键显示当前目录树
    map <F2> :NERDTreeToggle<CR>
    let NERDTreeWinSize=25 
    
    Plugin 'indentLine.vim'
    Plugin 'delimitMate.vim'
    
    " 非 github 仓库的插件"
    " Plugin 'git://git.wincent.com/command-t.git'
    " 本地仓库的插件 "
    
    call vundle#end()
    
    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    """""新文件标题
    """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    "新建.c,.h,.sh,.java文件,自动插入文件头 
    autocmd BufNewFile *.cpp,*.[ch],*.sh,*.java exec ":call SetTitle()" 
    ""定义函数SetTitle,自动插入文件头 
    func SetTitle() 
    	"如果文件类型为.sh文件 
    	if &filetype == 'sh' 
    		call setline(1, "##########################################################################") 
    		call append(line("."), "# File Name: ".expand("%")) 
    		call append(line(".")+1, "# Author: amoscykl") 
    		call append(line(".")+2, "# mail: amoscykl980629@163.com") 
    		call append(line(".")+3, "# Created Time: ".strftime("%c")) 
    		call append(line(".")+4, "#########################################################################") 
    		call append(line(".")+5, "#!/bin/zsh")
    		call append(line(".")+6, "PATH=/home/edison/bin:/home/edison/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/work/tools/gcc-3.4.5-glibc-2.3.6/bin")
    		call append(line(".")+7, "export PATH")
    		call append(line(".")+8, "")
    	else 
    		call setline(1, "/*************************************************************************") 
    		call append(line("."), "	> File Name: ".expand("%")) 
    		call append(line(".")+1, "	> Author: amoscykl") 
    		call append(line(".")+2, "	> Mail: amoscykl@163.com ") 
    		call append(line(".")+3, "	> Created Time: ".strftime("%c")) 
    		call append(line(".")+4, " ************************************************************************/") 
    		call append(line(".")+5, "")
    	endif
    	if &filetype == 'cpp'
    		call append(line(".")+6, "#include<iostream>")
        	call append(line(".")+7, "using namespace std;")
    		call append(line(".")+8, "")
    	endif
    	if &filetype == 'c'
    		call append(line(".")+6, "#include<stdio.h>")
    		call append(line(".")+7, "")
    	endif
    	"	if &filetype == 'java'
    	"		call append(line(".")+6,"public class ".expand("%"))
    	"		call append(line(".")+7,"")
    	"	endif
    	"新建文件后,自动定位到文件末尾
    	autocmd BufNewFile * normal G
    endfunc 
    """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    "键盘命令
    """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    
    nmap <leader>w :w!<cr>
    nmap <leader>f :find<cr>
    
    " 映射全选+复制 ctrl+a
    map <C-A> ggVGY
    map! <C-A> <Esc>ggVGY
    map <F12> gg=G
    " 选中状态下 Ctrl+c 复制
    vmap <C-c> "+y
    
    
    
    """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    ""实用设置
    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    " 设置当文件被改动时自动载入
    set autoread
    " quickfix模式
    autocmd FileType c,cpp map <buffer> <leader><space> :w<cr>:make<cr>
    "代码补全 
    set completeopt=preview,menu 
    "允许插件  
    filetype plugin on
    "共享剪贴板  
    set clipboard=unnamed 
    "从不备份  
    set nobackup
    "make 运行
    :set makeprg=g++\ -Wall\ \ %
    "自动保存
    set autowrite
    set ruler                   " 打开状态栏标尺
    set cursorline              " 突出显示当前行
    set magic                   " 设置魔术
    set guioptions-=T           " 隐藏工具栏
    set guioptions-=m           " 隐藏菜单栏
    "set statusline=\ %<%F[%1*%M%*%n%R%H]%=\ %y\ %0(%{&fileformat}\ %{&encoding}\ %c:%l/%L%)\
    " 设置在状态行显示的信息
    set foldcolumn=0
    set foldmethod=indent 
    set foldlevel=3 
    set foldenable              " 开始折叠
    " 不要使用vi的键盘模式,而是vim自己的
    set nocompatible
    " 语法高亮
    set syntax=on
    " 去掉输入错误的提示声音
    set noeb
    " 在处理未保存或只读文件的时候,弹出确认
    set confirm
    " 自动缩进
    set autoindent
    set cindent
    " Tab键的宽度
    set tabstop=4
    " 统一缩进为4
    set softtabstop=4
    set shiftwidth=4
    " 不要用空格代替制表符
    set noexpandtab
    " 在行和段开始处使用制表符
    set smarttab
    " 显示行号
    set number
    " 历史记录数
    set history=1000
    "禁止生成临时文件
    set nobackup
    set noswapfile
    "搜索忽略大小写
    set ignorecase
    "搜索逐字符高亮
    set hlsearch
    set incsearch
    "行内替换
    set gdefault
    "编码设置
    set enc=utf-8
    set fencs=utf-8,ucs-bom,shift-jis,gb18030,gbk,gb2312,cp936
    "语言设置
    set langmenu=zh_CN.UTF-8
    set helplang=cn
    " 我的状态行显示的内容(包括文件类型和解码)
    set statusline=%F%m%r%h%w\ [FORMAT=%{&ff}]\ [TYPE=%Y]\ [POS=%l,%v][%p%%]\ %{strftime(\"%d/%m/%y\ -\ %H:%M\")}
    set statusline=[%F]%y%r%m%*%=[Line:%l/%L,Column:%c][%p%%]
    " 总是显示状态行
    set laststatus=2
    " 命令行(在状态行下)的高度,默认为1,这里是2
    set cmdheight=2
    " 侦测文件类型
    filetype on
    " 载入文件类型插件
    filetype plugin on
    " 为特定文件类型载入相关缩进文件
    filetype indent on
    " 保存全局变量
    set viminfo+=!
    " 带有如下符号的单词不要被换行分割
    set iskeyword+=_,$,@,%,#,-
    " 字符间插入的像素行数目
    set linespace=0
    " 增强模式中的命令行自动完成操作
    set wildmenu
    " 使回格键(backspace)正常处理indent, eol, start等
    set backspace=2
    " 允许backspace和光标键跨越行边界
    set whichwrap+=<,>,h,l
    " 可以在buffer的任何地方使用鼠标(类似office中在工作区双击鼠标定位)
    set mouse=a
    set selection=exclusive
    set selectmode=mouse,key
    " 通过使用: commands命令,告诉我们文件的哪一行被改变过
    set report=0
    " 在被分割的窗口间显示空白,便于阅读
    set fillchars=vert:\ ,stl:\ ,stlnc:\
    " 高亮显示匹配的括号
    set showmatch
    " 匹配括号高亮的时间(单位是十分之一秒)
    set matchtime=1
    " 光标移动到buffer的顶部和底部时保持3行距离
    set scrolloff=3
    " 为C程序提供自动缩进
    set smartindent
    " 高亮显示普通txt文件(需要txt.vim脚本)
     au BufRead,BufNewFile *  setfiletype txt
    "自动补全
    :inoremap ( ()<ESC>i
    :inoremap ) <c-r>=ClosePair(')')<CR>
    ":inoremap { {<CR>}<ESC>O
    ":inoremap } <c-r>=ClosePair('}')<CR>
    :inoremap [ []<ESC>i
    :inoremap ] <c-r>=ClosePair(']')<CR>
    :inoremap " ""<ESC>i
    :inoremap ' ''<ESC>i
    function! ClosePair(char)
    	if getline('.')[col('.') - 1] == a:char
    		return "\<Right>"
    	else
    		return a:char
    	endif
    endfunction
    filetype plugin indent on 
    "打开文件类型检测, 加了这句才可以用智能补全
    set completeopt=longest,menu
    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""

    然后保存文件,重新打开终端就行了!

     

     

     

     

     

    展开全文
  • ES学习记录1——ES基本配置

    千次阅读 2018-11-16 18:43:02
     Elasticsearch简称ES,官网对它的介绍是 The Heart of the Elastic Stack. Elasticsearch is a distributed, RESTful search and analytics engine capable of solving a growing number of use cases....

     Elasticsearch简称ES,官网对它的介绍是

    The Heart of the Elastic Stack. Elasticsearch is a distributed, RESTful search and analytics engine capable of solving a growing number of use cases. As the heart of the Elastic Stack, it centrally stores your data so you can discover the expected and uncover the unexpected.

     Elasticsearch是一个高度可扩展的开源全文搜索和分析引擎。它几乎可以提供几乎实时的方式快速存储、搜索和分析大量的数据,它通常用作底层引擎/技术,为具有复杂搜索功能和要求的应用程序提供支持。它是一个分布式、支持Restful搜索以及分析的引擎,是Elastic Stack的核心产品。主要使用场景有:

    • 在线网上商城,提供用户搜索你所卖的商品功能。在这个例子中,你可以使用Elasticsearch去存储你的全部的商品目录和存货清单并且提供搜索和搜索自动完成以及搜索推荐功能。
    • 收集日志或者业务数据,并且去分析并从这些数据中挖掘寻找市场趋势、统计资料、摘要信息或者反常情况。在这个例子中,你可以使用Logstash(part of the Elasticsearch/Logstash/Kibana stack)去收集、聚合并且解析你的数据,然后通过Logstash将数据注入Elasticsearch。一旦数据进入Elasticsearch,你就可以运行搜索和聚集并且从中挖掘任何你感兴趣的数据。
    • 一个价格预警平台,它可以让那些对价格精明的客户指定一个规则,比如:“我相中了一个电子产品,并且我想在下个月任何卖家的这个电子产品的价格低于多少钱的时候提醒我”。在这个例子中,你可以抓取所有卖家的价格,把价格放入Elasticsearch并且使用Elasticsearch的反向搜索(过滤器/抽出器)功能来匹配价格变动以应对用户的查询并最终一旦发现有匹配结果时给用户弹出提示框。
    • 有分析学/商业情报的需求并且想快速审查、分析并使用图像化进行展示,并且在一个很大的数据集上查询点对点的问题(试想有百万或千万的记录)。在这个例子中,你可以使用Elasticsearch去存储你的数据然后使用Kibana(part of the Elasticsearch/Logstash/Kibana stack)去构建定制化的仪表盘。这样你就可以很直观形象的了解对你重要的数据。此外,你可以使用Elasticsearch的集成功能,靠你的数据去展现更加复杂的商业情报查询。

    Elasticsearch底层Lucene开源库,其实它就是帮我们写了代码去调用Lucene的接口,提供了REST API的操作接口,JDK版本至少是JDK 8才能安装使用,具体介绍及其使用见官网文档

    1. 启动

     启动Elasticsearch:elasticsearch-6.4.0\bin\elasticsearch.bat,可以在DOS窗口看到启动信息,最终会看到类似于下面的一行(主要是节点名[V8JnETy]可能会不同):

    [2018-09-01T10:14:11,633][INFO ][o.e.n.Node               ] [V8JnETy] started
    

    在浏览器端访问9200端口(或者直接在powershell中输入curl http://localhost:9200命令),得到一个JSON格式的说明信息:

    {
      "name" : "V8JnETy",
      "cluster_name" : "elasticsearch",
      "cluster_uuid" : "Pkr4spGJTMy0K5BQ4rn5GA",
      "version" : {
        "number" : "6.4.0",
        "build_flavor" : "default",
        "build_type" : "zip",
        "build_hash" : "595516e",
        "build_date" : "2018-08-17T23:18:47.308994Z",
        "build_snapshot" : false,
        "lucene_version" : "7.4.0",
        "minimum_wire_compatibility_version" : "5.6.0",
        "minimum_index_compatibility_version" : "5.0.0"
      },
      "tagline" : "You Know, for Search"
    }
    

    这里可以手动更改集群cluter和节点名node,命令如下:

    ./elasticsearch  -Ecluster.name=my_first_cluster -Enode.name=my_first_node
    

    节点名为V8JnETy、集群名字为elasticsearch(默认名),以及版本信息;

    默认情况Elasticsearch只允许本机访问,如果需要远程访问,需要配置elasticsearch-6.4.0\config\elasticsearch.yml中的network.host属性(大约在55行处),将其改为network.host: 0.0.0.0,表示任何IP都可以访问,实际开发中会设置为具体的IP。

    1.1 ES的集群配置

     ES的集群配置(都是修改ES的配置文件elasticsearch.yml)主要分为:主节点配置和从节点配置。

    配置主节点jack_master

    # 配置分布式主节点
    # 集群名
    cluster.name: jack
    # 节点名
    node.name: jack_master
    # 是否为主节点
    node.master: true
    # 绑定IP
    network.host: 127.0.0.1
    

    然后再次解压ES安装包到其他目录,每解压一个出来就可以配置一个ES的从节点,比如这里是解压两次就可以配置2个从节点,分别修改它们的配置文件让它们加入到集群jack中:

    配置从节点jack_slave1

    # 配置从节点的信息
    # 集群名一定要和主节点所在集群名一致,默认集群按名字匹配
    cluster.name: jack
    # 节点名
    node.name: jack_slave1
    # 绑定IP
    network.host: 127.0.0.1
    # 默认端口是9200,如果不配置就会和主节点端口冲突
    http.port: 8100
    
    # 用于寻找集群,不加这个配置这个节点是游离在集群之外的
    discovery.zen.ping.unicast.hosts: ["127.0.0.1"]
    

    配置从节点jack_slave2

    # 配置从节点的信息
    # 集群名一定要和主节点所在集群名一致,默认集群按名字匹配
    cluster.name: jack
    # 节点名
    node.name: jack_slave2
    # 绑定IP
    network.host: 127.0.0.1
    # 默认端口是9200,如果不配置就会和主节点端口冲突
    http.port: 8200
    
    # 用于寻找集群,不加这个配置这个节点是游离在集群之外的
    discovery.zen.ping.unicast.hosts: ["127.0.0.1"]
    

    就照着上面的步骤ES可以轻松实现扩容,启动的时候注意节点启动顺序,先主节点再从节点再Kibana,在Kibana监控页面可以看到启动的三个节点,启动界面和监控页面如下:
    这里写图片描述

    这里写图片描述
    【问题】
    后来在启动集群时(场景:主节点jack_master启动后无警告,在启动从节点jack_slave1时,主节点窗口抛出此警告),出现如下的警告:

    [2018-11-16T08:53:31,911][WARN ][o.e.d.z.ElectMasterService] [jack_master] value for setting "discovery.zen.minimum_master_nodes" is too low. This can result in data loss! Please set it to at least a quorum of master-eligible nodes (current value: [-1], total number of master-eligible nodes used for publishing in this round: [2])
    

    上述警告是因为默认情况下discovery.zen.minimum_master_nodes=1一台服务器只能有一个主节点(选举一个Master需要多少节点,即最少候选节点),这个参数根据经验一般设置为N/2+1(若不为整数,则向下取整),N为集群中节点的数量。这个参数也是我在后面学习过程中导致脑裂的原因,扯得有点远了,回来上述的问题,可以配置上述参数为2即可(我本地是3个节点)。

    1.2 ES配置

     ES有很好的默认值,所以只需要很少的配置,可以使用Cluster Update Settings API在正在运行的群集上更改大多数设置。主要是指本地的一些配置,借助于配置文件进行配置,主要分为3块:

    • elasticsearch.yml,ES的配置文件;
    • jvm.options,ES的JVM配置文件;
    • log4j2.properties,ES的日志配置文件;

    关于配置文件的路径,默认是解压缩包的解压路径$ES_HOME/config,但是具体的配置路径可以通过ES_PATH_CONF环境变量来改变,比如:

    ES_PATH_CONF=/path/to/my/config ./bin/elasticsearch
    

    ES的配置文件是yaml格式,下面是配置data和logs目录的方法:

    path.data: /var/lib/elasticsearch
    path.logs: /var/log/elasticsearch
    

    环境变量还可以使用${...}这样的形式配置,比如:

    node.name:    ${HOSTNAME}
    network.host: ${ES_NETWORK_HOST}
    

    ES配置文件中,可配置的参数有(汇总,来源于网络):

    # es集群名,默认是elasticsearch,es会自动发现在同一网段下的es,如果在同一网段下有多个集群,就可以用这个属性来区分不同的集群
    cluster.name: elasticsearch
    
    # 节点名,默认随机指定一个name列表中名字,该列表在es的jar包中config文件夹里name.txt文件中,6.4.0版本中好像已经没有这个文件了
    node.name: "Franz Kafka"
    
    # 候选资格,表示该节点是否有资格被选举成为master,默认是true,es默认集群中的第一台机器为master,如果这台机挂了就会重新选举master
    node.master: true
    
    # 表示该节点是否存储索引数据,默认为true
    node.data: true
    
    # 设置索引分片个数,默认为5
    index.number_of_shards: 5
    
    # 设置索引副本个数,默认为1
    index.number_of_replicas: 1
    
    # 设置ES配置文件的存储路径,默认是es根目录下的config文件夹下
    path.conf: /path/to/conf
    
    # 设置索引数据的存储路径,默认是es根目录下的data文件夹下,可以设置多个存储路径,用逗号隔开,例:path.data: /path/to/data1,/path/to/data2
    path.data: /path/to/data
    
    # 设置临时文件的存储路径,默认是es根目录下的work文件夹
    path.work: /path/to/work
    
    # 设置日志文件的存储路径,默认是es根目录下的logs文件夹
    path.logs: /path/to/logs
    
    # 设置插件的存放路径,默认是es根目录下的plugins文件夹
    path.plugins: /path/to/plugins
    
    # 设置ES可以是否可以锁定内存,因为当jvm开始swapping时es的效率会降低,所以要保证它不swap,可以把ES_MIN_MEM和ES_MAX_MEM两个环境变量设置成同一个值,并且保证机器有足够的内存分配给es。同时也要允许ES的进程可以锁住内存,linux下可以通过`ulimit -l unlimited`命令
    bootstrap.mlockall: true
    
    # 设置绑定的ip地址,可以是ipv4或ipv6的,默认为0.0.0.0
    network.bind_host: 192.168.0.1
    
    # 设置其它节点和该节点交互的ip地址(真实IP),如果不设置它会自动判断
    network.publish_host: 192.168.0.1
    
    # 这个参数是用来同时设置bind_host和publish_host上面两个参数
    network.host: 192.168.0.1
    
    # 设置节点间交互的tcp端口,默认是9300
    transport.tcp.port: 9300
    
    # 设置是否压缩tcp传输时的数据,默认为false,不压缩
    transport.tcp.compress: true
    
    # 设置对外服务的http端口,默认为9200
    http.port: 9200
    
    # 设置内容的最大容量,默认100mb
    http.max_content_length: 100mb
    
    # 是否使用http协议对外提供服务,默认为true,开启
    http.enabled: false
    
    # gateway的类型,默认为local即为本地文件系统,可以设置为本地文件系统,分布式文件系统,hadoop的HDFS,和amazon的s3服务器,其它文件系统的设置方法下次再详细说
    gateway.type: local
    
    # 设置集群中N个节点启动时进行数据恢复,默认为1
    gateway.recover_after_nodes: 1
    
    # 设置初始化数据恢复进程的超时时间,默认是5分钟
    gateway.recover_after_time: 5m
    
    # 设置这个集群中节点的数量,默认为2,一旦这N个节点启动,就会立即进行数据恢复
    gateway.expected_nodes: 2
    
    # 初始化数据恢复时,并发恢复线程的个数,默认为4
    cluster.routing.allocation.node_initial_primaries_recoveries: 4
    
    # 添加删除节点或负载均衡时并发恢复线程的个数,默认为4
    cluster.routing.allocation.node_concurrent_recoveries: 2
    
    # 设置数据恢复时限制的带宽,如入100mb,默认为0,即无限制
    indices.recovery.max_size_per_sec: 0
    
    # 来限制从其它分片恢复数据时最大同时打开并发流的个数,默认为5
    indices.recovery.concurrent_streams: 5
    
    # 设置这个参数来保证集群中的节点可以知道其它N个有master资格的节点。默认为1,对于大的集群来说,可以设置大一点的值(2-4)
    discovery.zen.minimum_master_nodes: 1
    
    # 设置集群中自动发现其它节点时ping连接超时时间,默认为3秒,对于比较差的网络环境可以高点的值来防止自动发现时出错
    discovery.zen.ping.timeout: 3s
    
    # 设置是否打开多播发现节点,默认是true
    discovery.zen.ping.multicast.enabled: false
    
    # 设置集群中master节点的初始列表,可以通过这些节点来自动发现新加入集群的节点
    discovery.zen.ping.unicast.hosts: ["host1", "host2:port", "host3[portX-portY]"]
    

    1.3 ES的JVM配置

     ES的java虚拟机设置,很少需要手动配置,如果需要改动,绝大部分是改动堆的大小,设置JVM首选方式是配置jvm.options文件,这个配置文件包含了一系列分隔线分隔的JVM参数,使用的是一种特殊的语法,具体规则如下:

    • 仅包含空格的行将被忽略;
    • #开头的行被视作注释;
    • -开头的行被视作JVM选项,这个选项独立于JVM而应用,如-Xmx2g
    • 以数字开头后面接-:符号的,也被视作JVM选项,但仅在JVM的版本大于等于这个数字时才使用,如8-:-Xmx2g(大于等于JDK8版本才适用);
    • 以数字开头后面接:-符号的,也被视作JVM选项,但仅在JVM版本和数字相同时才适用,如8:-Xmx2g(仅适用于JDK8);
    • 以数字开头后接-数字:的,也被视作JVM想象,但仅在JVM版本在这两个数字区间内,如8-9:-Xmx2g
    • 除此之外的所有行都不生效;

    1.4 ES的安全配置

     ES中有些配置是敏感的,仅通过系统文件权限来保护是不够的,为此,Elasticsearch提供了一个密钥库和elasticsearch-keystore工具来管理密钥库中的设置。这些设置就像elasticsearch.yml中的常规配置一样,需要在集群中的每个节点进行指定,目前,所有安全设置都是基于特定于节点的设置,每个节点上的值必须相同。

    1.5 ES的日志配置

     ES采用log4j 2作日志管理,log4j 2可以使用log4j2.properties文件进行配置,ES就暴露出三个属性:${sys:es.logs.base_path}(解析为日志目录)、${sys:es.logs.cluster_name}(解析为集群名,在默认配置中用作日志文件名的前缀)和${sys:es.logs.node_name}(如果通过node.name显式的设置了节点名称,将解析为节点名),可以在配置文件中引用来确定日志文件的位置。

     加设置ES的根目录是/var/log/elasticsearch,集群名为production,下面是ES中log4j 2的部分配置:

    # 配置RollingFile的附加器
    appender.rolling.type = RollingFile
    appender.rolling.name = rolling
    # %ES_Home%/节点名.log就是日志文件的路径和名字,即/var/log/elasticsearch/production.log
    appender.rolling.fileName = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}.log
    appender.rolling.layout.type = PatternLayout
    appender.rolling.layout.pattern = [%d{ISO8601}][%-5p][%-25c{1.}] %marker%.-10000m%n
    # 滚动日志到/var/log/elasticsearch/production-yyyy-MM-dd-i.log,日志将在每个卷上压缩,i递增
    appender.rolling.filePattern = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}-%d{yyyy-MM-dd}-%i.log.gz
    appender.rolling.policies.type = Policies
    # 使用基于时间的滚动策略
    appender.rolling.policies.time.type = TimeBasedTriggeringPolicy
    # 每1天滚动日志
    appender.rolling.policies.time.interval = 1
    # 在日界上对齐卷(而不是每隔二十四小时滚动)
    appender.rolling.policies.time.modulate = true
    # 使用基于大小的滚动策略
    appender.rolling.policies.size.type = SizeBasedTriggeringPolicy
    # 256 MB后滚动日志
    appender.rolling.policies.size.size = 256MB
    # 滚动日志时使用删除操作
    appender.rolling.strategy.type = DefaultRolloverStrategy
    appender.rolling.strategy.fileIndex = nomax
    appender.rolling.strategy.action.type = Delete
    appender.rolling.strategy.action.basepath = ${sys:es.logs.base_path}
    # 仅删除与文件模式匹配的日志
    appender.rolling.strategy.action.condition.type = IfFileName
    # 该模式仅删除主日志
    appender.rolling.strategy.action.condition.glob = ${sys:es.logs.cluster_name}-*
    # 仅在我们累积了太多压缩日志时才删除
    appender.rolling.strategy.action.condition.nested_condition.type = IfAccumulatedFileSize
    # 压缩日志的大小条件为2 GB
    appender.rolling.strategy.action.condition.nested_condition.exceeds = 2GB
    

    【注意】可以使用.gz替换appender.rolling.filePattern属性中的.zip(使用zip格式压缩滚动日志),如果删除.gz扩展名,则日志将不会在滚动时进行压缩。

     如果想指定时间段内保留日志文件,可以使用带有删除操作的翻转策略。

    # 配置DefaultRolloverStrategy
    appender.rolling.strategy.type = DefaultRolloverStrategy
    # 配置删除操作以处理翻转
    appender.rolling.strategy.action.type = Delete
    # Elasticsearch日志的基本路径
    appender.rolling.strategy.action.basepath = ${sys:es.logs.base_path}
    # 处理翻转时应用的条件
    appender.rolling.strategy.action.condition.type = IfFileName
    # 从基本路径中删除文件(和全局${sys:es.logs.cluster_name}-*匹配的),这只需要删除已滚动的Elasticsearch日志,但不能删除已弃用和慢速日志
    appender.rolling.strategy.action.condition.glob = ${sys:es.logs.cluster_name}-* 
    # 应用于与glob匹配的文件的嵌套条件
    appender.rolling.strategy.action.condition.nested_condition.type = IfLastModified
    # 保留日志七天
    appender.rolling.strategy.action.condition.nested_condition.age = 7D 
    
    展开全文
  • 随着业务的发展,应用系统中的配置通常会越来越多,常见的一些应用配置大致会有数据源配置,数据源组件配置,业务组件配置等,对于这类配置都会比较稳定且较少变化,通常会放在文件中随应用一起发布。但实际中会有...
    在大型集群和分布式应用中,配置不宜分散到节点中,应该集中管理,为各种业务平台提供统一的配置管理服务。
    随着业务的发展,应用系统中的配置通常会越来越多,常见的一些应用配置大致会有数据源配置,数据源组件配置,业务组件配置等,对于这类配置都会比较稳定且较少变化,通常会放在文件中随应用一起发布。但实际中会有某些配置信息变化有一定频率和规律,并且希望能够做到尽量实时,比如一些营销类,或活动类应用系统,若使用传统的配置文件,加上重新发布应用可能会有些不方便,因此,才有了分布式配置管理平台,旨在能更好地解决这类问题。

    统一配置管理 VS 分布式管理配置
    (中央管理VS去中心化) 

    这个技术话题让我们想起了svn和git之争,统一管理和分布式之间的那些事儿,各有千秋。

     编程开发+脚本 或者借助第三方开源自动化工具 

    spring cloud/dubbo/autoconfig  +docker+k8s+ansible/saltstack  目前业界通用的解决方案

    Disconf+ZooKeeper+Spring (360和淘宝也开源了自己的统一配置管理)
    XXL-CONF
    diablo 
    ansible-playbook
    Fourinone
    saltstack
    DCMP

     推荐用 分布式管理配置  现在开源社区很多  你可以去搜一哈 
    选适合自己的  别人推荐的 不一定是最好的 

     分布式配置管理 - 开源中国社区 https://www.oschina.net/search?scope=all&q=%E5%88%86%E5%B8%83%E5%BC%8F%E9%85%8D%E7%BD%AE%E7%AE%A1%E7%90%86 

    分布式配置管理平台的设计与实现


    随着业务的发展,应用系统中的配置通常会越来越多,常见的一些应用配置大致会有数据源配置,数据源组件配置,业务组件配置等,对于这类配置都会比较稳定且较少变化,通常会放在文件中随应用一起发布。但实际中会有某些配置信息变化有一定频率和规律,并且希望能够做到尽量实时,比如一些营销类,或活动类应用系统,若使用传统的配置文件,加上重新发布应用可能会有些不方便,因此,才有了分布式配置管理平台,旨在能更好地解决这类问题。本文将介绍相关细节,及一个轻量的开源实现(https://github.com/ihaolin/diablo)

    分布式配置平台的一些应用场景

    分布式配置,也即配置中心。通常有以下的场景或需求,可以需要考虑使用分布式配置:

    • 对某些配置的更新,不想要重启应用,并且能近似实时生效;

    • 希望将配置进行统一管理,而非放入各应用的配置文件中;

    • 对于某些应用系统,其某些配置变更比较频繁,规律;

    • 通常配置中心也可作为在其他分布式应用中的感知组件,比如典型的Zookeeper;

    • ...。

    分布式配置平台需要满足的一些基本特性

    对于一个可靠的分布式配置平台,大致应该满足一些基本特性,如:

    • 高可用性:服务器集群应该无单点故障,即只要集群中还有存活的节点,就能提供服务;

    • 容错性:容错性主要针对客户端,应保证即便在配置平台不可用时,也不影响客户端的正常运行;

    • 高性能:对于配置平台,主要操作则是获取配置,不能因为获取配置给应用带来不可接受的损失;

    • 可靠的存储:这包括数据的备份容灾,一致性等,通过数据库和一些运维手段可以解决;

    • 近似实时生效:对于配置的变更,客户端应用能够及时感知;

    • 负载均衡:为了尽量提升服务器集群的性能及稳定性,应尽量保证客户端的请求能尽量均衡负载到各服务器节点;

    • 扩展性:服务器集群应该保证做到无感扩容,以提升集群服务能力;

    • ...。

    分布式配置平台Diablo的设计与实现

    Diablo架构设计

    Diablo的架构设计比较简单轻量,如图:

    • Apps:及各类业务应用实例;

    • Servers:为客户端提供获取配置服务的Server集群;

    • Redis Storage Service:用作数据存储。

    Diablo模型设计

    • 应用(App):通常,对于一个用于各外部系统的平台,都可以抽象这些系统为应用,用于标识或是分组。对于应用环境,个人觉得应交由应用去处理,而不是在平台为各应用提供不同环境,比如可以通过应用名称就能区分不同环境。除此外,通常需要让应用提供一些安全方面的配置,如签名Key,加密Key等,可用于在与外部应用交互中作一些安全处理(如签名,加密等);

    • 配置项(Config):对于配置平台,其数据模型比较简单,核心数据就是配置项。配置项除了基本的配置名称和配置值外,通常还需要有用于判定配置项是否变更的字段,可以用MD5值等。

    对等的服务器集群

    Diablo集群中的Server被视为是对等的,即各节点没有主从(Master/Slave)关系,是逻辑相等的,这样就避免了Master/Slave架构带来的问题,如数据同步延迟,数据丢失等问题。

    高性能处理

    客户端应用获取配置时,仅会从本地缓存中获取,开发人员在控制台更改配置后,会通知客户端刷新缓冲;

    使用Redis作存储

    配置平台本身并没有太多复杂的关联关系,因此使用NoSQL也能满足常用的查询。在设计存储Key上,因尽量保证Key的简洁清晰,比如,存储应用记录,可以以apps:1来标识一条记录,而存储结构最好使用hash,而不是使用JSON字符串。在使用Redis时,diablo避免使用了一些特殊函数,如管道,事务等,因为这些函数在一些Redis的高可用解决方案(如Redis Cluster,Redis Proxy等)中通常不支持,这样用户可以自由使用不同的Redis高可用方案。

    客户端的实现

    • 重试等待:diablo会通过重试等待等机制保证,在服务端集群不可用时,也不会影响客户端应用的正常运行,而是等待集群恢复;

    • 请求负载:为了使服务器集群的各节点的负载尽量均衡,在客户端进行请求处理前,服务端会为客户端分配一个可用的Server节点,后续的请求将在该节点上,这里使用的负载算法是一致性哈希;

    • 额外参数:通常对于客户端实现,在与服务器交互过程中,除了必要的数据外,还会携带一些额外参数,如客户端版本号(为后期作兼容性处理),语言类型(后端可针对不同语言进行处理)等。

    配置更新实时生效

    配置更新实时生效是配置平台的核心功能之一,对于获取配置的方式,大致有两种模式:

    • Pull模式:即客户端主动向服务端拉取配置信息,通常是客户端定时轮询拉取。这种方式最简单稳定,但是存在时间间隔问题,间隔太长,配置延迟更新越大,间隔太短对服务器会造成过多的压力;

    • Push模式:即当配置发生变更时,服务端主动推送更新至各个客户端。这种方式能保证配置更新实时生效,但需要在客户端/服务端建立长链接,服务端需要处理各种异常情况和协议规范,保证更新能实时推送成功,实现起来相对麻烦些。

    diablo使用了特殊Pull模式,即(长轮询)Long Pulling。当客户端发起Http请求后,服务端接收处理完请求,并不会立即返回客户端,而是等待一定条件发生或超时后才返回客户端,这里的一定条件就是当有配置发生变更时,这样就有效减少了客户端请求,也达到了实时生效的目的。对于Java长连接的实现,主要使用了Servlet规范中的AsyncContext,使得服务端接收到请求后,并不会让Servlet容器立即返回客户端,而是当调用AsyncContext.complete()方法时,才会返回。

    客户端语言类型

    diablo默认实现了Java语言的客户端,希望以后能支持node,go,python等语言。由于diablo仅通过Http接口提供服务,不同语言只要遵循API接口,即可实现不同的语言版本。若读者有意实现,可参考该客户端规范,下图为客户端与服务端的交互过程:

    diablo实践建议

    服务器集群部署:对于一个高可用的服务器集群,建议可以nginx等代理服务器作转发,这样在服务器集群发生变化时,客户端应用也不用更新任何配置,如:

    应用环境区分:对于需要区分不同环境的应用,可以通过不同的应用名称作区分,如app_dev,app_test。但对于生产环境,都建议与其他环境隔离,独立部署。

    总结

    以上,则是有关分布式配置管理平台设计与实现的相关细节,也欢迎issue和fork项目diablo。



    [案例分析讲解]

       案例一、  为了更好的解决分布式环境下多台服务实例的配置统一管理问题,本文提出了一套完整的分布式配置管理解决方案。结合.net项目具体情况,实现了配置发布的统一化,对配置进行持久化管理并对外提供restful接口,在此基础上,基于ZooKeeper实现对配置更改的实时推送。系统参考了百度的Disconf,实现和改进了部分功能,是Disconf的.Net精简版,功能有待进一步完善。

    这里主要使用到disconf分布式配置管理平台 支持window和linux下面是大家window环境步骤和一些操作总结。

      所需环境:Windows、nginx1.8.1、redis3.0.5、zookeeper3.4.6、mysql5.7 、python2.7.11、Git-2.6.4-64-bit.exe

    之前一直采用properties文件管理配置信息,若是集群则每个机器上都要拷贝一份,每次修改也需要依次修改。一直在寻找统一修改,实时生效,方便修改,分环境分系统的配置管理,自己也在整理设计,若找不到合意的就准备自己写一个,可以根据自己需求慢慢改进。通过开源中国微博知道了360的配置管理,看了下没大搞明白,貌似管理不太方便,反正不是我想要的,后来知道了百度的disconf,淘宝也有一个配置管理。我先看了百度的disconf,这就是我想要的,所以没看淘宝那个配置管理。

      首先这是一个开源项目,托管在github上,地址: https://github.com/knightliao/disconf,官方的文档还是很丰富的,地址:https://github.com/knightliao/disconf/wiki 。建议先看官方文档,文档很实用,花不了多少时间,我这里仅就官方没说,但刚接触这个的人常见的部分问题说说自己的解决方案,下面是一张运行效果图。

      

      要看这个项目,需要的知识:java相关技术、前端、git、mysql、tomcat、redis、zookeper、nginx,后面几个简单度一下就能了解个大概。

    1. 安装git客户端、下载代码、导入eclipse、运行redis、zookeper、mysql就不说了。
    2. windows上运行sh脚本小知识。
      一看项目内容就知道,这应该只考虑了Linux环境开发,只提供了sh脚本,而很多人都是windows开发环境。其实安装git客户端后,windows下是可以运行sh脚本的。如下图就是git下的sh软件和运行效果,需要先按官方教程配置环境变量,我换系统了所以没配置,之前配置过。

    3. 能不安装nginx吗?
      这是我刚开始在官方讨论群提的问题,得到的答案是不能,提到了什么动静分离,于是百度了解了下,对nginx在这里扮演的角色有了一个了解,知道他做了什么,才能知道他是否必须。了解了之后,就会知道,这里应该有多中方式实现不安装nginx,我实现了一种如下图所示,其他方式可以百度springMVC关于静态文件的处理方式,第一张截图就是我在eclipse中用tomcat运行的结果。这个能方便开发,正式环境建议还是按官方设计的方式使用,nginx对静态文件的处理要比tomcat快不少。
    4. 看交流群讨论,记录如下几点,可以研究下,看怎么修改能解决问题,然后推送官方,也贡献自己的一份力。
      1) 貌似使用spring4时有问题。
      2) 有人建议添加配置优先级,先读取环境变量,再各种配置文件,都没有时提供默认配置。

      由于官方文档比较详细,这里基本没有提到disconf本身的用途,使用方式。建议到处问人前先仔细看看官方教程。

    案例二、

    1.系统设计

    1.1设计理念

          l  简单易用,用户体验良好

          l  支持配置(KV配置项+配置文件)的分布式化管理

          l  配置发布、更新统一化:用户统一在平台上进行发布、更新配置。

          l  配置更新自动化:用户在平台更新配置,使用该配置的系统会自动发现该情况,并应用新配置。

     

          系统结构图如下: 

     

     

          初始化时,业务流程图如下:

     

          配置更新时,业务流程图如下:

     

    1.2.功能介绍

          系统模块架构图如下:

     

    1.2.1Client

      配置管理模块:统一管理用户实例中本地配置文件和配置项

      下载模块:restful风格的下载配置文件和配置项

      watch模块:监控远程配置文件和配置项的变化

    1.2.2Web

    配置管理模块:支持配置模板(配置项或配置文件)的上传、下载、更新

    配置存储模块:管理所有配置的存储和读取,根据appName、version、environment来区分项目配置

    通知模块:当配置更新后,实时通知使用这些配置的所有实例

    权限控制:web用户的权限控制

    2.客户端应用

    2.1添加clientConfig配置节点

    在app.config或者web.config中的configSections节点下添加配置

    <section name=”clientConfig” type=”Disconf.Net.Client.ClientConfigSection,Disconf.Net.Client”/>

    然后在appSettings同级别的节点上添加clientConfig配置,示例如下

    复制代码
    <configSections>
    
     <section name=”clientConfig” type=”Disconf.Net.Client.ClientConfigSection,Disconf.Net.Client”/>
    
    </configSections>
    
    <appSettings file=”appSettings.config”/>
    
    <clientConfig configSource=”clientConfig.config”/>
    复制代码

     

    2.2clientConfig配置说明

    具体示例如下:

    复制代码
    <clientConfig webApiHost=”http://192.168.1.100:8088/” enableRemote=”true”>
    
    <clientInfo appName=”consoletest” environment=”Dev” version=”1.0.0.0” clientName=”Console_1”/>
    
    <updateStrategy fileIgnores="notdown.txt" itemIgnores="aa,bb,cc " startedSync="true" retryTimes="3" retryIntervalSeconds="10" />
    
    <preservation absolutePath="false" tmpRootDirectory="Tmp\Download\Configs" factRootDirectory="" tmpItemsLocalName="~items.xml" tmpFilesLocalName="~files.txt"/>
    
    </clientConfig>
    复制代码

     

     

    节点名称

    必配

    默认值

    节点描述

    webApiHost

     

    Y

     

    Rest服务器域名地址

    enableRemote

     

    N

    true

    是否启用远程配置,默认true,设为false的话表示不从远程服务器下载配置

    clientInfo

    appName

    Y

     

    客户端程序名称,注意大小写要与服务端一致

    environment

    Y

     

    当前客户端程序所处环境,注意大小写要与服务端一致

    version

    Y

     

    当前客户端程序版本,注意大小写要与服务端一致

    clientName

    N

     

    客户端标识,用于服务端查看已更新客户端,如果不设置则默认获取客户端电脑名称

    updateStrategy

    fileIgnores

    N

     

    要忽略更新的文件配置,以,分割,注意大小写要与服务端一致

    itemIgnores

    N

     

    要忽略更新的键值对配置,以,分割,注意大小写要与服务端一致

    startedSync

    N

    true

    启动时是否同步加载,默认同步

    retryTimes

    N

    3

    当获取失败时的重试次数

    retryIntervalSeconds

    N

    10

    每次重试时间间隔,单位秒

    preservation

    absolutePath

    N

    false

    是否绝对路径,默认false。当false时,表示默认以

    AppDomain.CurrentDomain.BaseDirectory为比较点,注意:该配置同时适用于TmpRootDirectory、

    FactRootDirectory,即要么都只能绝对路径,要么都只能相对路径

    tmpRootDirectory

    N

    Tmp/Download/Configs

    下载下来的配置临时保存文件夹根目录

    factRootDirectory

    N

    Configs

    配置文件实际所在的根目录

    tmpItemsLocalName

    N

    ~items.xml

    在临时目录下用于保存所有键值对的文件名,设置为空表示不保存,文件保存在TmpRootDirectory目录下,所以注意不要与

    实际配置文件名字冲突

     

    tmpFilesLocalName

    N

    ~files.txt

    在临时目录下用于保存所有文件配置名的文件名,设置为空表示不保存,文件保存在TmpRootDirectory目录下,所以注意不要与实际配置文件名字冲突

     

    2.3Rules

    除了配置外,还需要设置更新策略,客户端才能进行配置更新。目前,Rules设置仅支持编码的方式进行,Rule分两种:FileRule,ItemRule,下面分别进行描述:

    FileRule:用于设置如何更新文件类型配置,其包含以下方法

    方法名

    描述

    IFileRule MapTo(string refreshSectionName)

    注册Rule规则,设置默认的文件配置映射

    参数refreshSectionName表示更新回调时,

    ConfigurationManager.RefreshSection要刷新的节点名称,默认采用远程配置的configName

    IFileRule RefreshIgnores()

    不自动调用ConfigurationManager.RefreshSection方法更新配置

    IFileRule CallBack(Action action)

    当文件下载完成并且替换本地对应文件后回调,注意此处将采用委托链的方式,即多次调用均会被执行

    ItemRule:用于设置如何更新键值对类型配置,其包含以下方法

    方法名

    描述

    IItemRule MapTo(string propName)

    注册Rule规则,设置默认的属性映射参数

    propName表示要赋值的属性名,默认采用远程的configName

    IItemRule SetProperty<T>(T entity, string propName = null, Func<string, object> typeConvert = null)

    更新指定实体的属性值,按默认方式获取实例属性,注意此处多次调用均会被执行

    IItemRule SetProperty(object entity, PropertyInfo prop, Func<string, object> typeConvert = null)

    更新指定实体的属性值,注意此处多次调用均会被执行

    IItemRule SetStaticProperty<T>(string propName = null, Func<string, object> typeConvert = null)

    更新静态属性的值,按默认方式获取静态属性,注意此处多次调用均会被执行

    IItemRule SetStaticProperty(PropertyInfo prop, Func<string, object> typeConvert = null)

    更新静态属性的值,注意此处多次调用均会被执行

    IItemRule CallBack(Action<string> action)

    当值发生变更时如何进行回调,注意此处将采用委托链的方式,即多次调用均会被执行

    2.4ConfigManager

    该类为Client配置入口,通过Singleton提供唯一实例,除了提供Rules的配置入口外,还提供异常通知的事件

    要使Disconf.Net.Client工作,必须显示执行指定方法manager.Init(),而在init之前,还需设置Rule和Fault,可以通过ConfigManager.Instance来获取该类的实例对象,然后通过对应的Rule进行相关Rule设定,示例如下:

    复制代码
    //要更新的文件
    
    ConfigManager.Instance.FileRules.For("appSettings.config").CallBack(() => {
    
          Console.WriteLine("File changed notice twice");
    
    });
    
    
    //要更新的键值对
    
    ConfigManager.Instance.ItemRules.For("Dai").MapTo("Person").SetStaticProperty<Program>().CallBack(v =>{
    
    Console.WriteLine("Now item value:{0}", v);
    
    Console.WriteLine("Program.Person is {0} now", Program.Person);
    
        if (v.Length > 3)
    
        {
    
            throw new Exception("Too Long");
    
        }
    
    });
    
    
    //忽略更新到本地的键值对
    
    ConfigManager.Instance.ItemRules.For("Peng").CallBack(v =>{
    
    Console.WriteLine("Now item value:{0}", v);
    
    });
    
    
    //异常处理
    
    ConfigManager.Instance.Faulted+=Manager_Faulted;
    
    //Config初始化,包括ZooKeeper、scan等
    
    ConfigManager.Instance.Init();
    复制代码

     

    需要特别说明的是:

    1、File因为属于下载后覆盖指定位置文件的方式,所以对于Rule可以设置默认规则,如例子中的appSettings.config,其对应的就是config文件中的appSettings部分,此时如果不需要进行CallBack调用,且文件名称(去除后缀)部分与Section一致,那么这部分Rule设置可以忽略,程序会在初始化时自动进行默认设置,而对于Item,因为无法确认更新策略,所以如果不设置Rule,那么就算从服务端获取到了值,该部分也只能被忽略。

    2、对于异常部分,程序只是简单的通过Faulted事件来传递异常信息,该事件只有一个Exception类型的参数。

    3.web端应用

    配置步骤:

    1、  创建具体应用(项目)

    2、  创建应用的配置模板(1~n个配置,如appSetting.config、redisconfig.config、rabbitMQConfig.config等配置模板)

    3、  创建应用的环境(如:开发环境、测试环境、仿真环境等),修改相关的配置

    4、  启用对应的配置

    5、  至此,client端就可以获取应用环境对应的所有配置

    3.1登录

    登陆进入配置管理界面

    3.2应用

     

     

    【新建】:填写应用名称,应用描述保存完成新建,返回可返回应用管理首页。

    【初始化ZooKeeper】:第一次启动时Zookeeper初始化。

    【编辑】:与新建界面一致,可修改应用名称,应用描述,保存即返回应用管理首页。

    【编辑环境】:进入环境环境配置管理首页。

    【删除】:删除对应应用记录。

    3.3模板

    显示所有模板,操作环境配置前,需要先配置模板,根据模板对相应环境的配置进行操作。

     

    【新建】:新增模板,填写模板名称、描述、类型、默认值版本号等,如选择文件类型。可上传文件读取文件内容,版本号可以选择已经有的版本号,或者新建版本号。

    【编辑】:操作同新建模板,可对模板内容进行修改。

    【删除】:点击删除可删除对应模板记录,如该模板在环境中存在配置项,则该模板不允许删除,需删除对应该模板的配置项,才可以删除对应模板。

    3.4环境

    【新增环境】点击加号可以新增环境,填写环境名称,描述保存即可。

     

    【编辑环境】在对应环境上点击鼠标右键即可弹出编辑菜单,点击Edit即可编辑环境,可以修改名称内容等。

     

    【配置首页】:配置首页根据版本进行分类,默认显示头部第一个版本,点击其他版本可以进行切换,显示的配置项是模板默认配置项,点击启用即可个性化赋值,针对不同环境进行不同的赋值。编辑可编辑相应配置,禁用等同于删除配置。

     

    【启用配置】:名称默认值不能修改,可以点击使用默认值,直接赋值,也可以上传文件使用文件内容,保存即可。

    【编辑配置】:操作同启用配置,保存即可修改值。

    【禁用配置】:禁用等同于删除配置,删除对应模板配置项,可删除对应模板。

    3.5角色

    【角色首页】:

    Ø  角色首页展示角色列表,角色分为超级管理员和非超级管理员;

    Ø  超级管理员角色不展示;

    Ø  超级管理员可以看到所有非超级管理员角色,非超级管理员只可以看到当前角色用户创建的角色;

    Ø  可以新增角色,也可以对角色进行编辑,只有在创建用户时勾选是否为系统管理员才可以进行角色管理。

    【新建角色】:

    Ø  新建角色输入角色名称,可以勾选的权限为当前用户所拥有的权限;

    Ø  新建的角色作为该用户的下属角色,可分配给当前用户新建的用户;

    Ø  父级权限为新建应用所增加的权限,以后每增加一个环境,就相应的增加该应用下的该环境权限,除超级管理员外的角色需对应勾选该权限才能看到该应用或者该权限,保存角色即可。

    【编辑角色】:操作同新建角色,可以对该角色进行名称修改,权限修改。

    3.6用户

    管理用户首页,显示所有用户,可进行新建,编辑用户等操作。

     

    【新建用户】:填写姓名,用户名,密码,选择角色(拥有对应角色权限、且可以选择的角色为当前登陆用户新建的角色),选择是否为系统管理员(系统管理员拥有新建用户、新建角色权限),保存即可。

    【编辑用户】:操作同新建用户,保存即可修改。


    案例三、

    《分布式配置管理平台XXL-CONF》

    一、简介

    1.1 概述

    XXL-CONF 是一个分布式配置管理平台,其核心设计目标是“为分布式业务提供统一的配置管理服务”。现已开放源代码,开箱即用。

    1.2 特性

    • 1、简单易用: 上手非常简单, 只需要引入maven依赖和一行配置即可;
    • 2、在线管理: 提供配置管理中心, 支持在线管理配置信息;
    • 3、实时推送: 配置信息更新后, Zookeeper实时推送配置信息, 项目中配置数据会实时更新并生效, 不需要重启线上机器;
    • 4、高性能: 系统会对Zookeeper推送的配置信息, 在Encache中做本地缓存, 在接受推送更新或者缓存失效时会及时更新缓存数据, 因此业务中对配置数据的查询并不存在性能问题;
    • 5、配置备份: 配置数据首先会保存在Zookeeper中, 同时, 在MySQL中会对配置信息做备份, 保证配置数据的安全性;
    • 6、HA: 配置中心基于Zookeeper集群, 只要集群节点保证存活数量大于N/2+1, 就可保证服务稳定, 避免单点风险;
    • 7、分布式: 可方便的接入线上分布式部署的各个业务线, 统一管理配置信息;
    • 8、配置共享: 平台中的配置信息针对各个业务线是平等的, 各个业务线可以共享配置中心的配置信息, 当然也可以配置业务内专属配置信息;
    • 9、配置分组: 支持对配置进行分组管理, 每条配置将会生成全局唯一标示GroupKey,在client端使用时,需要通过该值匹配对应的配置信息;

    1.3 背景

    why not properties

    常规项目开发过程中, 通常会将配置信息位于在项目resource目录下的properties文件文件中, 配置信息通常包括有: jdbc地址配置、redis地址配置、活动开关、阈值配置、黑白名单……等等。使用properties维护配置信息将会导致以下几个问题:

    • 1、需要手动修改properties文件;
    • 2、需要重新编译打包;
    • 3、需要重启线上服务器 (项目集群时,更加令人崩溃) ;
    • 4、配置生效不及时: 因为流程复杂, 新的配置生效需要经历比较长的时间才可以生效;
    • 5、不同环境上线包不一致: 例如JDBC连接, 不同环境需要差异化配置;

    why XXL-CONF

    • 1、不需要 (手动修改properties文件) : 在配置管理中心提供的Web界面中, 定位到指定配置项, 输入新的配置的值, 点击更新按钮即可;
    • 2、不需要 (重新编译打包) : 配置更新后, 实时推送新配置信息至项目中, 不需要编译打包;
    • 3、不需要 (重启线上服务器) : 配置更新后, 实时推送新配置信息至项目中, 实时生效, 不需要重启线上机器; (在项目集群部署时, 将会节省大量的时间, 避免了集群机器一个一个的重启, 费时费力)
    • 4、配置生效 "非常及时" : 点击更新按钮, 新的配置信息将会即可推送到项目中, 瞬间生效, 非常及时。比如一些开关类型的配置, 配置变更后, 将会立刻推送至项目中并生效, 相对常规配置修改繁琐的流程, 及时性可谓天壤之别;
    • 5、不同环境 "同一个上线包" : 因为差异化的配置托管在配置中心, 因此一个上线包可以复用在生产、测试等各个运行环境, 提供能效;

    1.4 下载

    源码地址 (将会在两个git仓库同步发布最新代码)
    中央仓库地址 (最新Release版本)
    <dependency>
      <groupId>com.xuxueli</groupId>
      <artifactId>xxl-conf-core</artifactId>
      <version>1.3.0</version>
    </dependency>
    
    博客地址 (将会在两个博客同步更新文档)
    技术交流群 (仅作技术交流)
    • 群4:464762661 image
    • 群3:242151780 (群即将满,请加群4)
    • 群2:438249535 (群即将满,请加群4)
    • 群1:367260654 (群即将满,请加群4)

    1.5 环境

    • Maven3+
    • Jdk1.7+
    • Tomcat7+
    • Zookeeper3.4+
    • Mysql5.5+

    二、快速入门

    2.1 初始化“数据库”

    请下载项目源码并解压,获取 "调度数据库初始化SQL脚本" 并执行即可。脚本位置如下:

    xxl-conf/db/xxl-conf.sql
    

    2.2 编译源码

    解压源码,按照maven格式将源码导入IDE, 使用maven进行编译即可,源码结构如下图所示:

    输入图片说明

    • xxl-conf-admin:配置管理中心
    • xxl-conf-core:公共依赖
    • xxl-conf-example: 接入XXl-CONF的Demo项目

    2.3 “配置管理中心” 项目配置

    项目:xxl-conf-admin
    作用:管理线上配置信息
    

    配置文件位置:

    xxl-conf/xxl-conf-admin/src/main/resources/xxl-config-admin.properties
    

    配置项目说明:

    # xxl-conf, zk address  (配置中心zookeeper集群地址,如有多个地址用逗号分隔)
    xxl.conf.zkserver=127.0.0.1:2181
    
    # xxl-conf, jdbc    (配置中心mysql地址)
    xxl.conf.jdbc.driverClass=com.mysql.jdbc.Driver
    xxl.conf.jdbc.url=jdbc:mysql://localhost:3306/xxl-conf?Unicode=true&amp;characterEncoding=UTF-8
    xxl.conf.jdbc.username=root
    xxl.conf.jdbc.password=root_pwd
    
    # xxl-conf, admin login (管理中心登录账号密码)
    xxl.conf.login.username=admin
    xxl.conf.login.password=123456
    

    2.4 “接入XXL-CONF的Demo项目” 项目配置

    项目:xxl-conf-example
    作用:供用户参考学习如何接入XXL-CONF
    
    A、引入maven依赖
    <!-- xxl-conf-client -->
    <dependency>
        <groupId>com.xuxueli</groupId>
        <artifactId>xxl-conf-core</artifactId>
        <version>${xxl.conf.version}</version>
    </dependency>
    
    B、配置 “XXL-CONF配置解析器”

    可参考配置文件:

    /xxl-conf/xxl-conf-example/src/main/resources/spring/applicationcontext-xxl-conf.xml
    

    配置项说明

    <!-- XXL-CONF配置解析器 -->
    <bean id="xxlConfPropertyPlaceholderConfigurer" class="com.xxl.conf.core.spring.XxlConfPropertyPlaceholderConfigurer" />
    
    C、设置 "xxl-conf.properties"

    可参考配置文件:

    /xxl-conf/xxl-conf-example/src/main/resources/xxl-conf.properties
    

    配置项说明

    # xxl-conf, zk address  (配置中心zookeeper集群地址,如有多个地址用逗号分隔)
    xxl.conf.zkserver=127.0.0.1:2181
    

    该配置文件,除了支持配置ZK地址,还可以配置一些本地配置。 XXL-CONF 加载配置时会优先加载 "xxl-conf.properties" 中的配置, 然后才会加载ZK中的配置。可以将一些希望存放本地的配置存放在该文件。

    2.5 新增配置分组

    输入图片说明

    每个配置分组对应一个唯一的GroupName,作为该分组下配置的统一前缀。在“分组管理”栏目可以创建并管理配置分组信息,系统已经提供一个默认分组.

    2.6 新增配置信息

    登录"配置管理中心"

    输入图片说明

    进入"配置管理界面",点击"新增配置"按钮

    输入图片说明

    在弹出界面,填写配置信息

    输入图片说明

    至此, 一条配置信息已经添加完成.

    通过client端,可以实时获取配置信息, 通过本地已经加载过得配置将会接受Zookeeper的更新推送, 如下如日志:

    输入图片说明

    2.7 项目中使用XXL-CONF

    项目: xxl-conf-example:   (可以参考 com.xxl.conf.example.controller.IndexController.index() )
    作用: 接入XXl-CONF的Demo项目
    
    • 方式1: XML文件中的占位符方式

      <bean id="configuration" class="com.xxl.conf.example.core.constant.Configuration">
          <property name="paramByXml" value="${default.key01}" />
      </bean>
      

      特点:

      • 上面配置说明: 在项目启动时, Configuration的paramByXml属性, 会根据配置的占位符${default.key01}, 去XXL-CONF中匹配KEY=key01的配置信息, 赋值给paramByXml;
      • 目前, 该方式配置信息, 只会在项目启动时从XXL-CONF中加载一次, 项目启动后该值不会变更。 例如配置数据连接信息, 如果XXL-CONF平台中连接地址配置改边, 需要重启后才生效;
      • 该方式, 底层本质上是通过 "方式2: API方式" 实现的。
    • 方式2: API方式

      String paramByClient = XxlConfClient.get("default.key02", null);
      

      特点:

      • 上面代码说明: 会获取XXL-CONF平台中KEY=default.key02的配置信息, 如果不存在值使用传递的默认值;
      • 因为Zookeeper会实时推送配置更新到客户端, 因此该方法放回的值可以XXL-CONF平台中的值保持实时一致;
      • XXL-CONF会对Zookeeper推送的配置信息做本地缓存, 该方法查询的是缓存的配置信息, 因此该方法并不会产生性能问题, 使用时不需要考虑性能问题;

    三、总体设计

    3.1 架构图

    输入图片说明

    3.2 "配置项" 设计

    系统配置信息以K/V的形式存在, "配置项" 属性如下:

    • 分组: "配置项" 的分组, 便于配置分组管理;
    • KEY : "配置项" 的全局唯一标识, 对应一条配置信息;
    • VALUE : "配置项" 中保存的数据信息, 仅仅支持String字符串格式;
    • 描述 : 配置项的描述信息;

    每条配置,将会生成全局唯一标示GroupKey,在client端使用时,需要通过该值匹配对应的配置信息;

    3.3 "配置中心" 设计

    输入图片说明

    • 1、ZK设计: 系统在ZK集群中占用一个根目录 "/xxl-conf", 每新增一条配置项, 将会在该目录下新增一个子节点。结构如下图, 当配置变更时将会触发ZK节点的变更, 将会触发对应类型的ZK广播。
    • 2、数据库备份配置信息: 配置信息在ZK中的新增、变更等操作, 将会同步备份到Mysql中, 进一步保证数据的安全性;
    • 3、配置推送: 配置推送功能在ZK的Watch机制实现。Client在加载一条配置信息时将会Watch该配置对应的ZK节点, 因此, 当对该配置项进行配置更新等操作时, 将会触发ZK的NodeDataChanged广播, Client竟会立刻得到通知并刷新本地缓存中的配置信息;

    ZK之watcher普及(来源官方文档,以及网络博客)

    1、可以注册watcher的方法:getData、exists、getChildren。
    2、可以触发watcher的方法:create、delete、setData。连接断开的情况下触发的watcher会丢失。
    3、一个Watcher实例是一个回调函数,被回调一次后就被移除了。如果还需要关注数据的变化,需要再次注册watcher。
    4、New ZooKeeper时注册的watcher叫default watcher,它不是一次性的,只对client的连接状态变化作出反应。(推荐ZK初始化时, 主动Watcher如exists)
    5、实现永久监听: 由于zookeeper是一次性监听,所以我们必须在wather的process方法里面再设置监听。
    6、getChildren("/path")监视/path的子节点,如果(/path)自己删了,也会触发NodeDeleted事件。
    
    《操作--事件》 event For “/path” event For “/path/child”
    create(“/path”) EventType.NodeCreated
    delete(“/path”) EventType.NodeDeleted
    setData(“/path”) EventType.NodeDataChanged
    create(“/path/child”) EventType.NodeChildrenChanged(getChild) EventType.NodeCreated
    delete(“/path/child”) EventType.NodeChildrenChanged(getChild) EventType.NodeDeleted
    setData(“/path/child”) EventType.NodeDataChanged

    《事件--Watch方式》 | Default Watcher | exists(“/path”) | getData(“/path”) | getChildren(“/path”) --- | --- | --- | --- EventType.None | 触发 | 触发 | 触发 | 触发 EventType.NodeCreated | | 触发 | 触发 |
    EventType.NodeDeleted | | 触发 | 触发 | EventType.NodeDataChanged | | 触发 | 触发 | EventType.NodeChildrenChanged | | | | 触发

    ZooKeeper的一个性能测试

    测试数据来自阿里中间件团队

    ZK集群情况: 3台ZooKeeper服务器。8核64位jdk1.6;log和snapshot放在不同磁盘;

    • 场景一: pub创建NODE,随后删除

      • 操作: 同一个目录下,先create EPHEMERAL node,再delete;create和delete各计一次更新。没有订阅。一个进程开多个连接,每个连接绑定一个线程,在多个path下做上述操作;不同的连接操作的path不同
      • 结果数据: "dataSize(字节)-TPS-响应时间(ms)" 统计结果为: 255-14723-82, 1024-7677-280, 4096-2037-1585;
    • 场景二: pub创建NODE, sub订阅并获取数据

      • 操作: 一个进程开多个连接,每连接一个线程,每个连接在多个path下做下述操作;不同的连接操作的path不同。每个path有3个订阅者连接,一个修改者连接。先全部订阅好。然后每个修改者在自己的每个path下创建一个EPHEMERAL node,不删除;创建前记录时间,订阅者收到event后记录时间(eventStat);重新get到数据后再记录时间(dataStat)。共1000个pub连接,3000个sub连接,20W条数据。收到通知后再去读取数据,五台4核client机器。
      • 结果汇总: getAfterNotify=false(只收事件,受到通知后不去读取数据);五台4核client机器
      • 结果数据: "dataSize(字节)-TPS-响应时间(ms)" 统计结果为: 255-1W+-256ms, 1024-1W+-256, 2048-1W+-270, 4096-8000+-520;
    • 场景三: pub创建NODE,随后设置数据

      • 一个进程开多个连接,每连接一个线程,每个连接在多个path下做下述操作;不同的连接操作的path不同。每个path有一个修改者连接,没有订阅者。每个修改者在自己的每个path下设置数据。
      • 结果汇总: getAfterNotify=false(只收事件,受到通知后不去读取数据);五台4核client机器
      • 结果数据: "dataSize(字节)-TPS-响应时间(ms)" 统计结果为: 255-14723-82, 1024-7677-280, 4096-2037-1585 ;

    总结: 由于一致性协议带来的额外网络交互,消息开销,以及本地log的IO开销,再加上ZK本身每1000条批量处理1次的优化策略,写入的平均响应时间总会在50-60ms之上。但是整体的TPS还是可观的。单个写入数据的体积越大,响应时间越长,TPS越低,这也是普遍规律了。压测过程中log文件对磁盘的消耗很大。实际运行中应该使用自动脚本定时删除历史log和snapshot文件。

    3.4 "配置管理中心" 设计

    "配置管理中心" 是 "配置中心" 的上层封装, 提供Web界面供用户对配置信息进行配置查询、配置新增、配置更新和配置删除等操作;

    3.5 "客户端" 设计

    输入图片说明

    API方式加载配置: 客户端主要分为三层:

    • ZK-Client : 第一层为ZK远程客户端的封装, 当业务方项目初始化某一个用到的配置项时, 将会触发ZK-Client对该配置对应节点的Watch, 因此当该节点变动时将会监听到ZK的类似NodeDataChanged的广播, 可以实时获取最新配置信息;
    • Ehcache : 第二层为客户端本地缓存, 可以大大提高系统的并发能力, 当配置初始化或者接受到ZK-Client的配置变更时, 将会把配置信息缓存只Encache中, 业务中针对配置的查询都是读缓存方式实现, 降低对ZK集群的压力;
    • Client-API : 第三层为暴露给业务方使用API, 简单易用, 一行代码获取配置信息, 同时可保证API获取到的配置信息是实时最新的配置信息;

    (API方式加载配置, 因为底层做了配置本地缓存, 因此可以放心应用在业务代码中, 不必担心并发压力。完整的支持配置实时推送更新)

    输入图片说明

    Bean方式加载配置:

    系统会在Spring容器中追加一个"PropertyPlaceholderConfigurer"属性解析器, 内部通过自定义的"StringValueResolver"解析器解析配置占位符 "${...}", 匹配到的配置信息将调用"XXL-CFONF"的API客户端加载最新配置信息进行Bean对象的属性赋值,最终完成实例化过程。

    (Bean方式加载配置,仅仅在实例化时加载一次; 考虑都实例化后的对象通常为持久化对象, 如数据库连接池对象, 不建议配置的太灵活, 因此Bean类型配置更新需要重启机器)

    四、历史版本

    4.1 版本1.1.0新特性

    • 1、简单易用: 上手非常简单, 只需要引入maven依赖和一行配置即可;
    • 2、在线管理: 提供配置管理中心, 支持在线管理配置信息;
    • 3、实时推送: 配置信息更新后, Zookeeper实时推送配置信息, 项目中配置数据会实时更新并生效, 不需要重启线上机器;
    • 4、高性能: 系统会对Zookeeper推送的配置信息, 在Encache中做本地缓存, 在接受推送更新或者缓存失效时会及时更新缓存数据, 因此业务中对配置数据的查询并不存在性能问题;
    • 5、配置备份: 配置数据首先会保存在Zookeeper中, 同时, 在MySQL中会对配置信息做备份, 保证配置数据的安全性;
    • 6、HA: 配置中心基于Zookeeper集群, 只要集群节点保证存活数量大于N/2+1, 就可保证服务稳定, 避免单点风险;
    • 7、分布式: 可方便的接入线上分布式部署的各个业务线, 统一管理配置信息;
    • 8、配置共享: 平台中的配置信息针对各个业务线是平等的, 各个业务线可以共享配置中心的配置信息, 当然也可以配置业务内专属配置信息;

    4.2 版本1.2.0新特性

    • 1、配置分组: 支持对配置进行分组管理, 每条配置将会生成全局唯一标示GroupKey,在client端使用时,需要通过该值匹配对应的配置信息;

    4.3 版本1.3.0新特性

    • 1、支持在线维护配置分组;
    • 2、项目groupId从com.xxl迁移至com.xuxueli,为推送maven中央仓库做准备;
    • 3、v1.3.0版本开始,推送公共依赖至中央仓库;

    4.4 版本1.3.1新特性(Coding)

    • zookeeper地址方式从磁盘迁移至项目内;

    TODO LIST

    • 1、权限管理:以分组为权限最小单元,只有分组的成员用户才有权限进行对应的配置操作;
    • 2、zookeeper客户端优化, 或将改用zkclient或者curator;
    • 3、local cache 备份到磁盘;zk异常且local properties未配置时,从磁盘上读取配置;

    五、其他

    5.1 报告问题

    XXL-CONF托管在Github上,如有问题可在 ISSUES 上提问,也可以加入上文技术交流群;

    5.2 接入登记

    更多接入公司,欢迎在github 登记


    案例四、

    DCMP 详细介绍

    DCMP是分布式配置管理平台。提供了一个etcd的管理界面,可通过界面修改配置信息,借助confd可实现配置文件的同步。

    安装 && 启动

    > go get github.com/silenceper/dcmp
    > ./service.sh

    界面预览

    访问 http://127.0.0.1:8000/


    Ø  超级管理员角色不展示;

    Ø  超级管理员可以看到所有非超级管理员角色,非超级管理员只可以看到当前角色用户创建的角色;

    Ø  可以新增角色,也可以对角色进行编辑,只有在创建用户时勾选是否为系统管理员才可以进行角色管理。

    【新建角色】:

    Ø  新建角色输入角色名称,可以勾选的权限为当前用户所拥有的权限;

    Ø  新建的角色作为该用户的下属角色,可分配给当前用户新建的用户;

    Ø  父级权限为新建应用所增加的权限,以后每增加一个环境,就相应的增加该应用下的该环境权限,除超级管理员外的角色需对应勾选该权限才能看到该应用或者该权限,保存角色即可。

    【编辑角色】:操作同新建角色,可以对该角色进行名称修改,权限修改。

    3.6用户

    管理用户首页,显示所有用户,可进行新建,编辑用户等操作。

     

    【新建用户】:填写姓名,用户名,密码,选择角色(拥有对应角色权限、且可以选择的角色为当前登陆用户新建的角色),选择是否为系统管理员(系统管理员拥有新建用户、新建角色权限),保存即可。

    【编辑用户】:操作同新建用户,保存即可修改。


    案例三、

    《分布式配置管理平台XXL-CONF》

    一、简介

    1.1 概述

    XXL-CONF 是一个分布式配置管理平台,其核心设计目标是“为分布式业务提供统一的配置管理服务”。现已开放源代码,开箱即用。

    1.2 特性

    • 1、简单易用: 上手非常简单, 只需要引入maven依赖和一行配置即可;
    • 2、在线管理: 提供配置管理中心, 支持在线管理配置信息;
    • 3、实时推送: 配置信息更新后, Zookeeper实时推送配置信息, 项目中配置数据会实时更新并生效, 不需要重启线上机器;
    • 4、高性能: 系统会对Zookeeper推送的配置信息, 在Encache中做本地缓存, 在接受推送更新或者缓存失效时会及时更新缓存数据, 因此业务中对配置数据的查询并不存在性能问题;
    • 5、配置备份: 配置数据首先会保存在Zookeeper中, 同时, 在MySQL中会对配置信息做备份, 保证配置数据的安全性;
    • 6、HA: 配置中心基于Zookeeper集群, 只要集群节点保证存活数量大于N/2+1, 就可保证服务稳定, 避免单点风险;
    • 7、分布式: 可方便的接入线上分布式部署的各个业务线, 统一管理配置信息;
    • 8、配置共享: 平台中的配置信息针对各个业务线是平等的, 各个业务线可以共享配置中心的配置信息, 当然也可以配置业务内专属配置信息;
    • 9、配置分组: 支持对配置进行分组管理, 每条配置将会生成全局唯一标示GroupKey,在client端使用时,需要通过该值匹配对应的配置信息;

    1.3 背景

    why not properties

    常规项目开发过程中, 通常会将配置信息位于在项目resource目录下的properties文件文件中, 配置信息通常包括有: jdbc地址配置、redis地址配置、活动开关、阈值配置、黑白名单……等等。使用properties维护配置信息将会导致以下几个问题:

    • 1、需要手动修改properties文件;
    • 2、需要重新编译打包;
    • 3、需要重启线上服务器 (项目集群时,更加令人崩溃) ;
    • 4、配置生效不及时: 因为流程复杂, 新的配置生效需要经历比较长的时间才可以生效;
    • 5、不同环境上线包不一致: 例如JDBC连接, 不同环境需要差异化配置;

    why XXL-CONF

    • 1、不需要 (手动修改properties文件) : 在配置管理中心提供的Web界面中, 定位到指定配置项, 输入新的配置的值, 点击更新按钮即可;
    • 2、不需要 (重新编译打包) : 配置更新后, 实时推送新配置信息至项目中, 不需要编译打包;
    • 3、不需要 (重启线上服务器) : 配置更新后, 实时推送新配置信息至项目中, 实时生效, 不需要重启线上机器; (在项目集群部署时, 将会节省大量的时间, 避免了集群机器一个一个的重启, 费时费力)
    • 4、配置生效 "非常及时" : 点击更新按钮, 新的配置信息将会即可推送到项目中, 瞬间生效, 非常及时。比如一些开关类型的配置, 配置变更后, 将会立刻推送至项目中并生效, 相对常规配置修改繁琐的流程, 及时性可谓天壤之别;
    • 5、不同环境 "同一个上线包" : 因为差异化的配置托管在配置中心, 因此一个上线包可以复用在生产、测试等各个运行环境, 提供能效;

    1.4 下载

    源码地址 (将会在两个git仓库同步发布最新代码)
    中央仓库地址 (最新Release版本)
    <dependency>
      <groupId>com.xuxueli</groupId>
      <artifactId>xxl-conf-core</artifactId>
      <version>1.3.0</version>
    </dependency>
    
    博客地址 (将会在两个博客同步更新文档)
    技术交流群 (仅作技术交流)
    • 群4:464762661 image
    • 群3:242151780 (群即将满,请加群4)
    • 群2:438249535 (群即将满,请加群4)
    • 群1:367260654 (群即将满,请加群4)

    1.5 环境

    • Maven3+
    • Jdk1.7+
    • Tomcat7+
    • Zookeeper3.4+
    • Mysql5.5+

    二、快速入门

    2.1 初始化“数据库”

    请下载项目源码并解压,获取 "调度数据库初始化SQL脚本" 并执行即可。脚本位置如下:

    xxl-conf/db/xxl-conf.sql
    

    2.2 编译源码

    解压源码,按照maven格式将源码导入IDE, 使用maven进行编译即可,源码结构如下图所示:

    输入图片说明

    • xxl-conf-admin:配置管理中心
    • xxl-conf-core:公共依赖
    • xxl-conf-example: 接入XXl-CONF的Demo项目

    2.3 “配置管理中心” 项目配置

    项目:xxl-conf-admin
    作用:管理线上配置信息
    

    配置文件位置:

    xxl-conf/xxl-conf-admin/src/main/resources/xxl-config-admin.properties
    

    配置项目说明:

    # xxl-conf, zk address  (配置中心zookeeper集群地址,如有多个地址用逗号分隔)
    xxl.conf.zkserver=127.0.0.1:2181
    
    # xxl-conf, jdbc    (配置中心mysql地址)
    xxl.conf.jdbc.driverClass=com.mysql.jdbc.Driver
    xxl.conf.jdbc.url=jdbc:mysql://localhost:3306/xxl-conf?Unicode=true&amp;characterEncoding=UTF-8
    xxl.conf.jdbc.username=root
    xxl.conf.jdbc.password=root_pwd
    
    # xxl-conf, admin login (管理中心登录账号密码)
    xxl.conf.login.username=admin
    xxl.conf.login.password=123456
    

    2.4 “接入XXL-CONF的Demo项目” 项目配置

    项目:xxl-conf-example
    作用:供用户参考学习如何接入XXL-CONF
    
    A、引入maven依赖
    <!-- xxl-conf-client -->
    <dependency>
        <groupId>com.xuxueli</groupId>
        <artifactId>xxl-conf-core</artifactId>
        <version>${xxl.conf.version}</version>
    </dependency>
    
    B、配置 “XXL-CONF配置解析器”

    可参考配置文件:

    /xxl-conf/xxl-conf-example/src/main/resources/spring/applicationcontext-xxl-conf.xml
    

    配置项说明

    <!-- XXL-CONF配置解析器 -->
    <bean id="xxlConfPropertyPlaceholderConfigurer" class="com.xxl.conf.core.spring.XxlConfPropertyPlaceholderConfigurer" />
    
    C、设置 "xxl-conf.properties"

    可参考配置文件:

    /xxl-conf/xxl-conf-example/src/main/resources/xxl-conf.properties
    

    配置项说明

    # xxl-conf, zk address  (配置中心zookeeper集群地址,如有多个地址用逗号分隔)
    xxl.conf.zkserver=127.0.0.1:2181
    

    该配置文件,除了支持配置ZK地址,还可以配置一些本地配置。 XXL-CONF 加载配置时会优先加载 "xxl-conf.properties" 中的配置, 然后才会加载ZK中的配置。可以将一些希望存放本地的配置存放在该文件。

    2.5 新增配置分组

    输入图片说明

    每个配置分组对应一个唯一的GroupName,作为该分组下配置的统一前缀。在“分组管理”栏目可以创建并管理配置分组信息,系统已经提供一个默认分组.

    2.6 新增配置信息

    登录"配置管理中心"

    输入图片说明

    进入"配置管理界面",点击"新增配置"按钮

    输入图片说明

    在弹出界面,填写配置信息

    输入图片说明

    至此, 一条配置信息已经添加完成.

    通过client端,可以实时获取配置信息, 通过本地已经加载过得配置将会接受Zookeeper的更新推送, 如下如日志:

    输入图片说明

    2.7 项目中使用XXL-CONF

    项目: xxl-conf-example:   (可以参考 com.xxl.conf.example.controller.IndexController.index() )
    作用: 接入XXl-CONF的Demo项目
    
    • 方式1: XML文件中的占位符方式

      <bean id="configuration" class="com.xxl.conf.example.core.constant.Configuration">
          <property name="paramByXml" value="${default.key01}" />
      </bean>
      

      特点:

      • 上面配置说明: 在项目启动时, Configuration的paramByXml属性, 会根据配置的占位符${default.key01}, 去XXL-CONF中匹配KEY=key01的配置信息, 赋值给paramByXml;
      • 目前, 该方式配置信息, 只会在项目启动时从XXL-CONF中加载一次, 项目启动后该值不会变更。 例如配置数据连接信息, 如果XXL-CONF平台中连接地址配置改边, 需要重启后才生效;
      • 该方式, 底层本质上是通过 "方式2: API方式" 实现的。
    • 方式2: API方式

      String paramByClient = XxlConfClient.get("default.key02", null);
      

      特点:

      • 上面代码说明: 会获取XXL-CONF平台中KEY=default.key02的配置信息, 如果不存在值使用传递的默认值;
      • 因为Zookeeper会实时推送配置更新到客户端, 因此该方法放回的值可以XXL-CONF平台中的值保持实时一致;
      • XXL-CONF会对Zookeeper推送的配置信息做本地缓存, 该方法查询的是缓存的配置信息, 因此该方法并不会产生性能问题, 使用时不需要考虑性能问题;

    三、总体设计

    3.1 架构图

    输入图片说明

    3.2 "配置项" 设计

    系统配置信息以K/V的形式存在, "配置项" 属性如下:

    • 分组: "配置项" 的分组, 便于配置分组管理;
    • KEY : "配置项" 的全局唯一标识, 对应一条配置信息;
    • VALUE : "配置项" 中保存的数据信息, 仅仅支持String字符串格式;
    • 描述 : 配置项的描述信息;

    每条配置,将会生成全局唯一标示GroupKey,在client端使用时,需要通过该值匹配对应的配置信息;

    3.3 "配置中心" 设计

    输入图片说明

    • 1、ZK设计: 系统在ZK集群中占用一个根目录 "/xxl-conf", 每新增一条配置项, 将会在该目录下新增一个子节点。结构如下图, 当配置变更时将会触发ZK节点的变更, 将会触发对应类型的ZK广播。
    • 2、数据库备份配置信息: 配置信息在ZK中的新增、变更等操作, 将会同步备份到Mysql中, 进一步保证数据的安全性;
    • 3、配置推送: 配置推送功能在ZK的Watch机制实现。Client在加载一条配置信息时将会Watch该配置对应的ZK节点, 因此, 当对该配置项进行配置更新等操作时, 将会触发ZK的NodeDataChanged广播, Client竟会立刻得到通知并刷新本地缓存中的配置信息;

    ZK之watcher普及(来源官方文档,以及网络博客)

    1、可以注册watcher的方法:getData、exists、getChildren。
    2、可以触发watcher的方法:create、delete、setData。连接断开的情况下触发的watcher会丢失。
    3、一个Watcher实例是一个回调函数,被回调一次后就被移除了。如果还需要关注数据的变化,需要再次注册watcher。
    4、New ZooKeeper时注册的watcher叫default watcher,它不是一次性的,只对client的连接状态变化作出反应。(推荐ZK初始化时, 主动Watcher如exists)
    5、实现永久监听: 由于zookeeper是一次性监听,所以我们必须在wather的process方法里面再设置监听。
    6、getChildren("/path")监视/path的子节点,如果(/path)自己删了,也会触发NodeDeleted事件。
    
    《操作--事件》 event For “/path” event For “/path/child”
    create(“/path”) EventType.NodeCreated
    delete(“/path”) EventType.NodeDeleted
    setData(“/path”) EventType.NodeDataChanged
    create(“/path/child”) EventType.NodeChildrenChanged(getChild) EventType.NodeCreated
    delete(“/path/child”) EventType.NodeChildrenChanged(getChild) EventType.NodeDeleted
    setData(“/path/child”) EventType.NodeDataChanged

    《事件--Watch方式》 | Default Watcher | exists(“/path”) | getData(“/path”) | getChildren(“/path”) --- | --- | --- | --- EventType.None | 触发 | 触发 | 触发 | 触发 EventType.NodeCreated | | 触发 | 触发 |
    EventType.NodeDeleted | | 触发 | 触发 | EventType.NodeDataChanged | | 触发 | 触发 | EventType.NodeChildrenChanged | | | | 触发

    ZooKeeper的一个性能测试

    测试数据来自阿里中间件团队

    ZK集群情况: 3台ZooKeeper服务器。8核64位jdk1.6;log和snapshot放在不同磁盘;

    • 场景一: pub创建NODE,随后删除

      • 操作: 同一个目录下,先create EPHEMERAL node,再delete;create和delete各计一次更新。没有订阅。一个进程开多个连接,每个连接绑定一个线程,在多个path下做上述操作;不同的连接操作的path不同
      • 结果数据: "dataSize(字节)-TPS-响应时间(ms)" 统计结果为: 255-14723-82, 1024-7677-280, 4096-2037-1585;
    • 场景二: pub创建NODE, sub订阅并获取数据

      • 操作: 一个进程开多个连接,每连接一个线程,每个连接在多个path下做下述操作;不同的连接操作的path不同。每个path有3个订阅者连接,一个修改者连接。先全部订阅好。然后每个修改者在自己的每个path下创建一个EPHEMERAL node,不删除;创建前记录时间,订阅者收到event后记录时间(eventStat);重新get到数据后再记录时间(dataStat)。共1000个pub连接,3000个sub连接,20W条数据。收到通知后再去读取数据,五台4核client机器。
      • 结果汇总: getAfterNotify=false(只收事件,受到通知后不去读取数据);五台4核client机器
      • 结果数据: "dataSize(字节)-TPS-响应时间(ms)" 统计结果为: 255-1W+-256ms, 1024-1W+-256, 2048-1W+-270, 4096-8000+-520;
    • 场景三: pub创建NODE,随后设置数据

      • 一个进程开多个连接,每连接一个线程,每个连接在多个path下做下述操作;不同的连接操作的path不同。每个path有一个修改者连接,没有订阅者。每个修改者在自己的每个path下设置数据。
      • 结果汇总: getAfterNotify=false(只收事件,受到通知后不去读取数据);五台4核client机器
      • 结果数据: "dataSize(字节)-TPS-响应时间(ms)" 统计结果为: 255-14723-82, 1024-7677-280, 4096-2037-1585 ;

    总结: 由于一致性协议带来的额外网络交互,消息开销,以及本地log的IO开销,再加上ZK本身每1000条批量处理1次的优化策略,写入的平均响应时间总会在50-60ms之上。但是整体的TPS还是可观的。单个写入数据的体积越大,响应时间越长,TPS越低,这也是普遍规律了。压测过程中log文件对磁盘的消耗很大。实际运行中应该使用自动脚本定时删除历史log和snapshot文件。

    3.4 "配置管理中心" 设计

    "配置管理中心" 是 "配置中心" 的上层封装, 提供Web界面供用户对配置信息进行配置查询、配置新增、配置更新和配置删除等操作;

    3.5 "客户端" 设计

    输入图片说明

    API方式加载配置: 客户端主要分为三层:

    • ZK-Client : 第一层为ZK远程客户端的封装, 当业务方项目初始化某一个用到的配置项时, 将会触发ZK-Client对该配置对应节点的Watch, 因此当该节点变动时将会监听到ZK的类似NodeDataChanged的广播, 可以实时获取最新配置信息;
    • Ehcache : 第二层为客户端本地缓存, 可以大大提高系统的并发能力, 当配置初始化或者接受到ZK-Client的配置变更时, 将会把配置信息缓存只Encache中, 业务中针对配置的查询都是读缓存方式实现, 降低对ZK集群的压力;
    • Client-API : 第三层为暴露给业务方使用API, 简单易用, 一行代码获取配置信息, 同时可保证API获取到的配置信息是实时最新的配置信息;

    (API方式加载配置, 因为底层做了配置本地缓存, 因此可以放心应用在业务代码中, 不必担心并发压力。完整的支持配置实时推送更新)

    输入图片说明

    Bean方式加载配置:

    系统会在Spring容器中追加一个"PropertyPlaceholderConfigurer"属性解析器, 内部通过自定义的"StringValueResolver"解析器解析配置占位符 "${...}", 匹配到的配置信息将调用"XXL-CFONF"的API客户端加载最新配置信息进行Bean对象的属性赋值,最终完成实例化过程。

    (Bean方式加载配置,仅仅在实例化时加载一次; 考虑都实例化后的对象通常为持久化对象, 如数据库连接池对象, 不建议配置的太灵活, 因此Bean类型配置更新需要重启机器)

    四、历史版本

    4.1 版本1.1.0新特性

    • 1、简单易用: 上手非常简单, 只需要引入maven依赖和一行配置即可;
    • 2、在线管理: 提供配置管理中心, 支持在线管理配置信息;
    • 3、实时推送: 配置信息更新后, Zookeeper实时推送配置信息, 项目中配置数据会实时更新并生效, 不需要重启线上机器;
    • 4、高性能: 系统会对Zookeeper推送的配置信息, 在Encache中做本地缓存, 在接受推送更新或者缓存失效时会及时更新缓存数据, 因此业务中对配置数据的查询并不存在性能问题;
    • 5、配置备份: 配置数据首先会保存在Zookeeper中, 同时, 在MySQL中会对配置信息做备份, 保证配置数据的安全性;
    • 6、HA: 配置中心基于Zookeeper集群, 只要集群节点保证存活数量大于N/2+1, 就可保证服务稳定, 避免单点风险;
    • 7、分布式: 可方便的接入线上分布式部署的各个业务线, 统一管理配置信息;
    • 8、配置共享: 平台中的配置信息针对各个业务线是平等的, 各个业务线可以共享配置中心的配置信息, 当然也可以配置业务内专属配置信息;

    4.2 版本1.2.0新特性

    • 1、配置分组: 支持对配置进行分组管理, 每条配置将会生成全局唯一标示GroupKey,在client端使用时,需要通过该值匹配对应的配置信息;

    4.3 版本1.3.0新特性

    • 1、支持在线维护配置分组;
    • 2、项目groupId从com.xxl迁移至com.xuxueli,为推送maven中央仓库做准备;
    • 3、v1.3.0版本开始,推送公共依赖至中央仓库;

    4.4 版本1.3.1新特性(Coding)

    • zookeeper地址方式从磁盘迁移至项目内;

    TODO LIST

    • 1、权限管理:以分组为权限最小单元,只有分组的成员用户才有权限进行对应的配置操作;
    • 2、zookeeper客户端优化, 或将改用zkclient或者curator;
    • 3、local cache 备份到磁盘;zk异常且local properties未配置时,从磁盘上读取配置;

    五、其他

    5.1 报告问题

    XXL-CONF托管在Github上,如有问题可在 ISSUES 上提问,也可以加入上文技术交流群;

    5.2 接入登记

    更多接入公司,欢迎在github 登记


    案例四、

    Ø  超级管理员角色不展示;

    Ø  超级管理员可以看到所有非超级管理员角色,非超级管理员只可以看到当前角色用户创建的角色;

    Ø  可以新增角色,也可以对角色进行编辑,只有在创建用户时勾选是否为系统管理员才可以进行角色管理。

    【新建角色】:

    Ø  新建角色输入角色名称,可以勾选的权限为当前用户所拥有的权限;

    Ø  新建的角色作为该用户的下属角色,可分配给当前用户新建的用户;

    Ø  父级权限为新建应用所增加的权限,以后每增加一个环境,就相应的增加该应用下的该环境权限,除超级管理员外的角色需对应勾选该权限才能看到该应用或者该权限,保存角色即可。

    【编辑角色】:操作同新建角色,可以对该角色进行名称修改,权限修改。

    3.6用户

    管理用户首页,显示所有用户,可进行新建,编辑用户等操作。

     

    【新建用户】:填写姓名,用户名,密码,选择角色(拥有对应角色权限、且可以选择的角色为当前登陆用户新建的角色),选择是否为系统管理员(系统管理员拥有新建用户、新建角色权限),保存即可。

    【编辑用户】:操作同新建用户,保存即可修改。


    案例三、

    《分布式配置管理平台XXL-CONF》

    一、简介

    1.1 概述

    XXL-CONF 是一个分布式配置管理平台,其核心设计目标是“为分布式业务提供统一的配置管理服务”。现已开放源代码,开箱即用。

    1.2 特性

    • 1、简单易用: 上手非常简单, 只需要引入maven依赖和一行配置即可;
    • 2、在线管理: 提供配置管理中心, 支持在线管理配置信息;
    • 3、实时推送: 配置信息更新后, Zookeeper实时推送配置信息, 项目中配置数据会实时更新并生效, 不需要重启线上机器;
    • 4、高性能: 系统会对Zookeeper推送的配置信息, 在Encache中做本地缓存, 在接受推送更新或者缓存失效时会及时更新缓存数据, 因此业务中对配置数据的查询并不存在性能问题;
    • 5、配置备份: 配置数据首先会保存在Zookeeper中, 同时, 在MySQL中会对配置信息做备份, 保证配置数据的安全性;
    • 6、HA: 配置中心基于Zookeeper集群, 只要集群节点保证存活数量大于N/2+1, 就可保证服务稳定, 避免单点风险;
    • 7、分布式: 可方便的接入线上分布式部署的各个业务线, 统一管理配置信息;
    • 8、配置共享: 平台中的配置信息针对各个业务线是平等的, 各个业务线可以共享配置中心的配置信息, 当然也可以配置业务内专属配置信息;
    • 9、配置分组: 支持对配置进行分组管理, 每条配置将会生成全局唯一标示GroupKey,在client端使用时,需要通过该值匹配对应的配置信息;

    1.3 背景

    why not properties

    常规项目开发过程中, 通常会将配置信息位于在项目resource目录下的properties文件文件中, 配置信息通常包括有: jdbc地址配置、redis地址配置、活动开关、阈值配置、黑白名单……等等。使用properties维护配置信息将会导致以下几个问题:

    • 1、需要手动修改properties文件;
    • 2、需要重新编译打包;
    • 3、需要重启线上服务器 (项目集群时,更加令人崩溃) ;
    • 4、配置生效不及时: 因为流程复杂, 新的配置生效需要经历比较长的时间才可以生效;
    • 5、不同环境上线包不一致: 例如JDBC连接, 不同环境需要差异化配置;

    why XXL-CONF

    • 1、不需要 (手动修改properties文件) : 在配置管理中心提供的Web界面中, 定位到指定配置项, 输入新的配置的值, 点击更新按钮即可;
    • 2、不需要 (重新编译打包) : 配置更新后, 实时推送新配置信息至项目中, 不需要编译打包;
    • 3、不需要 (重启线上服务器) : 配置更新后, 实时推送新配置信息至项目中, 实时生效, 不需要重启线上机器; (在项目集群部署时, 将会节省大量的时间, 避免了集群机器一个一个的重启, 费时费力)
    • 4、配置生效 "非常及时" : 点击更新按钮, 新的配置信息将会即可推送到项目中, 瞬间生效, 非常及时。比如一些开关类型的配置, 配置变更后, 将会立刻推送至项目中并生效, 相对常规配置修改繁琐的流程, 及时性可谓天壤之别;
    • 5、不同环境 "同一个上线包" : 因为差异化的配置托管在配置中心, 因此一个上线包可以复用在生产、测试等各个运行环境, 提供能效;

    1.4 下载

    源码地址 (将会在两个git仓库同步发布最新代码)
    中央仓库地址 (最新Release版本)
    <dependency>
      <groupId>com.xuxueli</groupId>
      <artifactId>xxl-conf-core</artifactId>
      <version>1.3.0</version>
    </dependency>
    
    博客地址 (将会在两个博客同步更新文档)
    技术交流群 (仅作技术交流)
    • 群4:464762661 image
    • 群3:242151780 (群即将满,请加群4)
    • 群2:438249535 (群即将满,请加群4)
    • 群1:367260654 (群即将满,请加群4)

    1.5 环境

    • Maven3+
    • Jdk1.7+
    • Tomcat7+
    • Zookeeper3.4+
    • Mysql5.5+

    二、快速入门

    2.1 初始化“数据库”

    请下载项目源码并解压,获取 "调度数据库初始化SQL脚本" 并执行即可。脚本位置如下:

    xxl-conf/db/xxl-conf.sql
    

    2.2 编译源码

    解压源码,按照maven格式将源码导入IDE, 使用maven进行编译即可,源码结构如下图所示:

    输入图片说明

    • xxl-conf-admin:配置管理中心
    • xxl-conf-core:公共依赖
    • xxl-conf-example: 接入XXl-CONF的Demo项目

    2.3 “配置管理中心” 项目配置

    项目:xxl-conf-admin
    作用:管理线上配置信息
    

    配置文件位置:

    xxl-conf/xxl-conf-admin/src/main/resources/xxl-config-admin.properties
    

    配置项目说明:

    # xxl-conf, zk address  (配置中心zookeeper集群地址,如有多个地址用逗号分隔)
    xxl.conf.zkserver=127.0.0.1:2181
    
    # xxl-conf, jdbc    (配置中心mysql地址)
    xxl.conf.jdbc.driverClass=com.mysql.jdbc.Driver
    xxl.conf.jdbc.url=jdbc:mysql://localhost:3306/xxl-conf?Unicode=true&amp;characterEncoding=UTF-8
    xxl.conf.jdbc.username=root
    xxl.conf.jdbc.password=root_pwd
    
    # xxl-conf, admin login (管理中心登录账号密码)
    xxl.conf.login.username=admin
    xxl.conf.login.password=123456
    

    2.4 “接入XXL-CONF的Demo项目” 项目配置

    项目:xxl-conf-example
    作用:供用户参考学习如何接入XXL-CONF
    
    A、引入maven依赖
    <!-- xxl-conf-client -->
    <dependency>
        <groupId>com.xuxueli</groupId>
        <artifactId>xxl-conf-core</artifactId>
        <version>${xxl.conf.version}</version>
    </dependency>
    
    B、配置 “XXL-CONF配置解析器”

    可参考配置文件:

    /xxl-conf/xxl-conf-example/src/main/resources/spring/applicationcontext-xxl-conf.xml
    

    配置项说明

    <!-- XXL-CONF配置解析器 -->
    <bean id="xxlConfPropertyPlaceholderConfigurer" class="com.xxl.conf.core.spring.XxlConfPropertyPlaceholderConfigurer" />
    
    C、设置 "xxl-conf.properties"

    可参考配置文件:

    /xxl-conf/xxl-conf-example/src/main/resources/xxl-conf.properties
    

    配置项说明

    # xxl-conf, zk address  (配置中心zookeeper集群地址,如有多个地址用逗号分隔)
    xxl.conf.zkserver=127.0.0.1:2181
    

    该配置文件,除了支持配置ZK地址,还可以配置一些本地配置。 XXL-CONF 加载配置时会优先加载 "xxl-conf.properties" 中的配置, 然后才会加载ZK中的配置。可以将一些希望存放本地的配置存放在该文件。

    2.5 新增配置分组

    输入图片说明

    每个配置分组对应一个唯一的GroupName,作为该分组下配置的统一前缀。在“分组管理”栏目可以创建并管理配置分组信息,系统已经提供一个默认分组.

    2.6 新增配置信息

    登录"配置管理中心"

    输入图片说明

    进入"配置管理界面",点击"新增配置"按钮

    输入图片说明

    在弹出界面,填写配置信息

    输入图片说明

    至此, 一条配置信息已经添加完成.

    通过client端,可以实时获取配置信息, 通过本地已经加载过得配置将会接受Zookeeper的更新推送, 如下如日志:

    输入图片说明

    2.7 项目中使用XXL-CONF

    项目: xxl-conf-example:   (可以参考 com.xxl.conf.example.controller.IndexController.index() )
    作用: 接入XXl-CONF的Demo项目
    
    • 方式1: XML文件中的占位符方式

      <bean id="configuration" class="com.xxl.conf.example.core.constant.Configuration">
          <property name="paramByXml" value="${default.key01}" />
      </bean>
      

      特点:

      • 上面配置说明: 在项目启动时, Configuration的paramByXml属性, 会根据配置的占位符${default.key01}, 去XXL-CONF中匹配KEY=key01的配置信息, 赋值给paramByXml;
      • 目前, 该方式配置信息, 只会在项目启动时从XXL-CONF中加载一次, 项目启动后该值不会变更。 例如配置数据连接信息, 如果XXL-CONF平台中连接地址配置改边, 需要重启后才生效;
      • 该方式, 底层本质上是通过 "方式2: API方式" 实现的。
    • 方式2: API方式

      String paramByClient = XxlConfClient.get("default.key02", null);
      

      特点:

      • 上面代码说明: 会获取XXL-CONF平台中KEY=default.key02的配置信息, 如果不存在值使用传递的默认值;
      • 因为Zookeeper会实时推送配置更新到客户端, 因此该方法放回的值可以XXL-CONF平台中的值保持实时一致;
      • XXL-CONF会对Zookeeper推送的配置信息做本地缓存, 该方法查询的是缓存的配置信息, 因此该方法并不会产生性能问题, 使用时不需要考虑性能问题;

    三、总体设计

    3.1 架构图

    输入图片说明

    3.2 "配置项" 设计

    系统配置信息以K/V的形式存在, "配置项" 属性如下:

    • 分组: "配置项" 的分组, 便于配置分组管理;
    • KEY : "配置项" 的全局唯一标识, 对应一条配置信息;
    • VALUE : "配置项" 中保存的数据信息, 仅仅支持String字符串格式;
    • 描述 : 配置项的描述信息;

    每条配置,将会生成全局唯一标示GroupKey,在client端使用时,需要通过该值匹配对应的配置信息;

    3.3 "配置中心" 设计

    输入图片说明

    • 1、ZK设计: 系统在ZK集群中占用一个根目录 "/xxl-conf", 每新增一条配置项, 将会在该目录下新增一个子节点。结构如下图, 当配置变更时将会触发ZK节点的变更, 将会触发对应类型的ZK广播。
    • 2、数据库备份配置信息: 配置信息在ZK中的新增、变更等操作, 将会同步备份到Mysql中, 进一步保证数据的安全性;
    • 3、配置推送: 配置推送功能在ZK的Watch机制实现。Client在加载一条配置信息时将会Watch该配置对应的ZK节点, 因此, 当对该配置项进行配置更新等操作时, 将会触发ZK的NodeDataChanged广播, Client竟会立刻得到通知并刷新本地缓存中的配置信息;

    ZK之watcher普及(来源官方文档,以及网络博客)

    1、可以注册watcher的方法:getData、exists、getChildren。
    2、可以触发watcher的方法:create、delete、setData。连接断开的情况下触发的watcher会丢失。
    3、一个Watcher实例是一个回调函数,被回调一次后就被移除了。如果还需要关注数据的变化,需要再次注册watcher。
    4、New ZooKeeper时注册的watcher叫default watcher,它不是一次性的,只对client的连接状态变化作出反应。(推荐ZK初始化时, 主动Watcher如exists)
    5、实现永久监听: 由于zookeeper是一次性监听,所以我们必须在wather的process方法里面再设置监听。
    6、getChildren("/path")监视/path的子节点,如果(/path)自己删了,也会触发NodeDeleted事件。
    
    《操作--事件》 event For “/path” event For “/path/child”
    create(“/path”) EventType.NodeCreated
    delete(“/path”) EventType.NodeDeleted
    setData(“/path”) EventType.NodeDataChanged
    create(“/path/child”) EventType.NodeChildrenChanged(getChild) EventType.NodeCreated
    delete(“/path/child”) EventType.NodeChildrenChanged(getChild) EventType.NodeDeleted
    setData(“/path/child”) EventType.NodeDataChanged

    《事件--Watch方式》 | Default Watcher | exists(“/path”) | getData(“/path”) | getChildren(“/path”) --- | --- | --- | --- EventType.None | 触发 | 触发 | 触发 | 触发 EventType.NodeCreated | | 触发 | 触发 |
    EventType.NodeDeleted | | 触发 | 触发 | EventType.NodeDataChanged | | 触发 | 触发 | EventType.NodeChildrenChanged | | | | 触发

    ZooKeeper的一个性能测试

    测试数据来自阿里中间件团队

    ZK集群情况: 3台ZooKeeper服务器。8核64位jdk1.6;log和snapshot放在不同磁盘;

    • 场景一: pub创建NODE,随后删除

      • 操作: 同一个目录下,先create EPHEMERAL node,再delete;create和delete各计一次更新。没有订阅。一个进程开多个连接,每个连接绑定一个线程,在多个path下做上述操作;不同的连接操作的path不同
      • 结果数据: "dataSize(字节)-TPS-响应时间(ms)" 统计结果为: 255-14723-82, 1024-7677-280, 4096-2037-1585;
    • 场景二: pub创建NODE, sub订阅并获取数据

      • 操作: 一个进程开多个连接,每连接一个线程,每个连接在多个path下做下述操作;不同的连接操作的path不同。每个path有3个订阅者连接,一个修改者连接。先全部订阅好。然后每个修改者在自己的每个path下创建一个EPHEMERAL node,不删除;创建前记录时间,订阅者收到event后记录时间(eventStat);重新get到数据后再记录时间(dataStat)。共1000个pub连接,3000个sub连接,20W条数据。收到通知后再去读取数据,五台4核client机器。
      • 结果汇总: getAfterNotify=false(只收事件,受到通知后不去读取数据);五台4核client机器
      • 结果数据: "dataSize(字节)-TPS-响应时间(ms)" 统计结果为: 255-1W+-256ms, 1024-1W+-256, 2048-1W+-270, 4096-8000+-520;
    • 场景三: pub创建NODE,随后设置数据

      • 一个进程开多个连接,每连接一个线程,每个连接在多个path下做下述操作;不同的连接操作的path不同。每个path有一个修改者连接,没有订阅者。每个修改者在自己的每个path下设置数据。
      • 结果汇总: getAfterNotify=false(只收事件,受到通知后不去读取数据);五台4核client机器
      • 结果数据: "dataSize(字节)-TPS-响应时间(ms)" 统计结果为: 255-14723-82, 1024-7677-280, 4096-2037-1585 ;

    总结: 由于一致性协议带来的额外网络交互,消息开销,以及本地log的IO开销,再加上ZK本身每1000条批量处理1次的优化策略,写入的平均响应时间总会在50-60ms之上。但是整体的TPS还是可观的。单个写入数据的体积越大,响应时间越长,TPS越低,这也是普遍规律了。压测过程中log文件对磁盘的消耗很大。实际运行中应该使用自动脚本定时删除历史log和snapshot文件。

    3.4 "配置管理中心" 设计

    "配置管理中心" 是 "配置中心" 的上层封装, 提供Web界面供用户对配置信息进行配置查询、配置新增、配置更新和配置删除等操作;

    3.5 "客户端" 设计

    输入图片说明

    API方式加载配置: 客户端主要分为三层:

    • ZK-Client : 第一层为ZK远程客户端的封装, 当业务方项目初始化某一个用到的配置项时, 将会触发ZK-Client对该配置对应节点的Watch, 因此当该节点变动时将会监听到ZK的类似NodeDataChanged的广播, 可以实时获取最新配置信息;
    • Ehcache : 第二层为客户端本地缓存, 可以大大提高系统的并发能力, 当配置初始化或者接受到ZK-Client的配置变更时, 将会把配置信息缓存只Encache中, 业务中针对配置的查询都是读缓存方式实现, 降低对ZK集群的压力;
    • Client-API : 第三层为暴露给业务方使用API, 简单易用, 一行代码获取配置信息, 同时可保证API获取到的配置信息是实时最新的配置信息;

    (API方式加载配置, 因为底层做了配置本地缓存, 因此可以放心应用在业务代码中, 不必担心并发压力。完整的支持配置实时推送更新)

    输入图片说明

    Bean方式加载配置:

    系统会在Spring容器中追加一个"PropertyPlaceholderConfigurer"属性解析器, 内部通过自定义的"StringValueResolver"解析器解析配置占位符 "${...}", 匹配到的配置信息将调用"XXL-CFONF"的API客户端加载最新配置信息进行Bean对象的属性赋值,最终完成实例化过程。

    (Bean方式加载配置,仅仅在实例化时加载一次; 考虑都实例化后的对象通常为持久化对象, 如数据库连接池对象, 不建议配置的太灵活, 因此Bean类型配置更新需要重启机器)

    四、历史版本

    4.1 版本1.1.0新特性

    • 1、简单易用: 上手非常简单, 只需要引入maven依赖和一行配置即可;
    • 2、在线管理: 提供配置管理中心, 支持在线管理配置信息;
    • 3、实时推送: 配置信息更新后, Zookeeper实时推送配置信息, 项目中配置数据会实时更新并生效, 不需要重启线上机器;
    • 4、高性能: 系统会对Zookeeper推送的配置信息, 在Encache中做本地缓存, 在接受推送更新或者缓存失效时会及时更新缓存数据, 因此业务中对配置数据的查询并不存在性能问题;
    • 5、配置备份: 配置数据首先会保存在Zookeeper中, 同时, 在MySQL中会对配置信息做备份, 保证配置数据的安全性;
    • 6、HA: 配置中心基于Zookeeper集群, 只要集群节点保证存活数量大于N/2+1, 就可保证服务稳定, 避免单点风险;
    • 7、分布式: 可方便的接入线上分布式部署的各个业务线, 统一管理配置信息;
    • 8、配置共享: 平台中的配置信息针对各个业务线是平等的, 各个业务线可以共享配置中心的配置信息, 当然也可以配置业务内专属配置信息;

    4.2 版本1.2.0新特性

    • 1、配置分组: 支持对配置进行分组管理, 每条配置将会生成全局唯一标示GroupKey,在client端使用时,需要通过该值匹配对应的配置信息;

    4.3 版本1.3.0新特性

    • 1、支持在线维护配置分组;
    • 2、项目groupId从com.xxl迁移至com.xuxueli,为推送maven中央仓库做准备;
    • 3、v1.3.0版本开始,推送公共依赖至中央仓库;

    4.4 版本1.3.1新特性(Coding)

    • zookeeper地址方式从磁盘迁移至项目内;

    TODO LIST

    • 1、权限管理:以分组为权限最小单元,只有分组的成员用户才有权限进行对应的配置操作;
    • 2、zookeeper客户端优化, 或将改用zkclient或者curator;
    • 3、local cache 备份到磁盘;zk异常且local properties未配置时,从磁盘上读取配置;

    五、其他

    5.1 报告问题

    XXL-CONF托管在Github上,如有问题可在 ISSUES 上提问,也可以加入上文技术交流群;

    5.2 接入登记

    更多接入公司,欢迎在github 登记


    案例五、

    分布式配置管理平台Disconf

    摘要

    为了更好的解决分布式环境下多台服务实例的配置统一管理问题,本文提出了一套完整的分布式配置管理解决方案(简称为disconf[4],下同)。首先,实现了同构系统的配置发布统一化,提供了配置服务server,该服务可以对配置进行持久化管理并对外提供restful接口,在此基础上,基于zookeeper实现对配置更改的实时推送,并且,提供了稳定有效的容灾方案,以及用户体验良好的编程模型和WEB用户管理界面。其次,实现了异构系统的配置包管理,提出基于zookeeper的全局分布式一致性锁来实现主备统一部署、系统异常时的主备自主切换。通过在百度内部以及外部等多个产品线的实践结果表明,本解决方案是有效且稳定的。

    技术背景

    在一个分布式环境中,同类型的服务往往会部署很多实例。这些实例使用了一些配置,为了更好地维护这些配置就产生了配置管理服务。通过这个服务可以轻松地管理成千上百个服务实例的配置问题。

    王阿晶提出了基于zooKeeper的配置信息存储方案的设计与实现[1], 它将所有配置存储在zookeeper上,这会导致配置的管理不那么方便,而且他们没有相关的源码实现。淘宝的diamond[2]是淘宝内部使用的一个管理持久配置的系统,它具有完整的开源源码实现,它的特点是简单、可靠、易用,淘宝内部绝大多数系统的配置都采用diamond来进行统一管理。他将所有配置文件里的配置打散化进行存储,只支持KV结构,并且配置更新的推送是非实时的。百度内部的BJF配置中心服务[3]采用了类似淘宝diamond的实现,也是配置打散化、只支持KV和非实时推送。

    同构系统是市场的主流,特别地,在业界大量使用部署虚拟化(如JPAAS系统,SAE,BAE)的情况下,同一个系统使用同一个部署包的情景会越来越多。但是,异构系统也有一定的存在意义,譬如,对于“拉模式”的多个下游实例,同一时间点只能只有一个下游实例在运行。在这种情景下,就存在多台实例机器有“主备机”模式的问题。目前国内并没有很明显的解决方案来统一解决此问题。

    功能特点与设计理念

    disconf是一套完整的基于zookeeper的分布式配置统一解决方案。

    它的功能特点是

    • 支持配置(配置项+配置文件)的分布式化管理
      • 配置发布统一化
      • 配置发布、更新统一化(云端存储、发布):配置存储在云端系统,用户统一在平台上进行发布、更新配置。
      • 配置更新自动化:用户在平台更新配置,使用该配置的系统会自动发现该情况,并应用新配置。特殊地,如果用户为此配置定义了回调函数类,则此函数类会被自动调用。
    • 配置异构系统管理
      • 异构包部署统一化:这里的异构系统是指一个系统部署多个实例时,由于配置不同,从而需要多个部署包(jar或war)的情况(下同)。使用Disconf后,异构系统的部署只需要一个部署包,不同实例的配置会自动分配。特别地,在业界大量使用部署虚拟化(如JPAAS系统,SAE,BAE)的情况下,同一个系统使用同一个部署包的情景会越来越多,Disconf可以很自然地与他天然契合。 异构主备自动切换:如果一个异构系统存在主备机,主机发生挂机时,备机可以自动获取主机配置从而变成主机。
      • 异构主备机Context共享工具:异构系统下,主备机切换时可能需要共享Context。可以使用Context共享工具来共享主备的Context。
    • 注解式编程,极简的使用方式:我们追求的是极简的、用户编程体验良好的编程方式。通过简单的标注+极简单的代码撰写,即可完成复杂的配置分布式化。
    • 需要Spring编程环境

    它的设计理念是:

    • 简单,用户体验良好:
      • 摒弃了打散化配置的管理方式[2,3],仍旧采用基于配置文件的编程方式,这和程序员以前的编程习惯(配置都是放在配置文件里)一致。特别的,为了支持较为小众的打散化配置功能,还特别支持了配置项。
      • 采用了基于XML无代码侵入编程方式:只需要几行XML配置,即可实现配置文件发布更新统一化、自动化。
      • 采用了基于注解式的弱代码侵入编程方式:通过编程规范,一个配置文件一个配置类,代码结构简单易懂。XML几乎没有任何更改,与原springXML配置一样。真正编程时,几乎感觉不到配置已经分布式化
    • 可以托管任何类型的配置文件,这与[2,3]只能支持KV结构的功能有较大的改进。
    • 配置更新实时推送
    • 提供界面良好Web管理功能,可以非常方便的查看配置被哪些实例使用了。

    详细设计

    架构设计

    disconf服务集群模式

    disconf的模块架构图

    每个模块的简单介绍如下:

    • Disconf-core
      • 分布式通知模块:支持配置更新的实时化通知
      • 路径管理模块:统一管理内部配置路径URL
    • Disconf-client
      • 配置仓库容器模块:统一管理用户实例中本地配置文件和配置项的内存数据存储
      • 配置reload模块:监控本地配置文件的变动,并自动reload到指定bean
      • 扫描模块:支持扫描所有disconf注解的类和域
      • 下载模块:restful风格的下载配置文件和配置项
      • watch模块:监控远程配置文件和配置项的变化
      • 主备分配模块:主备竞争结束后,统一管理主备分配与主备监控控制
      • 主备竞争模块:支持分布式环境下的主备竞争
    • Disconf-web
      • 配置存储模块:管理所有配置的存储和读取
      • 配置管理模块:支持配置的上传、下载、更新
      • 通知模块:当配置更新后,实时通知使用这些配置的所有实例
      • 配置自检监控模块:自动定时校验实例本地配置与中心配置是否一致
      • 权限控制:web的简单权限控制
    • Disconf-tools
      • context共享模块:提供多实例间context的共享。

    流程设计

    运行流程详细介绍:

    与2.0版本的主要区别是支持了:主备分配功能/主备切换事件。

    • 启动事件A:以下按顺序发生。
      • A3:扫描静态注解类数据,并注入到配置仓库里。
      • A4+A2:根据仓库里的配置文件、配置项,去 disconf-web 平台里下载配置数据。这里会有主备竞争
      • A5:将下载得到的配置数据值注入到仓库里。
      • A6:根据仓库里的配置文件、配置项,去ZK上监控结点。
      • A7+A2:根据XML配置定义,到 disconf-web 平台里下载配置文件,放在仓库里,并监控ZK结点。这里会有主备竞争。
      • A8:A1-A6均是处理静态类数据。A7是处理动态类数据,包括:实例化配置的回调函数类;将配置的值注入到配置实体里。
    • 更新配置事件B:以下按顺序发生。
      • B1:管理员在 Disconf-web 平台上更新配置。
      • B2:Disconf-web 平台发送配置更新消息给ZK指定的结点。
      • B3:ZK通知 Disconf-cient 模块。
      • B4:与A4一样。
      • B5:与A5一样。
      • B6:基本与A4一样,唯一的区别是,这里还会将配置的新值注入到配置实体里。
    • 主备机切换事件C:以下按顺序发生。
      • C1:发生主机挂机事件。
      • C2:ZK通知所有被影响到的备机。
      • C4:与A2一样。
      • C5:与A4一样。
      • C6:与A5一样。
      • C7:与A6一样。

    模块实现

    disconf-web提供了前后端分离的web架构,具体可见:https://github.com/knightliao/disconf/tree/master/disconf-web

    本部分会重点介绍disconf-client的实现方式。

    注解式disconf实现

    本实现会涉及到 配置仓库容器模块、扫描模块、下载模块、watch模块,

    http://ww1.sinaimg.cn/bmiddle/60c9620fjw1eqj9zzgc7yj20b20pn41v.jpg

    使用AOP拦截的一个好处是可以比较轻松的实现配置控制,比如并发环境下的配置统一生效。关于这方面的讨论可以见这里

    特别地,本方式提供的编程模式非常简单,例如使用以下配置类的程序在使用它时,可以直接@Autowired进来进行调用,使用它时就和平常使用普通的JavaBean一样,但其实它已经分布式化了。配置更新时,配置类亦会自动更新。

    @Service
    @DisconfFile(filename = "redis.properties")
    public class JedisConfig {
    
        // 代表连接地址
        private String host;
    
        // 代表连接port
        private int port;
    
        /**
         * 地址, 分布式文件配置
         * 
         * @return
         */
        @DisconfFileItem(name = "redis.host", associateField = "host")
        public String getHost() {
            return host;
        }
    
        public void setHost(String host) {
            this.host = host;
        }
    
        /**
         * 端口, 分布式文件配置
         * 
         * @return
         */
        @DisconfFileItem(name = "redis.port", associateField = "port")
        public int getPort() {
            return port;
        }
    
        public void setPort(int port) {
            this.port = port;
        }
    }
    

    基于XML配置disconf实现

    本实现提供了无任何代码侵入方式的分布式配置。

    ReloadablePropertiesFactoryBean继承了Spring Properties文件的PropertiesFactoryBean类,管理所有当配置更新时要进行reload的配置文件。对于被管理的每一个配置文件,都会通过 配置仓库容器模块、扫描模块、下载模块、watch模块 进行配置获取至配置仓库里。

    ReloadingPropertyPlaceholderConfigurer继承了Spring Bean配置值控制类PropertyPlaceholderConfigurer。在第一次扫描spring bean里,disconf会记录配置文件的配置与哪些bean有关联。

    ReloadConfigurationMonitor是一个定时任务,定时check本地配置文件是否有更新。

    当配置中心的配置被更新时,配置文件会被下载至实例本地,ReloadConfigurationMonitor即会监控到此行为,并且通知 ReloadingPropertyPlaceholderConfigurer 对相关的bean类进行值更新。

    特别的,此种方式无法解决并发情况下配置统一生效的问题。

    主备分配实现

    在实现中,为每个配置提供主备选择的概念。用户实例在获取配置前需要先进行全局唯一性竞争才能得到配置值。在这里,我们采用基于zookeeper的全局唯一性锁来实现。

    Comparisons

      淘宝Diamond[2] Disconf 比较
    数据持久性 存储在mysql上 存储在mysql上 都持久化到数据库里,都易于管理
    推拉模型 拉模型,每隔15s拉一次全量数据 基于Zookeeper的推模型,实时推送 disconf基于分布式的Zookeeper来实时推送,在稳定性、实效性、易用性上均优于diamond
    配置读写 支持实例对配置读写。支持某台实例写配置数据,并广播到其它实例上 只支持实例对配置读。通过在disconf-web上更新配置到达到广播写到所有应用实例 从目前的应用场景来看,实例对配置的写需求不是那么明显。disconf支持的中心化广播方案可能会与人性思考更加相似。
    容灾 多级容灾模式,配置数据会dump在本地,避免中心服务挂机时无法使用 多级容灾模式,优先读取本地配置文件。 双方均支持在中心服务挂机时配置实例仍然可以使用
    配置数据模型 只支持KV结构的数据,非配置文件模式 支持传统的配置文件模式(配置文件),亦支持KV结构数据(配置项) 使用配置文件的编程方式可能与程序员的编程习惯更为相似,更易于接受和使用。
    编程模型 需要将配置文件拆成多个配置项,没有明显的编程模型 在使用配置文件的基础上,提供了注解式和基于XML的两种编程模型
    并发性 多条配置要同时生效时,无法解决并发同时生效的问题




    展开全文
  • IntelliJ IDEA详细配置和使用教程

    万次阅读 多人点赞 2017-12-26 11:16:26
    前言 正所谓工欲善其事必先利其器,对开发人员而言若想提高编码效率,一款高效的开发工具是必不可少的,相信看到该博客的朋友们都已经对IntelliJ IDEA有所了解了,所以此处就不对IntelliJ IDEA进行介绍,而是直接...
  • 第一篇 IDEA安装和基本配置使用

    万次阅读 多人点赞 2018-08-10 15:42:13
    最近在学习新技术时,发现许多教程都是...毕竟网上好多实用教程那叫一个啰嗦,还竟是没用的配置。因为是eclipse的老用户,因此先把一些eclipse干的事实现下,再有就是扩展一些IDEA好的功能。 一、IDEA的安装 官网...
  • Eclipse如何配置JDK

    万次阅读 2019-08-01 10:03:59
    第一步、首先需要大家安装好最新版的eclipse和jdk10,双击打开桌面eclipse图标。然后弹出窗口要我们选择工作空间路径,建议最好新建一个目录。 ...第一次打开会有些许慢,需要加载的东西多些。...
  • Java开发环境安装与配置(快速配置)

    千次阅读 多人点赞 2020-09-08 16:57:46
    知识的广度来自知识的深度,学习如果不成体系那是多可怕的一件事儿,希望...Java开发环境安装与配置JDK介绍Java与JDK的关系下载JDK安装JDK环境变量配置1. 配置位置2.JAVA_HOME3.CLASSPATH4.Path进入命令提示符测试1.
  • Maven配置教程

    万次阅读 多人点赞 2018-12-29 17:00:24
    Maven配置 Maven项目对象模型(POM),可以通过一小段描述信息来管理项目的构建,报告和文档的软件项目管理工具。 Maven 的配置却让一些初学者望而却步,这里我就把Maven的详细配置过程写下,希望能对你有所帮助...
  • 对于一般使用 STL 的用户而言,Allocator 是不可见的,如果需要对 STL 进行扩展,如编写自定义的容器,就需要调用 Allocator 的内存分配函数进行空间配置。 在C++中,一个对象的内存配置和释放一般都包含两个步骤,...
  • nginx配置https服务

    万次阅读 2019-06-22 14:27:14
    nginx配置https服务
  • 现在是2019.7.21,由于这阵一直在忙,今天我用最新的VSCode(Version 1.36.1) 和最新的Cpp插件(version 0.24.0)按照本文的...)标示出来,供大家参考进行配置。也可以参考官方文档:https://code.visualstudio.c...
  • springboot2多环境配置与部署

    万次阅读 2019-02-24 11:02:23
    使用jpa操作数据库,最终实现根据不同 的配置文件读取不同的数据源(数据库,缓存以及其他配置信息) 本文主要介绍的是多环境配置与部署,所以关于其他配置就简略的叙述 多环境配置配置application.properties(公共...
  • Tomcat配置本地文件【图片】服务器

    万次阅读 2019-12-22 14:21:21
    人工智能,零基础入门!...这个是tomcat用的最多的地方,分享一下,但是其实Tomcat还可以作为其他来用,这里要讲的就是用tomcat来配置本地的文件服务器。下面是步骤: 二、配置步骤 【1】下载一个to...
  • web.xml的加载过程配置详解

    万次阅读 多人点赞 2017-12-02 09:24:20
    首先会去读取web.xml配置文件里的配置,当这一步骤没有出错并且完成之后,项目才能正常的被启动起来。  启动WEB项目的时候,容器首先会去读取web.xml配置文件中的两个节点: 和 如图:    紧接着,容器...
  • phpwamp配置应该如何修改,Web服务器、php、mysql的具体的配置在哪里修改
  • 报错如上。(如果按照我的方法不能解决,请继续百度。。我目前只会该方法,也不打算继续深究) 原因: 公众号配置的回调域名与请求的域名不一致。...根据微信公众号配置修改,微信公众号的配置位置在:登录微信
  • intellij IDEA配置tomcat

    万次阅读 多人点赞 2019-09-12 10:11:09
    intellij IDEA配置tomcat 如果网上流传的方法(即方法2)不能配置成功,点击加号什么都没有的话,请看方法一配置方法。 解决问题:intlellij IDEA配置tomcat点击加号没东西。 方法一:手动添加tomcat插件然后再...
  • Spring Boot 属性配置和使用

    万次阅读 多人点赞 2017-04-23 13:19:36
    Spring Boot 属性配置和使用Spring Boot 允许通过外部配置让你在不同的环境使用同一应用程序的代码,简单说就是可以通过配置文件来注入属性或者修改默认的配置。Spring Boot 入门 请看:...
1 2 3 4 5 ... 20
收藏数 6,452,117
精华内容 2,580,846
关键字:

配置