HAProxy和Nginx 配置HTTP/2完整指南

push 发表了文章 0 个评论 4275 次浏览 2016-04-01 00:21 来自相关话题

基于最近对HTTP/2的争论和它的优势,是时候升级底层架构了。这篇文章将会介绍如何在安装和配置HAProxy和Ngnix(使用ssl终端)。为了简化流程,我建议你准备好使用Docker镜像。 ...查看全部
基于最近对HTTP/2的争论和它的优势,是时候升级底层架构了。这篇文章将会介绍如何在安装和配置HAProxy和Ngnix(使用ssl终端)。为了简化流程,我建议你准备好使用Docker镜像。
hanginx.png

如果你想跳过安装环节或你只对配置环节感兴趣,可以跳至文章后面配置部分。


我为什么需要关注HTTP/2?​


这里有一些介绍HTTP/2益处的文章-而且我鼓励你去读一读。下面我将重点介绍我认为比较重要的几点。
HTTP/2的主要优势:
    []使用二进制数据(不像HTTP/1.1一样使用明文)而且它使用了header数据压缩。不用再为header和cookie的大小而担心了。[/][]它是完全多元化的,为了提升并发性可以使用一个连接加载多种资源。你的网站性能在需要引入多种资源的时候会表现得更好,因为现在它们可以在一次TCP连接中全部加载,在非阻塞模式中。域名切分和资源级联变成了反面模式。简单来说:你的网站加载会更快。 [/][]它允许服务器提前推送请求到客户端的缓存(目前Ngnix不支持这个特性) [/][]它使用新的ALPN扩展,那将允许更快地加密连接。这个加密协议在初始化连接的阶段是可用的。[/]
今天我可以使用它吗?
use.png
是的,你可以。正如你所看到的在Can I Use上的服务那样,所有现代浏览器现在都支持HTTP/2,这里包括IE11和Edge。唯一的例外是移动端的Opera Mini和Android浏览器不支持它。此外,下面描述的配置都会确保客户端在不支持HTTP/2的情况下,退回到HTTP/1.1。这非常重要:你的网站应该为那些老版本浏览器或搜索引擎爬虫提供访问支持。

安装

我会在CentOS 7下安装,如果你使用其他Linux发布版本,你可以简单调整下代码。你需要做的:
    []站点能跑通SSL。如果你还没有虚拟证书的话,你需要使用虚拟证书(简单)。[/][]Ngnix 1.9.5 或更新版本( 简单 )。[/][]安装配置好OpenSSL的HAPorxy 1.6或更新版( 需要一些技巧 )。[/][]良好的HAPRoxy和Ngnix配置( 简单 )。[/][]确认你是否已经在使用HTTP/2,HTTP/2 and SPDY indicator 对Chrome友好。[/]

OpenSSL部分是需要一些技巧,因为大部分有OpwnSSL 1.0.1(或者更旧的版本)的Linux分支都不支持ALPN(应用层协议协商)。ALPN协议允许应用层去协商,这个协议将被用在连接中,而且这是基本的,如果我们要在相同的TCP端口支持HTTP/2和HTTP/1。除此之外,HTTP/2在HAProxy中只支持使用ALPN,所以它一定会在我们的列表里。
如果你对安装流程熟悉的话,请直接跳至配置部分.
1.获取 SSL 证书
你可以很便宜的从ssl2buy.com上买到信任证书,那里有许多靠谱发行机构的代售。我曾经在那里买了一堆证书而且我推荐他们的服务和客户支持。你可以从那里拿到低于20美元的AphaSSL证书。
 
如果你需要为HAProxy或Nginx生成虚拟证书,你可以使用下面的命令:
# Generate all keys and cefiticates in /etc/ssl/ directory
openssl genrsa -out /etc/ssl/dummy.key 2048
openssl req -new -key /etc/ssl/dummy.key -out /etc/ssl/dummy.csr -subj "/C=GB/L=London/O=Company Ltd/CN=haproxy"
openssl x509 -req -days 3650 -in /etc/ssl/dummy.csr -signkey /etc/ssl/dummy.key -out /etc/ssl/dummy.crt
# Concatenate certificate and key into .pem format, compatible with HAProxy
cat /etc/ssl/dummy.crt /etc/ssl/dummy.key > /etc/ssl/dummy.pem
我们需要在下一步的配置中使用生成的证书和秘钥。
 
2.Nginx 安装
在CentOS 7上安装Ngnix 1.9十分简单。唯一需要做的就是使用主线版YUM源,而不是稳定版。就像Nginx.org page上描述的那样,把yum源的配置放到/etc/yum.repos.d/nginx.repo位置然后执行yum install:搞定。
 
让我们创建一个Ngnix vhost.conf(虚拟主机配置文件)确保我们的Nginx在拥有HTTP/2的情况下正常工作。下面是一个简单的vhost配置:
    第一点:关键点是在listen 443 default_server ssl http2那一行。这就相当于你使用了HTTP/2。

    第二点:现在忽略第三行listen 81部分的配置 - 我们一会再回来看这部分。

    第三点:我使用使用标准的80/443端口在Docker镜像里跑这个样例,所以它们不会和我的host主机上的任何端口发生冲突。如果有需要,你可以把它调整至适用你的需要。

    第四点:使用在获取SSL证书那一步生成的dummy.crt和dummy.key。

好了,当你使用https://协议连接站点时,HTTP/2提示器会提示你站点正在运行HTTP/2协议。

恭喜你,你的Ngnix已经在运行HTTP/2了!
 
3、OpenSSL 和 HAProxy 安装
这一部分有点棘手。我们需要编译OpenSSL 1.0.2的源码(因为在yum中还没有可用的资源)并且在之后的HAProxy重编译中还会使用到它。
 
建立OpenSSL的工作,我们使用no-shared参数,并且HAProxy是通过静态方式连接到OpenSSL的。我遵照的是HAProxy官方的README.但可笑的是,我最终还是采用了其他的方式……并且要非常足智多谋。你会常常去读这些冗长且乏味的README文件吗?
在那之后,你应该已经编译通过HAProxy且安装好了。测试一下:
haproxy -vv

 4、配置
这是一个我们将用到完整的/etc/haproxy/haproxy.cfg(HAProxy配置):

最本质的部分在这:

这里我们定义了HTTPS前端接口在客户端请求HAProxy时监听443端口。

请求被后端的nodes-http2还是nodes-http处理取决于客户端是否支持HTTP2.注意我们决定SSL在HAProxy上使用这个配置,连接对后端服务器来说是被解密过的。我们的后端服务器可以被HAProxy用web服务器的域名访问(这就是运行过程中的Nginx,就像我们上面说的)。

在bind *:443 line with alpn h2,http/1.1一行我们建议为了方便客户端使用最好两种协议(HTTP/2 and HTTP/1.1)都支持。

这样的话浏览器即使不支持HTTP/2,也可以浏览我们的网站。

use_backend nodes-http2 if { ssl_fc_alpn -i h2 } 支持HTTP/2的客户端会被重定向到nodes-http2后端节点,剩下使用HTTP/1.1协议的将被nodes-http处理。如果你想让后端兼容还不支持HTTP/2的客户端,这件事十分重要的。

因此我们会有下面这一行:

server node1 web.server:81 check send-proxy

在这里,我们只讨论了HAProxy和HTTP/2协议。通常它连接web.server在81端口。我们还有更令人高兴的惊喜吗?
让我们用nginx下列虚拟主机配置(如上所述):

这一行:listen 81 default_server http2 proxy_protocol;

定义了服务器在端口81,会处理HTTP/2的请求。请注意,我们无法在服务器使用443端口进行SSL连接:SSL连接已经被HAProxy解密过了,所以现在我们有一个非加密连接。因此我们需要限制服务器的81端口只使用HTTP/2,不使用SSL。

