第一章 Zookeeper的架构原理和使用
第 4 节
Zookeeper的部署和运维
部署Zookeeper集群
虚拟机环境规划
主机名 | ip | 操作系统 |
---|---|---|
centos7_1 | 192.168.80.30 | CentOS7 |
centos7_2 | 192.168.80.31 | CentOS7 |
centos7_3 | 192.168.80.32 | CentOS7 |
1)安装CentOS7操作系统
使用visualbox(或VMware)安装CentOS-7-x86_64-Minimal-1804.iso(Minimal表示是最小化版本)
2)CentOS7系统基础配置
#1.设置网络环境
vi /etc/sysconfig/network-scripts/ifcfg-enp0s3
ONBOOT=yes
IPADDR=192.168.30.10
BOOTPROTO=static
/etc/init.d/network restart
#2.关闭防火墙和Selinux
systemctl disable firewalld
systemctl stop firewalld
vi /etc/selinux/config
SELINUX=disabled
setenforce 0
#3.准备安装环境,安装一个上传下载的命令、清空目录。之后软件都安装在/usr/local目录下
yum install -y lrzsz
cd /usr/local
rm -rf *
#4 设置置主机名
vi /etc/hostname
vi /etc/sysconfig/network
hostname centos7_1
vi /etc/hosts
#配置3台机器的域名映射
192.168.80.30 centos7_1
192.168.80.31 centos7_2
192.168.80.32 centos7_3
## ctrl+D退出登录,重新登录,可以check下host配置是否生效
#到这里,我们可以使用CRT和XShell连接了。
##############可选配置##################
#配置dns (可选)
[root@localhost ~]# cat /etc/resolv.conf
# Generated by NetworkManager
nameserver 8.8.8.8
#自动补全 (可选)
yum install bash-completion
其他2台虚拟机类似的重复以上3步。(当然熟练使用VmWare的同学,可以使用克隆功能,修改下克隆后的虚拟机mac地址和网络配置中的ip、host配置,这样可以省去重新安装操作系统的步骤。不过安装CentOS-7-x86_64-Minimal-1804.iso时间也不长,一般也就5分钟)。
下载jdk1.8 linux版本,下载zookeeper-3.4.5.tar.gz。通过rz命令,上传到/usr/local目录下。之后按照如下脚本执行命令即可。
#配置JDK环境
tar zxf jdk-8u201-linux-x64.tar.gz
mv jdk1.8.0_201/ jdk
vi ~/.bashrc
export ZOOKEEPER_HOME=/usr/local/zk
export JAVA_HOME=/usr/local/jdk
export CLASS_PATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib
export PATH=$PATH:$ZOOKEEPER_HOME/bin:$JAVA_HOME/bin
source ~/.bashrc
#安装zookeeper(设置环境变量、
cd /usr/local
tar zxf zookeeper-3.4.5.tar.gz
mv zookeeper-3.4.5 zk
vi ~/.bashrc
export ZOOKEEPER_HOME=/usr/local/zk
export PATH=$PATH:$ZOOKEEPER_HOME/bin
source ~/.bashrc
#修改zookeeper配置文件
cd /usr/local/zk/conf/
cp zoo_sample.cfg zoo.cfg
vi zoo.cfg
#修改
dataDir=/usr/local/zk/data
#新增
server.0=node1:2888:3888
server.1=node1:2888:3888
server.2=node3:2888:3888
# 创建数据存放目录
mkdir /usr/local/zk/data
cd /usr/local/zk/data
vi myid
0
#检查命令:
cat /usr/local/zk/conf/zoo.cfg
cat /usr/local/zk/data/myid
#拷贝整个zk目录到其他2台机器
scp -P 22 -r zk/ root@192.168.30.11:/usr/local/
scp -P 22 -r zk/ root@192.168.30.12:/usr/local/
#修改下两台机器的myid配置,分别为1、2
#启动:分别在三台机器上执行
zkServer.sh start
#检查ZooKeeper状态 应该是一个leader,两个follower
zkServer.sh status
#检查三个节点是否都有QuromPeerMain进程
jps
部署完成后,最终得到了如下图所示的Zookeeper集群:
Tips: 2888端口:数据同步和运行时通信 3888端口:leader选举是通信 2181端口:提供给客户端连接使用。
Zookeeper的机器配置和启动参数分析
通过上面的步骤, 你应该可以基本跑起来Zookeeper了。但是我们用的是虚拟机,但是实际生产运维部署的时候,Zookeeper的物理机的配置又应该怎么选择呢?
其实这个要结合之前介绍的Zookeeper基本架构原理,就不难分析出。
首先Zookeeper需要支撑高并发,CPU核数高肯定是好的 16核,一般就算比较高配了,当然也可以是24、36、48核一般中小公司,甚至一些大公司都是是用不到的,只有超大互联网公司才可能用,一般使用16核就差不多了。
Zookeeper需要支撑高性能,数据主要在内存维护。而且内存结构无法存海量数据,所以内存配置的大一点最好。一般可以使用32G的内存。
Zookeeper为了保证数据一致性,受单机写、过半写限制、写的时候又会持久化事务日志和快照数据,这个写入速度越快越好,而且是顺序写为主。所以磁盘使用SSD硬盘,肯定可以更好的提高写吞吐量。
通过上面的分析,可以得出,一般建议Zookeeper使用16核 32G SSD硬盘的物理机配置,而且Zookeeper集群一般是小集群部署,所以通常是3台或5台这样的高配物理机。
有了上面的机器配置后,这样的集群到你能抗多少的写qps和读qps呢?你可以参考下官方给出的一个压测报告,如下图所示:
上面是3.2版本,2核并且是SATA硬盘的测试,每秒读写都是1k的测试。都能轻松的抗下几万的qps。
那么一般来说,16核32G,3台机器,1个leader,2个follower,leader主要是写,每秒抗几万并发写入,每秒抗十几万的读是没有问题的。
之前我们提到过,Zookeeper的写QPS无法线性的扩展。但是读可以通过增加Observer节点来扩展。所以如果需要抗更多的读QPS,可以考虑使用Observer。
实际在中间件使用时,几乎不会用到Observer节点,因为中间的系统的高并发不是在Zookeeper上,通常是在中间件上。而一般只有一些业务系统,大数据或者java业务系统需要使用zk做分布式协调通知、配置管理等才可能有高并发读的场景。可能需要扩展Observer节点。
你可以取找运维聊聊,看看你们公司的Zookeeper机器集群是什么样的配置。熟悉生产机器配置不光是运维知道就够了,如果你是高级、资深、架构师、技术专家这个级别的程序员,这个起码要掌握的。这样你才能更好地cover住你负责的系统,对系统的瓶颈做到心中有数。
你还记得zk启动时的命令么?启动命令如下:
zkServer.sh start
脚本对应的核心启动代码如下:
if [ "x$JMXDISABLE" = "x" ]
then
echo "JMX enabled by default" >&2
# for some reason these two options are necessary on jdk6 on Ubuntu
# accord to the docs they are not necessary, but otw jconsole cannot
# do a local attach
# 默认JMX端口,是只对本地开放
ZOOMAIN="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.local.only=$JMXLOCALONLY org.apache.zookeeper.server.quorum.QuorumPeerMain"
else
echo "JMX disabled by user request" >&2
#QuorumPeerMain代表了peer架构,Quorum机制,大多是成功就算成功
ZOOMAIN="org.apache.zookeeper.server.quorum.QuorumPeerMain"
fi
##省略其他
case $1 in
start)
echo -n "Starting zookeeper ... "
if [ -f $ZOOPIDFILE ]; then
if kill -0 `cat $ZOOPIDFILE` > /dev/null 2>&1; then
echo $command already running as process `cat $ZOOPIDFILE`.
exit 0
fi
fi
#通过nohup启动一个后台线程,启动入口就是之前的ZOOMAIN变量指定的代码类
nohup $JAVA "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \
-cp "$CLASSPATH" $JVMFLAGS $ZOOMAIN "$ZOOCFG" > "$_ZOO_DAEMON_OUT" 2>&1 < /dev/null &
可以看到zk启动主要是通过zkServer.sh start,之后设置了一个环境变量ZOOMAIN,在之后通过nohup启动了一个进程。整体大致如下图所示:
从之前的启动参数可以看出,$JVMFLAGS这个就是zookeeper设置JVM参数的变量。默认是通过zookeeper/bin/zkEnv.sh这个脚本设置这个变量的。如下所示:
#!/bin/sh
ZOOBINDIR=${ZOOBINDIR:-/usr/bin}
ZOOKEEPER_PREFIX=${ZOOBINDIR}/..
# 省略...
ZOOCFG="$ZOOCFGDIR/$ZOOCFG"
if [ -f "$ZOOCFGDIR/java.env" ]
then
. "$ZOOCFGDIR/java.env"
fi
# 省略...
可以看到,通过zookeeper/conf/java.env这个文件设置java相关参数的。当然如果你直接在启动脚本zkServer.sh的启动命令里中加,当然也没有什么问题。
zookeeper设置JVM内存参数思路,由于zookeeper使用了高配的物理机,内存一般是16G或者更大。此时使用经典的ParNew+CMS垃圾回收器就不太适合了,大内存一般使用G1会好一点。
而G1默认region是2MB,尽可能最大回收回收时间是200ms,这个默认值是否需要调整根据zookeeper存放数据的情况,运行情况来看。你可以通过jstat观察高峰时,对象的回收情况,年轻代、老年代回收的次数、时间,增长大小等等。来进行调优,这里我就不带大家展开讨论了。这里给一个demo供大家参考。
默认这个文件没有创建,你可以创建如下的java.env:
#!/bin/sh
export JAVA_HOME=$ZOOBINDIR/../../jdk6
# heap size MUST be modified according to cluster environment
export JVMFLAGS="-server -Xms12g -Xmx12g -Xmn6g -XX:+UseG1GC -XX:G1HeapRegionSize=16m -XX:G1ReservePercent=25 -XX:InitiatingHeapOccupancyPercent=30 -XX:SoftRefLRUPolicyMSPerMB=1000 -verbose:gc -Xloggc:/usr/local/zk/log/zk_gc_%p.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintAdaptiveSizePolicy -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=30m -XX:-OmitStackTraceInFastThrow -XX:+AlwaysPreTouch -XX:MaxDirectMemorySize=10g -XX:-UseLargePages -XX:-UseBiasedLocking $JVMFLAGS"
这些参数我简单解释一下:
-server:这个参数就是说用服务器模式启动,这个没什么好说
-Xms18g -Xmx18g -Xmn10g :由于使用了高配物理机是32g内存的。如果你不太好评估zookeeper之后的使用场景的话,有一个经验值是,总堆内存一般不超过机器内存的80%,年轻代一般为堆内存的一半左右。之后观察下,有需要在调整就可以了。
-XX:+UseG1GC -XX:G1HeapRegionSize=16m:选用了G1垃圾回收器来做分代回收,这里把G1的region大小设置为了16m,这个因为机器内存比较多,所以region大小可以调大一些给到16m,不然用2m的region,会导致region数量过多的。
-XX:G1ReservePercent=25:这个参数是,默认值是10%,意思是在G1管理的老年代里预留25%的空闲内存,保证新生代对象晋升到老年代的时候有足够空间,避免老年代内存都满了,新生代有对象要进入老年代没有充足内存了,这里调整的大一些好一点,10%有点小。
-XX:InitiatingHeapOccupancyPercent=30:这个参数是说,默认值是45%。当堆内存的使用率达到30%之后就会自动启动G1的并发垃圾回收,开始尝试回收一些垃圾对象。这里调低了一些,是为了提高了GC的频率,如果积累的垃圾对象过多,一次垃圾回收耗时过长的问题。
-XX:SoftRefLRUPolicyMSPerMB=1000:这个参数默认设置为0,建议这个参数不要设置为0,避免频繁回收一些软引用的Class对象,这里可以调整为比如1000
-verbose:gc -Xloggc:/dev/shm/mq_gc_%p.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintAdaptiveSizePolicy -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=30m:这一堆参数都是控制GC日志打印输出的,确定了gc日志文件的地址,要打印哪些详细信息,然后控制每个gc日志文件的大小是30m,最多保留5个gc日志文件。
-XX:-OmitStackTraceInFastThrow:这个参数是说,有时候JVM会抛弃一些异常堆栈信息,因此这个参数设置之后,就是禁用这个特性,要把完整的异常堆栈信息打印出来
-XX:+AlwaysPreTouch:这个参数的意思是我们刚开始指定JVM用多少内存,不会真正分配给他,会在实际需要使用的时候再分配给他
所以使用这个参数之后,就是强制让JVM启动的时候直接分配我们指定的内存,不要等到使用内存的时候再分配
-XX:MaxDirectMemorySize=10g:NIO中的direct buffer限定。这里限定了direct buffer最多申请多少,如果你机器内存比较大,可以适当调大这个值。
-XX:-UseLargePages -XX:-UseBiasedLocking:这两个参数的意思是禁用大内存页和偏向锁。
Zookeeper最最核心的配置
Zookeeper也一样,有一些配置。这里先给大家介绍其中最核心的几个配置,研究源码的涉及到的时候,你起码心理有个数,起码知道它是干嘛的。
其实这些配置和之前的架构原理是分不开的。
比如持久化机制中,涉及的配置主要有:
dataDir:存放zookeeper的数据快照目录。
dataLogDir:每台机器都会写入一个本地磁盘的事务日志的目录。
snapCount:100000,默认是10万条事务日志,生成一次快照数据。
如果有10w零300个事务。此时zk重启,他可以直接把包含10w个事务的快照直接加载到内存里来。然后把10w之后的300个事务,100001~1000300的事务,在内存里回放一遍,就可以在内存里恢复出来重启之前的数据了。即使没到达到10万条事务,就重启了,此时没有快照,10万个事务以内,不需要快照,因为直接读取事务日志回放到内存,就可以重建内存数据。
autopurge.purgeInterval=1
autopurge.snapRetainCount=3
定时清理数据文件的功能 每一个小时执行一次清理,每次保留3个数据快照文件
补充到之前的架构原理图中,如下图所示:
forceSync:yes 在commit提交的时候一般默认会强制把写的事务fsync到磁盘上去。
事务进行commit提交的时候,有没有日志丢失的风险?默认情况下,在2PC阶段的第一个阶段里,各个机器把事务日志写入磁盘,此时一般进入os cache的,没有直接进入物理磁盘上去。机器坏了,有可能会丢失部分os cache里没刷入磁盘的数据,所以需要fsync到磁盘上去。
tickTime:是zookeeper定义的时间单位,默认2000毫秒。其他的一些参数就会以这个tickTime为基准来进行设置。
initLimit:默认值10,意思是10 * tickTime=20s。多少个tickTime内,允许其他server连接并初始化数据,如果zooKeeper管理的数据较大,则应相应增大这个值。
syncLimit:默认值5,意思就是5 * tickTime=10s,leader跟follower之间会进行心跳,如果超过10s没有心跳,leader就把这个follower给踢出去了,认为它挂了。
leaderServers:默认值为yes。leader负责协调更新。当更新吞吐量远高于读取吞吐量时,可以设置为不接受客户端连接,以便leader可以专注于同步协调工作。
maxClientCnxns: 3.4.5默认是60,一台机器上,我们可以创建多少个zk客户端?是有限制的,默认来说60。注意这个zk客户端是API层面的一个对象,不是tcp连接。说白了,每台服务,无论是java业务系统还是中间件,一般使用一个zk客户端,单例的就行。不要每次使用都新创建,并发一高,会拒绝超过60个的其他客户端的。也就是多少台机器和服务一般就有多少个客户端。这个要根据使用zk集群的系统规模适当调整。
minSessionTimeout:最小的客户端session超时时间,默认值为2个tickTime,单位是毫秒maxSessionTimeout:最大的客户端session超时时间,默认值为20个tickTime,单位是毫秒
jute.maxbuffer znode中最多能存储多大数据量?默认1048575bytes,也就是1mb。一般来说建议,不要在znode中存储的数据量过大,默认这个值一般足够了。
peerType: 可以设置zookeeper的节点类型,比如observer的设置,设置成observer的节点不会参与选举,只会同步数据,提供只读服务
结合之前的架构原理图,参数对应的环节,基本如下所示:
zookeeper监控
除了通过zkServer.sh status查看集群状态,zookeeper还可以通过四字命令进行一些监控,举个例子:
yum install nc -y
echo conf | nc localhost 2181
[root@centos7_2 ~]# echo conf|nc localhost 2181
clientPort=2181
dataDir=/usr/local/zk/data/version-2
dataLogDir=/usr/local/zk/data/version-2
tickTime=2000
maxClientCnxns=60
minSessionTimeout=4000
maxSessionTimeout=40000
serverId=1
initLimit=10
syncLimit=5
electionAlg=3
electionPort=3888
quorumPort=2888
peerType=0
就可以看到zk相关的写配置,类似的还有很多conf(查看配置)、cons(查看连接)、mntr(输出比stat更详细的)等等。你有兴趣可以去查找下相关命令,甚至可以开发一个可视化的监控平台,可以自己基于他的这些命令的输出绘制出来一些图形实现可视化的监控。
而且zookeeper是java的一个进程,当然可以开启JVM进程远程端口,进行JVM内存监控和分析。在之前的zkServer.sh中可以配置如下参数即可:
-Dcom.sun.management.jmxremote.port=21811
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
这里不是运维培训,介绍这些是为了给大家提一个思想。你要思考,监控是如何实现的。其实监控的本质万变不离其宗,就是暴露监控数据接口,或者通过代码埋点记录,之后统计数据,绘制成图形之类的。你要抓住这一点本质思想,不要抓住一些表象手段,比如命令或者图形工具,而是多应该思考不同的中间件实现方式的区别,为什么,各自好处。为你之后自己实现监控或者开源项目时候提供思路和方向。
小结
好了,今天主要带你熟悉了zookeeper基本的部署、机器配置、启动参数、核心配置、基本监控这几点。这些虽然偏向运维的一些知识,这些知识本身和你可能平常接触的业务系统无关。但是你要的要明白,你要对其他中间件,包括自己的业务系统,也要有这样的把握能力。这个是一个优秀的工程师应该具备的,要想更好地职业发展,就不要被职业划分限制自己,你不是仅仅的熟悉业务系统就够了的。
金句甜点
之前我和大家聊了聊,贴标签和揭标签、不忘初心,找回斗志和单纯。这些其实都是为了让你突破和改变自己。因为之前我也说过,改变自己比改变别人更重要。你要想成长,进步这个是必须的。
你可以把这些当成心灵鸡汤,也可以当成我的一些指点。但前提是你要相信这些。相信不是靠你说什么,而是看你做什么的。你说你相信我说的这,认可我这些。其实没用。其实你内心不一定真的愿意接受的。我给大家讲个故事,有一个大师非常擅长祈雨,有一个村子干旱了很久,村里的人就请来了这个大师,大师祈雨问村民们:“你们相信我能祈来雨么?”。村民们大声地说:“相信!相信!”。大师看了看大家,说道:“既然你们相信,那为什么没有一个人带伞呢?”
其实很多时候你可能就是这样,不在乎一些有经验人的指点,在乎杂七杂八的人指指点点,被打击的没有斗志,被贴了一顿标签,禁锢了自己。你需要改变一下自己,慢慢接受成长记中的一些思想,慢慢的去尝试的做。慢慢的你就会发现,你变得不一样了。
希望各位阅读成长记的人,不仅仅成长的是知识,更重要的是思想和观念的成长。这也是我很重要的初心,很单纯的初心。大家一起努力吧!