题外话:小也有proxy_protocol关键词。在haproxy.cfg等效发送代理,在后端服务器配置。代理协议是独立出来的,这儿有篇文章很好的解释了原因。简而言之,它允许通过HAProxy后端服务器传送客户端的IP地址和端口号,这通常是非常理想的。

你可以使用上面的配置运行HAProxy:
HAProxy F / etc / HAProxy / haproxy.cfg
现在你应该能够连接到您的代理主机(例如https://localhost:443/),看到它正在运行HTTP / 2。如果你在Firefox的测试,检查网络请求头的标题,你会看到X-Firefox-Spdy: "h2"。


Docker images


如果你早已经会使用Docker,你可以用我们的MILLION12镜像。当Docker还是1.0版本的时候我们已经开始使用Docker很长时间了(MILLION12这是我们的仓库地址),而且我们已经构建了一堆有用的镜像。在这个例子里,我们将使用million12/haproxymillion12/nginx这两个镜像。里面的配置是我们讨论后的最终结果。

你可以运行整个栈通过使用docker-compose.yml文件。注意我们是通过haproxy容器里的web.server主机名连接Nignx的,那个域名就是当前haproxy.cfg使用的主机名。

连接https://haproxy:8443你就会看到屏幕显示出如下内容(注意蓝色的HTTP/2提示部分
nginx.png

haproxy.png

如果你想看看使用这些Docker镜像和上述配置的真实的产品工程,打开https://PrototypeBrewery.io 。Prototype Brewery是我们的产品,一个计划和构建交互式web项目的原型工具。打开看看吧,我们已经在使用HTTP/2了(别忘了注册)。


总结


正如你看到的,迁移到HTTP/2真的很简单,你今天就能做掉。 没有什么理由让你再等下去了,主流浏览器都已经支持它了。而且即使回迁到HTTP/1.1上你仍然是安全的。
分享阅读原文:http://m12.io/blog/http-2-with-haproxy-and-nginx-guide 
中文原文:http://www.oschina.net/translate/haproxy-nginx-http-2

nginx设置404页面不生效

being 回复了问题 2 人关注 1 个回复 5076 次浏览 2016-03-28 23:33 来自相关话题

gitlab + jenkins + salt持续集成

Something 发表了文章 2 个评论 10230 次浏览 2016-03-28 00:48 来自相关话题

目的:主要是了解持续集成方面的知识,分析gitlab的对于svn的优势,了解gitlab和jenkins的原理,再配合脚本和salt做一个简易的持续集成。 环境我使用三台机器做测试: 三台机器系统都是centos7,selinux di ...查看全部
目的:
主要是了解持续集成方面的知识,分析gitlab的对于svn的优势,了解gitlab和jenkins的原理,再配合脚本和salt做一个简易的持续集成。

环境
我使用三台机器做测试:
三台机器系统都是centos7,selinux disabled,iptables disabled
192.168.46.66 jenkins salt-master
192.168.46.82 gitlab
192.168.46.105 nginx salt-minion
该测试大致过程
通过提交代码到gitlab触发jenkins构建,jenkins调用脚本打包程序,通过salt分发包,并通过salt部署包


安装


1.1 安装jenkins
官网安装文档:https://wiki.jenkins-ci.org/display/JENKINS/Installing+Jenkins#InstallingJenkins-Unix%2FLinuxInstallation

我装的稳定版本:
1.1.1 添加Jenkins的源(repository):
wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat-stable/jenkins.repo
rpm --import https://jenkins-ci.org/redhat/jenkins-ci.org.key
yum install jenkins

1.1.2 安装/检查JDK
[root@localhost jenkins]# yum install java -y
[root@localhost jenkins]# java -version
openjdk version "1.8.0_71"
OpenJDK Runtime Environment (build 1.8.0_71-b15)
OpenJDK 64-Bit Server VM (build 25.71-b15, mixed mode)

1.1.3 安装Jenkins:
[root@localhost jenkins]#yum install jenkins

遇到错误
Delta RPMs disabled because /usr/bin/applydeltarpm not installed.
jenkins-1.653-1.1.noarch.rpm FAILED
http://pkg.jenkins-ci.org/redhat-stable/jenkins-1.653-1.1.noarch.rpm: [Errno 14] HTTP Error 404 - Not Found ] 0.0 B/s | 0 B --:--:-- ETA
Trying other mirror.
[root@localhost ~]# yum install -y deltarpm
继续安装jenkins
[root@localhost jenkins]#yum install jenkins

如果有防火墙
firewall-cmd --zone=public --add-port=8080/tcp --permanent
firewall-cmd --zone=public --add-service=http --permanent
firewall-cmd --reload
firewall-cmd --list-all

/usr/lib/jenkins/:jenkins安装目录,WAR包会放在这里。
/etc/sysconfig/jenkins:jenkins配置文件,“端口”,“JENKINS_HOME”等都可以在这里配置
/var/lib/jenkins/:默认的JENKINS_HOME。
/var/log/jenkins/jenkins.log:Jenkins日志文件。

启动Jenkins
[root@localhost jenkins]# /sbin/chkconfig jenkins on
[root@localhost jenkins]# systemctl start jenkins
1.1.4 安装salt-master
1.1.4.1 导入key
rpm --import https://repo.saltstack.com/yum/redhat/7/x86_64/latest/SALTSTACK-GPG-KEY.pub
1.1.4.2 编辑源/etc/yum.repos.d/saltstack.repo
[saltstack-repo]
name=SaltStack repo for RHEL/CentOS $releasever
baseurl=https://repo.saltstack.com/yum/redhat/$releasever/$basearch/latest
enabled=1
gpgcheck=1
gpgkey=https://repo.saltstack.com/yum/redhat/$releasever/$basearch/latest/SALTSTACK-GPG-KEY.pub
1.1.4.3 安装master
yum install salt-master -y
1.1.4.4 启动
systemctl enable salt-master
systemctl start salt-master
访问 192.168.46.66:8080 
jenkins.png

1.2 gitlab安装
官网安装文档:https://www.elastic.co/guide/index.html

1.2.1 安装相关软件
yum install curl policycoreutils openssh-server openssh-clients
systemctl enable sshd
systemctl start sshd
yum install postfix
systemctl enable postfix
systemctl start postfix
firewall-cmd --permanent --add-service=http
systemctl reload firewalld

1.2.2 下载安装gitlab包
curl -LJO https://packages.gitlab.com/gitlab/gitlab-ce/packages/el/7/gitlab-ce-8.5.5-ce.0.el7.x86_64.rpm/download
rpm -i gitlab-ce-8.5.5-ce.0.el7.x86_64.rpm

1.2.3 配置启动gitlab
gitlab-ctl reconfigure
gitlab-ctl --help 查看帮助
[root@localhost ~]# gitlab-ctl service-list
gitlab-workhorse*
logrotate*
nginx*
postgresql*
redis*
sidekiq*
unicorn*

1.2.4 登陆 192.168.46.82
Username: root
Password: 5iveL!fe
第一次登陆会让你修改密码
gitlab.png

1.3 nginx安装
1.3.1 安装nginx
yum install -y nginx
启动nginx
nginx
1.3.2 安装salt-minion
1.3.2.1 导入key
rpm --import https://repo.saltstack.com/yum/redhat/7/x86_64/latest/SALTSTACK-GPG-KEY.pub
1.3.2.2 编辑源/etc/yum.repos.d/saltstack.repo
[saltstack-repo]
name=SaltStack repo for RHEL/CentOS $releasever
baseurl=https://repo.saltstack.com/yum/redhat/$releasever/$basearch/latest
enabled=1
gpgcheck=1
gpgkey=https://repo.saltstack.com/yum/redhat/$releasever/$basearch/latest/SALTSTACK-GPG-KEY.pub
1.3.2.3 安装master
yum install salt-master -y
1.3.2.4 启动
systemctl enable salt-minion
systemctl start salt-minion


配置


2.1 配置46.105 nginx
2.1.1 配置nginx 
我的代码会发布到/usr/share/nginx/html/demo目录中
[root@nginx ~]# vim /etc/nginx/nginx.conf
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
root /usr/share/nginx/html/demo;
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
location / {
}
error_page 404 /404.html;
location = /40x.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
重启nginx
nginx -s reload
2.1.2 配置salt-minion
[root@nginx ~]# vim /etc/salt/minion
master: 192.168.46.66
id: nginx
重启minion
systemctl restart salt-minion

在46.66 salt-master 加入nginx
salt-key -a nginx
2.2 配置46.82 gitlab​
2.2.1 创建一个demo项目
gitlab2.png
2.2.2 在46.66上创建ssh key  
把公钥拷贝过来,私钥会拷到jenkins上,就可以无密码通信了
gitlab3.png
2.2.3 配置gitlab的web hooks
当有push时会触发jenkins构建
url为: http://192.168.46.66:8080/git/notifyCommit?url=git@192.168.46.82:root/demo.git
/git/notifyCommit?url=前为jenkins的地址和端口
/git/notifyCommit?url=后为git项目地址
2.2.4 配置salt master
vim /etc/salt/master
file_roots:
base:
- /srv/salt
重启salt-master
systemctl restart salt-master
gitlab4.png

2.2 配置46.66 jenkins
2.2.1 配置jenkins 
系统设置

jenkins2.png

jenkins3.png

创建项目
jenkins5.png

添加私钥
jenkins6.png

jenkins7.png

jenkins8.png
脚本放在/project
[root@jenkins01 project]# ls
code config deploy.sh package
code 代码目录
config 配置文件目录
package 代码打包后存放目录

deploy.sh jenkins构建时调用脚本
[root@jenkins01 project]# cat deploy.sh
#!/usr/bin/bash
###########[size=16]#[/size]
#author:CM
#mail:kakaximeng@163.com
#########[size=16]#[/size]
[size=16]提前建立相应的目录[/size]
LOCK_FILE="/project/lock"
CODE_PATH="/project/code"
DEMO_PATH="/project/code/demo"
PACKAGE_PATH="/srv/salt/package"
SALT_PACKAGE_PATH="package"
CONFIG_PATH="/project/config"
LOG_FILE="/project/deploy.log"
DEST_PROJECT="/project"
CTIME=$(date "+%Y-%m-%d-%H-%M")


Usage(){
echo "Usage: $0 ( deploy | list | rollback edition)"
}


Lock_judge(){
if [ -f $LOCK_FILE ];then
echo "warnning !!! someone else must deploy new version or rollback old version! please wait."
exit 2
else
touch -f $LOCK_FILE
fi
}

Delete_lock_file(){
if [ -f $LOCK_FILE ];then
rm -f $LOCK_FILE
fi
}

Deploy_code(){
Lock_judge
tag=$?
echo $tag
if [ $tag == 0 ];then
cd $DEMO_PATH
git pull
Commit=$(git log|head -n 1|cut -d ' ' -f 2)
Com_ID=$(echo ${Commit:0:6})
cp -r $DEMO_PATH ${PACKAGE_PATH}/demo_${Com_ID}_${CTIME}
cp -r ${CONFIG_PATH}/* ${PACKAGE_PATH}/demo_${Com_ID}_${CTIME}/
cd $PACKAGE_PATH
tar -zcf demo_${Com_ID}_${CTIME}.tar.gz demo_${Com_ID}_${CTIME}
salt "nginx" cp.get_file salt://${SALT_PACKAGE_PATH}/demo_${Com_ID}_${CTIME}.tar.gz ${DEST_PROJECT}/demo_${Com_ID}_${CTIME}.tar.gz
salt "nginx" cmd.run "cd ${DEST_PROJECT} && tar -zxf demo_${Com_ID}_${CTIME}.tar.gz && rm -f /usr/share/nginx/html/demo && ln -s ${DEST_PROJECT}/demo_${Com_ID}_${CTIME} /usr/share/nginx/html/demo && nginx -s reload"
fi
Delete_lock_file
}

Edition_list(){
salt 'nginx' cmd.run "ls -l ${DEST_PROJECT}|grep ^d"
}

Rollback_code(){
Lock_judge
salt 'nginx' cmd.run "rm -f /usr/share/nginx/html/demo && ln -s ${DEST_PROJECT}/$1 /usr/share/nginx/html/demo"
Delete_lock_file
}

main(){
case $1 in
deploy)
Deploy_code
;;
list)
Edition_list
;;
rollback)
Rollback_code $2
;;
*)
echo $1
Usage
;;
esac
}

main $1 $2

#脚本没有完善,没有写日志


测试


我之前已经推上去代码
jenkins9.png

nginx1.png
vim index.html

Adam Jensen


今晚上吃


西红柿鸡


西红牛腩


git add index.html
git commit -m "牛腩"
git push origin master
jenkins10.png

nginx2.png
回滚
[root@jenkins01 project]# ls
code config deploy.sh package
[root@jenkins01 project]# sh deploy.sh list
nginx:
drwxr-xr-x 7 root root 129 Mar 23 09:28 demo_205d03_2016-03-23-09-28
drwxr-xr-x 7 root root 129 Mar 23 17:04 demo_3c2a63_2016-03-23-17-04
drwxr-xr-x 7 root root 129 Mar 22 09:21 demo_43fabe_2016-03-22-09-21
drwxr-xr-x 7 root root 151 Mar 21 17:41 demo_4c5be1_2016-03-21-17-41
drwxr-xr-x 7 root root 151 Mar 21 17:48 demo_4c5be1_2016-03-21-17-48
drwxr-xr-x 7 root root 119 Mar 21 14:46 demo_574cdd_2016-03-21-14-46
drwxr-xr-x 7 root root 119 Mar 21 14:48 demo_574cdd_2016-03-21-14-48
drwxr-xr-x 7 root root 119 Mar 21 14:49 demo_574cdd_2016-03-21-14-49
drwxr-xr-x 7 root root 119 Mar 21 14:56 demo_574cdd_2016-03-21-14-56
drwxr-xr-x 7 root root 129 Mar 21 15:59 demo_8c7d37_2016-03-21-15-59
drwxr-xr-x 7 root root 129 Mar 23 17:22 demo_8cc9b4_2016-03-23-17-22
drwxr-xr-x 7 root root 129 Mar 21 15:41 demo_a2333d_2016-03-21-15-41
drwxr-xr-x 7 root root 129 Mar 23 17:03 demo_d5f4f3_2016-03-23-17-03

[root@jenkins01 project]# sh deploy.sh rollback demo_3c2a63_2016-03-23-17-04
nginx3.jpg

Centos下NTP时间服务器配置详解

Rock 发表了文章 0 个评论 5048 次浏览 2016-03-27 03:10 来自相关话题

NTP简介 NTP(Network Time Protocol,网络时间协议)是用来使网络中的各个计算机时间同步的一种协议。它的用途是把计算机的时钟同步到世界协调时UTC,其精度在局域网内可达0.1ms,在互联网上绝大多数的地方其精度 ...查看全部


NTP简介


NTP(Network Time Protocol,网络时间协议)是用来使网络中的各个计算机时间同步的一种协议。它的用途是把计算机的时钟同步到世界协调时UTC,其精度在局域网内可达0.1ms,在互联网上绝大多数的地方其精度可以达到1-50ms。
NTP服务器就是利用NTP协议提供时间同步服务的。


NTP服务器安装


# 系统自带ntp软件包
[root@node ~]# rpm -qa ntp
ntp-4.2.6p5-5.el6.centos.x86_64
# 如果没有就安装
[root@node ~]# yum -y install ntp


配置NTP服务


[root@node ~]# vim /etc/ntp.conf
# restrict default kod nomodify notrap nopeer noquery
restrict default nomodify
# nomodify客户端可以同步
# 将默认时间同步源注释改用可用源
# server 0.centos.pool.ntp.org iburst
# server 1.centos.pool.ntp.org iburst
# server 2.centos.pool.ntp.org iburst
# server 3.centos.pool.ntp.org iburst
server ntp1.aliyun.com #阿里时间服务器
server s2m.time.edu.cn #北京教育网时间服务器


启动NTP服务器


# 如果计划任务有时间同步,先注释,两种用法会冲突。
[root@node ~]# crontab -e
# time sync cron
#[i]/3 [/i] [i] [/i] * /usr/sbin/ntpdate ntp1.aliyun.com >/dev/null 2>&1
[root@node ~]# /etc/init.d/ntpd start
Starting ntpd: [ OK ]
[root@node ~]# ntpq -p
remote refid st t when poll reach delay offset jitter
==============================================================================
*ntp1.aliyun.com 10.137.38.86 2 u 22 64 1 525.885 -42.367 0.000
[root@node ~]# ntpstat
synchronised to NTP server (110.75.186.247) at stratum 3
time correct to within 4257 ms
polling server every 64 s


客户机时间同步


客户机要等几分钟再与新启动的ntp服务器进行时间同步,否则会提示no server suitable for synchronization found错误。
[root@node ~]# ntpdate 10.0.1.110 (ntp_server_ip)
27 Mar 18:40:16 ntpdate[1453]: step time server 10.0.1.110 offset 40.880807 sec
# 将命令放入计划任务即可。

Shell中数学计算总结

Ansible 发表了文章 1 个评论 2703 次浏览 2016-03-22 00:39 来自相关话题

Shell中的赋值和操作默认都是字符串处理,在此记下shell中进行数学运算的几个特殊方法,以后用到的时候可以来看看: 错误方法举例​ A: var=1+1 echo $var ...查看全部
Shell中的赋值和操作默认都是字符串处理,在此记下shell中进行数学运算的几个特殊方法,以后用到的时候可以来看看:


错误方法举例​


A:
 var=1+1
echo $var

输出的结果是1+1,悲剧,呵呵
B:
var=1
var=$var+1
echo $var

输出结果是1+1,依然悲剧,呵呵


正确方法


A: 使用let
var=1
let "var+=1"
echo $var

输出结果为2,这次没有悲剧
注意:
    []经我测试let几乎支持所有的运算符,在网上看到一篇文章说“let不支持++、--和逗号、(、)”,但经我测试自加、自减、以及括号的优先级都得到了很好的支持[/][]方幂运算应使用“”[/][]参数在表达式中直接访问,不必加$[/][]一般情况下算数表达式可以不加双引号,但是若表达式中有bash中的关键字则需加上[/][]let后的表达式只能进行整数运算[/]

B: 使用(())
var=1
((var+=1))
echo $var

输出结果为2
注意:
(())的使用方法与let完全相同
C: 使用$[]
var=1
var=$[$var+1]
echo $var

输出结果位2
注意:
    []$[]将中括号内的表达式作为数学运算先计算结果再输出[/][]对$[]中的变量进行访问时前面需要加$[/][]$[]支持的运算符与let相同,但也只支持整数运算[/]

D: 使用expr
var=1
var=`expr $var + 1`
echo $var

输出结果为2
注意:
    []expr后的表达式个符号间需用空格隔开[/][]expr支持的操作符有: |、&、<、<=、=、!=、>=、>、+、-、、/、%[/*][]expr支持的操作符中所在使用时需用\进行转义的有:|、&、<、<=、>=、>、[/*][]expr同样只支持整数运算[/]

E:使用bc(可以进行浮点数计算)
var=1
var=`echo "$var+1"|bc`
echo $var

输出结果为2
介绍:
bc是linux下的一个简单计算器,支持浮点数计算,在命令行下输入bc即进入计算器程序,而我们想在程序中直接进行浮点数计算时,利用一个简单的管道即可解决问题。
注意:
    []经我测试bc支持除位操作运算符之外的所有运算符。[/][]bc中要使用scale进行精度设置[/]

浮点数计算实例:
var=3.14
var=`echo "scale=2;$var*3"|bc`
echo $var

输出结果为9.42
F:使用awk(可进行浮点数计算)​
var=1
var=`echo "$var 1"|awk '{printf("%g",$1*$2)}'`
echo $var

输出结果为2
介绍:
awk是一种文本处理工具,同时也是一种程序设计语言,作为一种程序设计语言,awk支持多种运算,而我们可以利用awk来进行浮点数计算,和上面bc一样,通过一个简单的管道,我们便可在程序中直接调用awk进行浮点数计算。
注意:
    []awk支持除微操作运算符之外的所有运算符[/][]awk内置有log、sqr、cos、sin等等函数[/]

浮点数计算实例:
var=3.14
var=`echo "$var 2"|awk '{printf("%g",sin($1/$2))}'`
echo $var

输出结果为1


总结​


终于把所有搜集到的资料看完,最后总结分享给大家,以后再遇到shell计算问题,就可以直接用了!

Shell数组详解

Ansible 发表了文章 1 个评论 2730 次浏览 2016-03-21 23:34 来自相关话题

bash支持一维数组(不支持多维数组),并且没有限定数组的大小。类似与C语言,数组元素的下标由0开始编号。获取数组中的元素要利用下标,下标可以是整数或算术表达式,其值应大于或等于0。 数组定义 在Shell中 ...查看全部
bash支持一维数组(不支持多维数组),并且没有限定数组的大小。类似与C语言,数组元素的下标由0开始编号。获取数组中的元素要利用下标,下标可以是整数或算术表达式,其值应大于或等于0。


数组定义


在Shell中,用括号来表示数组,数组元素用"空格"符号分割开。定义数组的一般形式为:
array_name=(value1 ... valuen)
例如:
array_name=(value0 value1 value2 value3)
或者
array_name=(
value0
value1
value2
value3
)
还可以单独定义数组的各个分量:
array_name[0]=value0
array_name[1]=value1
array_name[2]=value2
可以不使用连续的下标,而且下标的范围没有限制。


读取数组


读取数组元素值的一般格式是:
${array_name[index]}
例如:
result=${array[2]}
举个栗子:
#!/bin/bash

IT[0]="hadoop"
IT[1]="cloud"
IT[2]="docker"
IT[3]="virtual"
IT[4]="web"
echo "First Index: ${IT[0]}"
echo "Second Index: ${IT[1]}"
运行脚本,输出:
First Index: hadoop
Second Index: cloud
用${数组名[下标]} 下标是从0开始  下标是:*或者@ 得到整个数组内容
举个栗子:
[root@workhost ansible]# vim test.sh
#!/bin/bash

IT[0]="hadoop"
IT[1]="cloud"
IT[2]="docker"
IT[3]="virtual"
IT[4]="web"
echo "First Method: ${IT[*]}"
echo "Second Method: ${IT[@]}"

[root@workhost ansible]# bash test.sh
First Method: hadoop cloud docker virtual web
Second Method: hadoop cloud docker virtual web


获取数组的长度


# 取得整个数组的长度
length=${#array_name[@]}
# 或者
length=${#array_name[*]}
# 取得数组单个元素的长度
lengthn=${#array_name[n]}

举个栗子:
len.png


删除数组


unset array_name
unset array_name[n]
举个栗子:
delete_array.png


特殊用法


分片:
[root@workhost ~]$ a=(1 2 3 4 5)
[root@workhost ~]$ echo ${a[@]:0:3}
1 2 3
[root@workhost ~]$ echo ${a[@]:1:4}
2 3 4 5

[root@workhost ~]$ c=(${a[@]:1:4})
[root@workhost ~]$ echo ${#c[@]}
4
[root@workhost ~]$ echo ${c}
2 3 4 5
直接通过 ${数组名[@或*]:起始位置:长度} 切片原先数组,返回是字符串,中间用"空格"分开,因此如果加上"()",将得到切片数组,上面例子:c就是一个新数据。
替换:
[root@workhost ~]$ a=(1 2 3 4 5)    
[root@workhost ~]$ echo ${a[@]/3/100}
1 2 100 4 5
[root@workhost ~]$ echo ${a[@]}
1 2 3 4 5

[root@workhost ~]$ a=(${a[@]/3/100})
[root@workhost ~]$ echo ${a[@]}
1 2 100 4 5
调用方法是:${数组名[@或*]/查找字符/替换字符} 该操作不会改变原先数组内容,如果需要修改,可以看上面例子,重新定义数据。
 

在线旅游平台如何确保第三方API高可用

cloudwise 发表了文章 1 个评论 2626 次浏览 2016-03-21 13:34 来自相关话题

十几年前,有一首流行歌曲《我想去桂林》红遍华夏大地,那时候旅游对很多人来说是一种奢侈。然而经济和社会福利的飞速发展,有钱有闲的国人越来越多,一到各种假期,不但国内旅游景点人满为患,就连周边国家和地区也满是中国游客,旅游已经成为大部分中国人日常生活中不可或缺的一 ...查看全部
十几年前,有一首流行歌曲《我想去桂林》红遍华夏大地,那时候旅游对很多人来说是一种奢侈。然而经济和社会福利的飞速发展,有钱有闲的国人越来越多,一到各种假期,不但国内旅游景点人满为患,就连周边国家和地区也满是中国游客,旅游已经成为大部分中国人日常生活中不可或缺的一部分。据国家旅游局发布的《2014年中国旅游业统计公报》显示,当年国内旅游人数达36.11亿人次,出境游人数达到1.07亿人次,全年实现旅游业总收入3.73万亿人民币。
随着互联网的普及和移动互联网的蓬勃兴起,在线旅游(OTA)行业率先实现了互联网+转型,酒店、机票、租车、景区门票、签证等上下游行业通过专业的旅游平台紧密结合,一站式获得出游所需的各项服务,在线旅游预订已经是大势所趋。

每天由API产生的订单多达几十万
主流在线旅游平台一般通过网站和移动客户端为用户提供服务,而服务需要对接国内20000余家及海外100000余家酒店预订,全国所有航线的机票预订,8000余家景区门票预订,全球热门演出门票预订,200多个城市租车预订,境内外品质旅游度假预订,并于近万家旅行社进行平台对接。
在线旅游平台通常采用典型电商IT架构,后端服务器、数据库做支撑,前端通过网页和移动端APP为用户提供服务,中间部署着CDN分布式网络和存储节点,不仅如此,还需要通过API与数以万计合作酒店、旅行社以及航空公司和第三方支付平台进行实时数据对接。对于在线旅游平台来说,线上系统不仅要处理大并发、高负载的用户访问,还面临着数据接口繁多、与第三方数据交换频繁的状况,内外部API接口多达数百个,每天通过API获取服务和交易的数据量多达几十万。

tour01.png

图:在线旅游平台通用IT架构

因为传统运维工具和方法对于API监控功能的缺失,所以在线旅游平台在日常运维过程中通常存在下列问题:
♦ 缺少对数据接口的监控,无法主动发现接口性能降低问题所在;
♦ 缺乏对接口性能的预警,当用户访问量骤增时导致数据接口调用的负载变化,进而影响API可用性;
♦ 缺少对接口的管理手段,传统的监控手段达不到突破性能瓶颈的效果。 
如何对API数据接口正确性和性能进行有效的监控和管理成为困扰IT部门的主要问题之一,不但会影响用户的服务体验,还将使公司运营蒙受损失:
♦ 利用第三方公司提供的应用程序API失败,导致业务中断,交易失败。
♦ 旅游网站通过API获取机票和酒店库存信息并抽取佣金,API调用失败导致失去一个客户或是一次机会。
♦ 其他数据源信息的API调用失败,影响自己的内容整合。
♦ 公司内部产品之间API不稳定导致业务下滑。
♦ 公司提供出去的API稳定性直接影响业务收入。

实时洞察API的可用性、响应时间和正确性
云智慧监控宝API监控是通过遍布全球的数百台分布式监测点模拟真实用户对在线旅游平台关键业务所使用的第三方API接口发起访问,实时检测API接口的可用性、正确性、响应时间,来确保接口运行的一致性、完整性、正确性、及时性。监控宝API监控不但能监控单个API接口的运行状况,还能对连续API请求所构成的事务(如:网络支付各个环节构成一个完整的支付事务)进行监控。
 
tour02.png.jpg

图:云智慧监控宝API监控原理

监控宝API监控能够:
♦ 支持API的get、post、put、delete、head、options六种请求。
♦ 支持Basic Auth, OAuth 1.0, OAuth2, Digest认证方式。
♦ 支持添加HTTP头Header和Value。
♦ 支持添加URL参数。
♦ 支持JSON、XML、Text、Response Status验证及脚本导入。
 
监控宝通过对API实时监控所获得即时数据进行统计分析,利用独家指标算法处理,结合告警消息、故障消息及故障类型统计分析,获得全面的历史数据,实现精准的数据分析;同时根据业务的实际需求和SLA自定义告警线,实时发送智能告警消息。通过监控宝Dashboard,用户可以快速查看TOP5的最低可用率、最长故障时间、最多故障次数、最低正确率、最长错误时间、最多错误次数、最慢响应时间、最快响应时间API数据图表,以及各监测点的相应监控数据。

保障SLA服务质量和业务营销
在线旅游平台IT部门通过监控宝的API监控,对酒店联盟、机票、景区门票和团购等关键业务的第三方API接口的可用率、响应时间及正确性进行24小时的实时监控,保障业务的正常运行和信息的及时获取。
♦ 主动识别API接口的可用性和可用率,分析数据接口的性能质量。
♦ 诊断和辨别API请求的正确与否,保障数据服务有效一致。
♦ 多点、真实、连续的性能数据分析,实现整个数据交互过程可反馈、可追述、可管控。
云智慧监控宝API监控解决方案为在线旅游平台提供的全面API性能监控、分析与管理的解决方案,能够帮助运营人员实时分析业务,明确业务性能问题以快速适应市场变化;帮助开发人员基于问题事件定位API瓶颈,以实现快速构建及改进持续交付;提供基于事务的API连续监控,帮助运维人员快速定位、预测API问题,确保运维SLA与服务质量;面向公司管理团队,提供多维报告与数据决策支持,帮助企业实现用户满意度及运营效率持续提升、企业营收不断提高。

监控宝API监控 免费试用中http://cloudwise.mikecrm.com/f.php?t=aZWutd 

如何让IT部门成为企业的价值中心

cloudwise 发表了文章 0 个评论 1954 次浏览 2016-03-18 14:37 来自相关话题

如今的中国企业正处在传统生产、经营模式向互联网+转型的风口浪尖,企业业务对IT和互联网的依赖越来越重,IT部门在企业的价值也是水涨船高。而IT部门要为企业创造更大的价值,同样需要改变过去被动响应的工作模式,主动走在业务的前面,来引导业务需求,驱动业务改变。 ...查看全部
如今的中国企业正处在传统生产、经营模式向互联网+转型的风口浪尖,企业业务对IT和互联网的依赖越来越重,IT部门在企业的价值也是水涨船高。而IT部门要为企业创造更大的价值,同样需要改变过去被动响应的工作模式,主动走在业务的前面,来引导业务需求,驱动业务改变。

为什么要做业务数据监控
四川文轩在线是一家做多渠道出版物(图书、音像)销售的电子商务公司,公司有自建的官网、移动官网、移动APP,也依托天猫、京东、当当、亚马逊、微信等平台开设自营旗舰店,另外还跟各大图书电商及各地新华书店开展各种供货、代发等业务。

1.png



由于公司业务线繁多,过去往往是业务部门通过月底报表发现业务出现明显滑坡后,才知道相关平台的IT系统出现故障,这时可能已经对企业经营造成不小的影响。为了保障业务系统的高效运行,公司要求对所有业务平台纳入监控,需要监控的业务指标非常多,靠人工去盯并不现实。因此,技术部门在监控好IT系统各环节运行情况的同时,还需要提供自动化工具给业务部门实现业务指标都系统自动监控。
现在网上有各种开源的、收费的、本地部署的、云上的监控工具,大多都是针对纯技术部门提供系统功能、性能及运行环境的监控,基本没有看到针对业务数据的监控工具,估计大家都认为业务数据的监控应该由报表来提供吧,据我了解的一些报表,要么没有,要么做的太粗糙不适合大规模使用。
业务数据监控相比系统监控的特点在于业务数据监控各公司、各岗位差异性很大,定制化要求很高,并且随着公司业务地发展可能随时需要调整。通过精挑细选,四川文轩在线于2013年开始使用云智慧监控宝,在监控宝独家功能——自定义监控的基础上,灵活构建起适应公司业务发展需要的监控体系。

公司中哪些角色需要监控业务数据?
首先公司的总经理、部门经理、业务主管肯定不会随时去关注具体的业务指标,他们也没时间去关注那么多,他们更适合使用报表查询分析结果来支撑他们制定业务策略。
而一线的业务运营人员呢?他们就需要时刻关注各项业务指标了,上级已经制定好各种业务操作办法,他们根据各项指标照着做。下面这张图简单说了下我们网店运营人员需要经常监控的几个指标:

2.png

(图一)

图上只简单列了几个常用的指标,网店运营人员当然还需要关注网店流量、用户特征、商品转换率、支付率、上架品种数等等指标,如果我们系统能够获取相关数据也是可以做成自动监控的。
图中列了3个指标,这些数据在报表中都能查到,但靠人查报表来监控实在太落后了,下班、请假、放假了怎么办?就应该当指标出现异常时监控系统及时给与相关人员报警通知才对。具体怎样来设计这些指标的告警呢?

下面我们大致介绍下销售相关的三个指标监控:
1、销售额:
每个公司肯定都有报表可以查到每日、每周、每月等等时间维度的销售额的,对于网店运营人员可能需要关注的更细,往往需要关注每小时的销售情况。当某个时点销售额急剧下降,可能意味着系统或者业务规则出现问题了,需要马上召集相关环节一起排查,尽快恢复,否则销售就丢失了。下面是我们关于某网店销售额的监控截图(出于公司信息保密原则,屏蔽了一些信息,请见谅):

3.png

(图二)

图二中有三条线:
当前小时的销售额 : 例如当前是9:08,则获取8:08~9:08的销售额
前一个时点销售额 : 例如当前是9:08,则获取7:08~8:08的销售额
昨天同时段销售额 :例如当前是9:08,则获取前一天8:08~9:08的销售额
我们可以看到,每天时间段不同,销售额会有很大差异,貌似很难针对当前小时销售额来设置一个阀值,所以我们用跟前一小时环比及跟昨天同时段同比来设置告警阀值,如下图:

4.png

(图三)

图三中分别是同比和环比差额。最终监控告警策略为:环比差额、同比差额低于一定阀值则报警,那么凌晨2点左右可能会有告警通知,但那时候业务也完全不用去管了,正常销售高峰期应该差额都不大的。
通过监控宝的自定义监控能够轻松实现销售额监控告警,当然监控宝暂时不支持自定义报警时段设置,否则还可以针对不同时间段,针对销售额绝对值设置不同告警阀值。例如凌晨不告警,白天分成几个时段分别设置几个最低销售额阀值。

2、销售平均折扣、销售平均客单价
这两个指标都对营销费用有很大影响,业务需要时刻关注,当折扣或客单价大幅降低时,需要马上知晓,曾经不止一家电商都由于系统、人为、第三方平台等原因导致的价格错误,最后要么认亏要么得罪消费者。这两个指标监控很简单,低于某个特定阀值就报警,在此不上图了。但如果订单量非常大时,就算出现有异常折扣订单,可能对整体销售折扣影响不大,这就需要另外设置一个指标:低于3折订单数监控。
其次技术人员是否需要监控业务数据?答案当然是需要的。下面举个我们网店系统运维人员需要监控的两个指标:

5.png

(图四)

网店转单时效 :网店订单支付时间与订单转入作业系统时间之间时效是否达到要求。我们要求订单支付后半小时内必须进入作业系统占用库存并安排发货,我们无论怎么设计这个系统,怎样监控系统的可用性和性能,最终我们仍然需要知道到底是否有漏网之鱼超出时效的,那一定是我们系统设计之初没有预见的。
网店转单性能 :转单程序没分钟转单数量。

这些指标的监控方式都比较简单,关键在于怎么从后端业务系统获取数据。
无论是一线业务运营人员还是系统运维人员根据各自的职责、业务特性都可以设计出很多监控指标,方式都是雷同的,在此不再赘述了。现在问题来了,这么多数据怎么提供给监控宝?
监控宝自定义监控设计地非常灵活,有兴趣去试用下或者看看帮助文档都能轻松掌握,关键是我们后端系统如何提供数据给监控宝呢?下面大致讲下我们监控系统的设计。

6.png

(图五)

第一版的系统设计非常简单,监控宝的数据都通过monitor提供,monitor系统只需要一张表记录各监控指标查询语句,所有的业务数据都直接通过sql查询业务数据库。这个结构随着业务量增加,会导致业务数据库压力越来越大,有些复杂业务监控sql语句执行性能已经不能被接收了,从而演变成下面这种结构:

7.png

(图六)

在监控数据库中根据业务监控指标对销售、商品、库存等指标进行建模,这些数据由业务系统流转过程中抽离出影响指标数据提交给业务数据收集器汇总保存,例如:从网店转单时,将订单的金额、折扣、网店支付时间等数据通过AMQ异步提交给收集器,收集器累计一个时间单元后计算出该时间单元内的总销售额、品均折扣、平均客单价等信息记录在监控库中,为了方便收集器快速配置,需要预先设置几种计算模型:直接保存、汇总、求平均、取最小值、最大值等等。
监控宝作为云智慧面向业务的全栈性能管理解决方案中的一环,与面向业务的端到端应用性能管理平台透视宝,和基于真实业务场景的大规模应用性能测试平台压测宝一起,共同以全面提升企业业务流程为目标,解决用户体验前置和云计算的快速发展带来的系统架构变化的挑战,帮助企业致胜互联网+。

了解更多IT性能监控知识,请关注云智慧官方微信(cloudwise2014)

8.jpg

 

自动立体化运维监控理念

push 发表了文章 0 个评论 3024 次浏览 2016-03-16 23:47 来自相关话题

场景 客户投诉有问题,于是研发测试运维开始投入定位和分析问题。A 研发去查日志,但是线上机器好多,一台一台的看, 日志文件太大,网速又慢,只能干等......B 研发同学觉得数据库可能有问题,但是自己又不能直接操作数据库,只能找DBA ...查看全部


场景


客户投诉有问题,于是研发测试运维开始投入定位和分析问题。
A 研发去查日志,但是线上机器好多,一台一台的看, 日志文件太大,网速又慢,只能干等......
B 研发同学觉得数据库可能有问题,但是自己又不能直接操作数据库,只能找DBA,但是DBA正好不在......
C 运维同学更头大,一边要应付研发和测试的各种问题,一边还要自己查机器CPU、内存、io、网络、程序 状态,而且还那么多机器


解决方案


这种情况就需要一套“立体化、自动化、可视化的监控”,具体实现如下:
1、立体化
将故障分析和定位时涉及的所有的相关信息都要监控起来,共分为5层
ywc.png

(1)业务层
收集和分析业务层的访问量、成功率等指标,例如当系统被刷的时候,业务层能够一目了然的看出访问量 会增加很多。
(2)应用服务层
以URI为维度的分析,可以看到某个URI的访问量、HTTP响应码分布、HTTP响应时间等指标
应用服务层与业务层并不是一一对应的关系,一个业务可能对应多个应用服务层的URI,一个URI也可能对应多个业务层的业务
(3)接口调用层
接口调用层指的是系统依赖的外部系统接口,收集的信息包括访问情况,包括时延、错误码、次数等,当外部系统故障导致我们的业务故障时,通过接口调用层就能够快速的定位具体问题
(4)基础组件层
基础组件层指系统依赖的底层组件,例如容器、数据库、缓存、消息队列
不同的组件收集的信息不一样,例如数据库MySQL的监控指标包括连接数、请求数、查询行数、更新行数等,而缓存包括 使用率、踢出率、命中率等
(5)基础设施层
基础设施层指操作系统状态、网络状态,收集的信息,包括cpu使用率、内存使用率、网卡流量、连接数等
2、自动化
不要再由人工去分析日志或者执行命令了,而是由程序自动完成这些动作
当故障定位的时候需要这些信息时,可以立即看到,节省故障定位时间
为此我们开发了一套数据收集和分析系统,这套系统可以从其它各个系统(包括业务系统、运维系统等)获取并分析数据,例如日志数据、状态数据等
数据自动化收集和分析系统的结构如下:
Logstash用于采集日志,redis用于缓存日志,elasticsearch用于存储和分析日志
logarch.png

3、可视化
故障定位所需要的信息能够通过图表和数字直观的展示出来
有了自动化的收集和分析作为基础,可视化只需要将数据做成图表展示即可
除此以外,同比、环比这类数据也可以通过系统直观的展示出来,方便快速判断问题所在
ksh.png


内容整理自"面向业务的立体化高可用架构设计"
作者李运华 阿里资深工程师


ZooKeeper架构设计及其应用

chris 发表了文章 0 个评论 8961 次浏览 2016-03-11 23:45 来自相关话题

ZooKeeper是一个开源的分布式服务框架,它是Apache Hadoop项目的一个子项目,主要用来解决分布式应用场景中存在的一些问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置管理等,它支持Standalone模式和分布式模式,在分布式模式下 ...查看全部
ZooKeeper是一个开源的分布式服务框架,它是Apache Hadoop项目的一个子项目,主要用来解决分布式应用场景中存在的一些问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置管理等,它支持Standalone模式和分布式模式,在分布式模式下,能够为分布式应用提供高性能和可靠地协调服务,而且使用ZooKeeper可以大大简化分布式协调服务的实现,为开发分布式应用极大地降低了成本。


总体架构​


ZooKeeper分布式协调服务框架的总体架构,如图所示:
zk1.png
ZooKeeper集群由一组Server节点组成,这一组Server节点中存在一个角色为Leader的节点,其他节点都为Follower。当客户端Client连接到ZooKeeper集群,并且执行写请求时,这些请求会被发送到Leader节点上,然后Leader节点上数据变更会同步到集群中其他的Follower节点。
Leader节点在接收到数据变更请求后,首先将变更写入本地磁盘,以作恢复之用。当所有的写请求持久化到磁盘以后,才会将变更应用到内存中。
ZooKeeper使用了一种自定义的原子消息协议,在消息层的这种原子特性,保证了整个协调系统中的节点数据或状态的一致性。Follower基于这种消息协议能够保证本地的ZooKeeper数据与Leader节点同步,然后基于本地的存储来独立地对外提供服务。
当一个Leader节点发生故障失效时,失败故障是快速响应的,消息层负责重新选择一个Leader,继续作为协调服务集群的中心,处理客户端写请求,并将ZooKeeper协调系统的数据变更同步(广播)到其他的Follower节点。


设计要点


ZooKeeper是基于如下4个目标来进行权衡和设计的,我们从设计及其特性的角度来详细说明:
    []简单[/]
分布式应用中的各个进程可以通过ZooKeeper的命名空间(Namespace)来进行协调,这个命名空间是共享的、具有层次结构的,更重要的是它的结构足够简单,像我们平时接触到的文件系统的目录结构一样容易理解,如图所示:
zk2.png
在ZooKeeper中每个命名空间(Namespace)被称为ZNode,你可以这样理解,每个ZNode包含一个路径和与之相关的元数据,以及继承自该节点的孩子列表。与传统文件系统不同的是,ZooKeeper中的数据保存在内存中,实现了分布式同步服务的高吞吐和低延迟。
[list=1][]在上图示例的ZooKeeper的数据模型中,有如下要点:[/][list=1][]每个节点(ZNode)中存储的是同步相关的数据(这是ZooKeeper设计的初衷,数据量很小,大概B到KB量级),例如状态信息、配置内容、位置信息等。[/][]一个ZNode维护了一个状态结构,该结构包括:版本号、ACL变更、时间戳。每次ZNode数据发生变化,版本号都会递增,这样客户端的读请求可以基于版本号来检索状态相关数据。[/][]每个ZNode都有一个ACL,用来限制是否可以访问该ZNode。[/][]在一个命名空间中,对ZNode上存储的数据执行读和写请求操作都是原子的。[/][]客户端可以在一个ZNode上设置一个监视器(Watch),如果该ZNode数据发生变更,ZooKeeper会通知客户端,从而触发监视器中实现的逻辑的执行。[/][]每个客户端与ZooKeeper连接,便建立了一次会话(Session),会话过程中,可能发生CONNECTING、CONNECTED和CLOSED三种状态。[/][]ZooKeeper支持临时节点(Ephemeral Nodes)的概念,它是与ZooKeeper中的会话(Session)相关的,如果连接断开,则该节点被删除。[/
    []冗余[/]
ZooKeeper被设计为复制集群架构,每个节点的数据都可以在集群中复制传播,使集群中的每个节点数据同步一致,从而达到服务的可靠性和可用性。前面说到,ZooKeeper将数据放在内存中来提高性能,为了避免发生单点故障(SPOF),支持数据的复制来达到冗余存储,这是必不可少的。
    []有序[/]
ZooKeeper使用时间戳来记录导致状态变更的事务性操作,也就是说,一组事务通过时间戳来保证有序性。基于这一特性。ZooKeeper可以实现更加高级的抽象操作,如同步等。
    []快速[/]
ZooKeeper包括读写两种操作,基于ZooKeeper的分布式应用,如果是读多写少的应用场景(读写比例大约是10:1),那么读性能更能够体现出高效。

数据模型

ZooKeeper有一个分层的命名空间,结构类似文件系统的目录结构,非常简单而直观。其中,ZNode是最重要的概念,前面我们已经描述过。另外,有ZNode有关的还包括Watches、ACL、临时节点、序列节点(Sequence Node)。
    []ZNode结构[/]
ZooKeeper中使用Zxid(ZooKeeper Transaction Id)来表示每次节点数据变更,一个Zxid与一个时间戳对应,所以多个不同的变更对应的事务是有序的。下面是ZNode的组成结构,引用文档如下所示:[list=1][]czxid – The zxid of the change that caused this znode to be created.[/][]mzxid – The zxid of the change that last modified this znode.[/][]ctime – The time in milliseconds from epoch when this znode was created.[/][]mtime – The time in milliseconds from epoch when this znode was last modified.[/][]version – The number of changes to the data of this znode.[/][]cversion – The number of changes to the children of this znode.[/][]aversion – The number of changes to the ACL of this znode.[/][]ephemeralOwner – The session id of the owner of this znode if the znode is an ephemeral node. If it is not an ephemeral node, it will be zero.[/][]dataLength – The length of the data field of this znode.[/][]numChildren – The number of children of this znode.[/]
    []Watches(监视)[/]
ZooKeeper中的Watch是只能触发一次。也就是说,如果客户端在指定的ZNode设置了Watch,如果该ZNode数据发生变更,ZooKeeper会发送一个变更通知给客户端,同时触发设置的Watch事件。如果ZNode数据又发生了变更,客户端在收到第一次通知后没有重新设置该ZNode的Watch,则ZooKeeper就不会发送一个变更通知给客户端。
ZooKeeper异步通知设置Watch的客户端。但是ZooKeeper能够保证在ZNode的变更生效之后才会异步地通知客户端,然后客户端才能够看到ZNode的数据变更。由于网络延迟,多个客户端可能会在不同的时间看到ZNode数据的变更,但是看到变更的顺序是能够保证有序一致的。
ZNode可以设置两类Watch,一个是Data Watches(该ZNode的数据变更导致触发Watch事件),另一个是Child Watches(该ZNode的孩子节点发生变更导致触发Watch事件)。调用getData()和exists() 方法可以设置Data Watches,调用getChildren()方法可以设置Child Watches。调用setData()方法触发在该ZNode的注册的Data Watches。调用create()方法创建一个ZNode,将触发该ZNode的Data Watches;调用create()方法创建ZNode的孩子节点,则触发ZNode的Child Watches。调用delete()方法删除ZNode,则同时触发Data Watches和Child Watches,如果该被删除的ZNode还有父节点,则父节点触发一个Child Watches。
另外,如果客户端与ZooKeeper Server断开连接,客户端就无法触发Watches,除非再次与ZooKeeper Server建立连接。
    []Sequence Nodes(序列节点)[/]
在创建ZNode的时候,可以请求ZooKeeper生成序列,以路径名为前缀,计数器紧接在路径名后面,例如,会生成类似如下形式序列:
qn-0000000001, qn-0000000002, qn-0000000003, qn-0000000004, qn-0000000005, qn-0000000006, qn-0000000007
    []ACLs(访问控制列表)[/]
ACL可以控制访问ZooKeeper的节点,只能应用于特定的ZNode上,而不能应用于该ZNode的所有孩子节点上。它主要有如下五种权限:[list=1][]CREATE 允许创建Child Nodes[/][]READ 允许获取ZNode的数据,以及该节点的孩子列表[/][]WRITE 可以修改ZNode的数据[/][]DELETE 可以删除一个孩子节点[/][]ADMIN 可以设置权限[/]ZooKeeper内置了4种方式实现ACL:[list=1][]world 一个单独的ID,表示任何人都可以访问[/][]auth 不使用ID,只有认证的用户可以访问[/][]digest 使用username:password生成MD5哈希值作为认证ID[/][]ip 使用客户端主机IP地址来进行认证[/]
    []ZooKeeper Session[/]

当客户端连接到ZooKeeper集群时,建立了会话。会话过程中的状态变迁,如图所示:
zk3.png
建立连接过程中,会话状态为CONNECTING;当连接建立成功后,会话状态变为CONNECTED。会话过程中,如果正常的话,会话的状态只能是CONNECTING和CONNECTED二者之一。如果在会话过程中连接断开,则变为CLOSED状态。


应用陷阱


并非任何分布式应用都适合使用ZooKeeper来构建协调服务,我们根据ZooKeeper提供的文档,给出哪些情况下使用会出现问题,又是如何应对这种问题的。总结如下:
1、丢失ZNode上的变更通知
客户端连接到ZooKeeper Server以后,会维护一个TCP连接。在CONNECTED状态下,客户端设置了某个ZNode的Watch监听器,可以收到来自该节点变更的通知(后续会触发一定的逻辑执行流程)。但是,如果由于网络异常,客户端断开了与ZooKeeper Server的连接,在断开的过程中,是无法收到ZooKeeper在ZNode上发送的节点数据变更通知的。
所以,如果使用ZooKeeper的Watch,必须要寻找保持CONNECTED的Watch,才能保证不会丢失该Watch监控的ZNode上的数据变更通知。
2、无效ZooKeeper集群节点列表​
与ZooKeeper集群交互时,一般情况下客户端会持有一个ZooKeeper集群节点的列表,或者列表的子集,那么会存在如下两种情况:
一种情况是,如果客户端持有的列表或者列表子集,其中节点都处于Active状态,能够提供协调服务,那么客户端访问ZooKeeper集群没有任何问题。
另一种情况,客户端持有ZooKeeper集群节点列表或列表子集,如果列表中的某些节点因为故障退出了集群,如果客户端再次连接这一类失效的节点,就无法获取服务。
所以,我们在应用中使用ZooKeeper集群时,一定要明确这一点,或者跳过无效的节点,或者重新寻找有效的节点继续业务处理,或者检查ZooKeeper集群,使整个集群恢复正常。
3、配置导致的性能问题
如果设置Java堆内存(Heap)不合理,会导致ZooKeeper内存不足,会在内存与文件系统之间进行数据交换,导致ZooKeeper的性能极大地下降,从而可能会影响应用程序。
为了避免Swapping问题的出现,主要考虑设置足够的Java堆内存,同时减少被操作系统和Cache使用的内存,尽量避免在内存与文件系统之间发生数据交换,或者可以将交换限制在一定的范围之内。
4、事务日志存储设备性能​
ZooKeeper会同步事务到存储设备,如果存储设备不是专用的,而是和其他I/O密集型应用共享同一磁盘,会导致ZooKeeper的效率。因为客户端请求ZNode数据变更而发生的事务,ZooKeeper会在响应之前将事务日志写入存储设备,如果存储设备是专用的,那么整个服务以至外部应用都会获得极大地性能提升。
5、ZNode存储大量数据导致性能问题​
ZooKeeper的设计初衷是,每个ZNode只存放少量的同步数据,如果存储了大量数据,导致ZooKeeper每次节点发生变更时需要将事务写入存储设备,同时还要在集群内部复制传播,这将导致不可避免的延迟和性能问题。
所以,如果需要与大量的数据相关,可以将大量数据存储在其他设备中,而只是在ZooKeeper中存储一个简单的映射,如指针、引用等等。

 
参考链接:
http://zookeeper.apache.org/ 
http://zookeeper.apache.org/doc/r3.3.4/zookeeperOver.html 
http://wiki.apache.org/hadoop/ZooKeeper/PoweredBy 
http://www.ibm.com/developerworks/cn/opensource/os-cn-zookeeper/ 
http://zookeeper.apache.org/doc/r3.3.4/recipes.html 
http://zookeeper.apache.org/doc/r3.3.4/zookeeperProgrammers.html