ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,也是Hadoop和Hbase的重要组件 。ZooKeeper的选主过程是以Fast Paxos算法为基础的。前面我有文章写过Paxos算法,Paxos 算法存在活锁的问题,即当有多个proposer交错提交时,有可能互相排斥导致没有一个proposer能提交成功,而Fast Paxos对Paxos进行了优化。通过选举产生一个leader (领导者),只有leader才能提交proposer,具体算法可见Fast Paxos 。因此,要想弄懂ZooKeeper首先得对Fast Paxos有所了解 。因为此算法的特性,所以要求Zookeeper集群的节点数最好为奇数个,而且算法保证如果集群中Leader宕机,只要剩余节点在总节点数的半数以上,就会重新选主,继续对外提供服务。也正是由于这个原因,使得Zookeeper是一个高效的分布式协调服务,可以提供配置信息管理、命名、分布式同步、集群管理、数据库切换等服务,被广泛应用于分布式应用场景中。本人在工作中也使用了很久,故此记录一下。
首先准备环境:三个虚拟机,jdk1.8。
ip地址 | 系统 |
---|---|
192.168.31.135 | CentOS 7 |
192.168.31.136 | CentOS 7 |
192.168.31.137 | CentOS 7 |
1 . JDK安装
(1) . jdk 1.8下载地址:jdk-8u181-linux-x64.tar.gz 。进入官网后选择Linux 64位下载
创建一个用户文件夹
mkdir /user/java
将要下载好的jdk包上传到这个目录下,然后进入到这个目录执行如下命令
tar -zxvf jdk-8u181-linux-x64.tar.gz
解压缩包后 /user/java 会多出一个 jdk1.8.0_181文件夹。
(2) . 配置环境变量
使用vi命令编辑profile文件即可。如下
vi /etc/profile
下拉直文件底部,然后添加如下内容
JAVA_HOME=/user/java/jdk1.8.0_181
PATH=$JAVA_HOME/bin:$PATH
CLASSPATH=.:$JAVA_HOME/jre/lib/ext:$JAVA_HOME/lib/tools.jar
export PATH JAVA_HOME CLASSPATH
然后保存退出。为验证是否安装成功,可输入两个命令 javac,java -version。在输入javac出现如下信息,而不是出现command not found 。
[root@centos1 java]# javac
用法: javac
然后再输入 java -version 出现如下信息则表示jdk已安装成功
[root@centos1 java]# java -version
java version "1.8.0_181"
Java(TM) SE Runtime Environment (build 1.8.0_45-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, mixed mode)
(2) . Zookeeper下载
下载地址:zookeeper 。如下图
选择合适的版本,这里我下载的是3.4.10。点击即可下载
也可在CentOS中通过shell命令下载。命令如下
wget http://mirrors.cnnic.cn/apache/zookeeper/zookeeper-3.4.10/zookeeper-3.4.10.tar.gz
通过如下镜像网址,可选择合适的版本下载
下载完成后将tar包放到 /user/zookeeper下,此目录可自行定义
进入到该目录,解压缩
tar -zxvf zookeeper-3.4.10.tar.gz
再定义两个目录用于存放日志和数据
图中3.3.6是我之前安装的版本。具体命令如下
mkdir zkdata #存放快照日志
mkdir zkdatalog#存放事物日志
(3) . 修改配置文件
进入到zookeeper的解压目录中,找到conf目录,查看
[root@centos1 /]# cd /user/zookeeper/zookeeper-3.3.6/conf
[root@centos1 conf]# ll
-rw-rw-r--. 1 1000 1000 535 7月 29 2012 configuration.xsl
-rw-rw-r--. 1 1000 1000 1698 7月 29 2012 log4j.properties
-rw-r--r--. 1 root root 550 4月 12 23:10 zoo.cfg
-rw-rw-r--. 1 1000 1000 380 7月 29 2012 zoo_sample.cfg
zoo_sample.cfg 这个文件是官方给我们的zookeeper的样板文件,给他复制一份命名为zoo.cfg,zoo.cfg是官方指定的文件命名规则。 这里我已经创建好了。zoo.cfg中配置信息如下
# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
dataDir=/user/zookeeper/zkdata
# the port at which the clients will connect
clientPort=2181
# log dir
dataLogDir=/user/zookeeper/zkdatalog
# ip setting
server.1=192.168.31.135:2888:3888
server.2=192.168.31.136:2888:3888
server.3=192.168.31.137:2888:3888
配置文件属性解释
#tickTime:
这个时间是作为 Zookeeper 服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是每个 tickTime 时间就会发送一个心跳。
#initLimit:
这个配置项是用来配置 Zookeeper 接受客户端(这里所说的客户端不是用户连接 Zookeeper 服务器的客户端,而是 Zookeeper 服务器集群中连接到 Leader 的 Follower 服务器)初始化连接时最长能忍受多少个心跳时间间隔数。当已经超过 5个心跳的时间(也就是 tickTime)长度后 Zookeeper 服务器还没有收到客户端的返回信息,那么表明这个客户端连接失败。总的时间长度就是 5*2000=10 秒
#syncLimit:
这个配置项标识 Leader 与Follower 之间发送消息,请求和应答时间长度,最长不能超过多少个 tickTime 的时间长度,总的时间长度就是5*2000=10秒
#dataDir:
快照日志的存储路径
#dataLogDir:
事物日志的存储路径,如果不配置这个那么事物日志会默认存储到dataDir制定的目录,这样会严重影响zk的性能,当zk吞吐量较大的时候,产生的事物日志、快照日志太多
#clientPort:
这个端口就是客户端连接 Zookeeper 服务器的端口,Zookeeper 会监听这个端口,接受客户端的访问请求。修改他的端口改大点
#server.1=192.168.31.135:2888:3888
这个1是服务器的标识也可以是其他的数字, 表示这个是第几号服务器,用来标识服务器,这个标识要写到快照目录下面myid文件里。192.168.31.135为集群里的IP地址,第一个端口是master和slave之间的通信端口,默认是2888,第二个端口是leader选举的端口,集群刚启动的时候选举或者leader挂掉之后进行新的选举的端口默认是3888
这个配置需要在每个zookeeper节点上配置,接下来在每个节点上分别创建myid文件
#server1
echo "1" > /opt/zookeeper/zkdata/myid
#server2
echo "2" > /opt/zookeeper/zkdata/myid
#server3
echo "3" > /opt/zookeeper/zkdata/myid
注意这里的1,2,3一定要和zoo.cfg配置文件中保持一致。
(4) . 日志配置
作为开发人员,日志对于我们来说重要性不言而喻了,一旦出现问题首先就要去查看日志。那么zookeeper的日志配置在哪呢。同样在conf下有个log4j.properties文件。内容如下
[root@centos1 conf]# cat log4j.properties
#
# ZooKeeper Logging Configuration
#
# Format is " (, )+
# DEFAULT: console appender only
#日志级别
log4j.rootLogger=INFO, CONSOLE
# Example with rolling log file
#log4j.rootLogger=DEBUG, CONSOLE, ROLLINGFILE
# Example with rolling log file and tracing
#log4j.rootLogger=TRACE, CONSOLE, ROLLINGFILE, TRACEFILE
#
# Log INFO level and above messages to the console
#
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Threshold=INFO
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} - %-5p [%t:%C{1}@%L] - %m%n
#
# Add ROLLINGFILE to rootLogger to get log file output
# Log DEBUG level and above messages to a log file
log4j.appender.ROLLINGFILE=org.apache.log4j.RollingFileAppender
log4j.appender.ROLLINGFILE.Threshold=DEBUG
log4j.appender.ROLLINGFILE.File=zookeeper.log
# Max log file size of 10MB
log4j.appender.ROLLINGFILE.MaxFileSize=10MB
# uncomment the next line to limit number of backup files
#log4j.appender.ROLLINGFILE.MaxBackupIndex=10
log4j.appender.ROLLINGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.ROLLINGFILE.layout.ConversionPattern=%d{ISO8601} - %-5p [%t:%C{1}@%L] - %m%n
#
# Add TRACEFILE to rootLogger to get log file output
# Log DEBUG level and above messages to a log file
log4j.appender.TRACEFILE=org.apache.log4j.FileAppender
log4j.appender.TRACEFILE.Threshold=TRACE
log4j.appender.TRACEFILE.File=zookeeper_trace.log
log4j.appender.TRACEFILE.layout=org.apache.log4j.PatternLayout
### Notice we are including log4j's NDC here (%x)
log4j.appender.TRACEFILE.layout.ConversionPattern=%d{ISO8601} - %-5p [%t:%C{1}@%L][%x] - %m%n
我们写代码时往往也会配置log4j ,因此会与zookeeper共用log4j的配置文件。所以需要修改这个配置文件,如下
# Define some default values that can be overridden by system properties
zookeeper.root.logger=INFO, CONSOLE #日志级别
zookeeper.console.threshold=INFO #使用下面的console来打印日志
zookeeper.log.dir=. #日志打印到哪里,就是在zoo.cfg中配置的路径
zookeeper.log.file=zookeeper.log
zookeeper.log.threshold=DEBUG
zookeeper.tracelog.dir=.
zookeeper.tracelog.file=zookeeper_trace.log
#
# ZooKeeper Logging Configuration
#
# Format is " (, )+
# DEFAULT: console appender only
#日志级别
#log4j.rootLogger=INFO, CONSOLE
log4j.rootLogger=${zookeeper.root.logger}
# Example with rolling log file
#log4j.rootLogger=DEBUG, CONSOLE, ROLLINGFILE
# Example with rolling log file and tracing
#log4j.rootLogger=TRACE, CONSOLE, ROLLINGFILE, TRACEFILE
#
# Log INFO level and above messages to the console
#
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Threshold=INFO
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} - %-5p [%t:%C{1}@%L] - %m%n
#
# Add ROLLINGFILE to rootLogger to get log file output
# Log DEBUG level and above messages to a log file
log4j.appender.ROLLINGFILE=org.apache.log4j.RollingFileAppender
log4j.appender.ROLLINGFILE.Threshold=DEBUG
log4j.appender.ROLLINGFILE.File=zookeeper.log
# Max log file size of 10MB
log4j.appender.ROLLINGFILE.MaxFileSize=10MB
# uncomment the next line to limit number of backup files
#log4j.appender.ROLLINGFILE.MaxBackupIndex=10
log4j.appender.ROLLINGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.ROLLINGFILE.layout.ConversionPattern=%d{ISO8601} - %-5p [%t:%C{1}@%L] - %m%n
#
# Add TRACEFILE to rootLogger to get log file output
# Log DEBUG level and above messages to a log file
log4j.appender.TRACEFILE=org.apache.log4j.FileAppender
log4j.appender.TRACEFILE.Threshold=TRACE
log4j.appender.TRACEFILE.File=zookeeper_trace.log
log4j.appender.TRACEFILE.layout=org.apache.log4j.PatternLayout
### Notice we are including log4j's NDC here (%x)
log4j.appender.TRACEFILE.layout.ConversionPattern=%d{ISO8601} - %-5p [%t:%C{1}@%L][%x] - %m%n
修改完成后,会发现日志文件输出目录不起作用。需修改zkEnv.sh 脚本,如下
[root@centos1 conf]# cat /user/zookeeper/zookeeper-3.3.6/bin/zkEnv.sh
#!/bin/sh
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This script should be sourced into other zookeeper
# scripts to setup the env variables
# We use ZOOCFGDIR if defined,
# otherwise we use /etc/zookeeper
# or the conf directory that is
# a sibling of this script's directory
if [ "x$ZOOCFGDIR" = "x" ]
then
if [ -d "/etc/zookeeper" ]
then
ZOOCFGDIR="/etc/zookeeper"
else
ZOOCFGDIR="$ZOOBINDIR/../conf"
fi
fi
if [ "x$ZOOCFG" = "x" ]
then
ZOOCFG="zoo.cfg"
fi
ZOOCFG="$ZOOCFGDIR/$ZOOCFG"
if [ -e "$ZOOCFGDIR/java.env" ]
then
. "$ZOOCFGDIR/java.env"
fi
if [ "x${ZOO_LOG_DIR}" = "x" ]
then
ZOO_LOG_DIR="."
fi
if [ "x${ZOO_LOG4J_PROP}" = "x" ]
then
ZOO_LOG4J_PROP="INFO,CONSOLE"
fi
if [ "$JAVA_HOME" != "" ]; then
JAVA="$JAVA_HOME/bin/java"
else
JAVA=java
fi
#add the zoocfg dir to classpath
CLASSPATH="$ZOOCFGDIR:$CLASSPATH"
for i in "$ZOOBINDIR"/../src/java/lib/*.jar
do
CLASSPATH="$i:$CLASSPATH"
done
#make it work in the release
for i in "$ZOOBINDIR"/../lib/*.jar
do
CLASSPATH="$i:$CLASSPATH"
done
#make it work in the release
for i in "$ZOOBINDIR"/../zookeeper-*.jar
do
CLASSPATH="$i:$CLASSPATH"
done
#make it work for developers
for d in "$ZOOBINDIR"/../build/lib/*.jar
do
CLASSPATH="$d:$CLASSPATH"
done
#make it work for developers
CLASSPATH="$ZOOBINDIR/../build/classes:$CLASSPATH"
case "`uname`" in
CYGWIN*) cygwin=true ;;
*) cygwin=false ;;
esac
if $cygwin
then
CLASSPATH=`cygpath -wp "$CLASSPATH"`
fi
#echo "CLASSPATH=$CLASSPATH"
找到配置ZOO_LOG_DIR以及ZOO_LOG4J_PROP的位置(大概在Line50,Line55) 。修改如下
if [ "x${ZOO_LOG_DIR}" = "x" ]
then
#配置zookeeper日志输出存放路径
ZOO_LOG_DIR="/home/xxx/zookeeper-2181/logs"
fi
if [ "x${ZOO_LOG4J_PROP}" = "x" ]
then
#配置日志输出级别,这里把几个级别一并配上
ZOO_LOG4J_PROP="INFO,CONSOLE,ROLLINGFILE,TRACEFILE"
fi
这样zookeeper的日志配置就完成了。
(5) . 启动Zookeeper
执行如下命令
#进入到Zookeeper的bin目录下
cd /user/zookeeper/zookeeper-3.3.6/bin
#启动服务(3台都需要操作)
./zkServer.sh start
查看服务状态
./zkServer.sh status
通过该命令能看到如下信息则表示zookeeper已经成功启动
#192.168.31.136
[root@centos2 bin]# ./zkServer.sh status
JMX enabled by default
Using config: /user/zookeeper/zookeeper-3.3.6/bin/../conf/zoo.cfg
Mode: leader
#192.168.31.135
[root@centos3 bin]# ./zkServer.sh status
JMX enabled by default
Using config: /user/zookeeper/zookeeper-3.3.6/bin/../conf/zoo.cfg
Mode: follower
(6) . 常见问题
执行 ./zkServer.sh start 命令未出现异常,但是在执行./zkServer.sh status 命令时报
./zkServer.sh status
It is probably not running
可去bin目录吗,执行cat zookeeper.out查看日志
(1) . 错误信息:没有找到主机路由
该错误是没有关闭防火墙导致的,关闭即可。CentOS中防火墙程序主要是firewall和iptables,CentOS7中firewall服务已经默认安装好了,而iptables服务需要安装,CentOS中 iptables通过控制端口来控制服务,而firewalld则是通过控制协议来控制端口 。命令如下:
yum install iptabes-services
firewall 相关的操作
#查看防火墙状态
firewall-cmd --state
#关闭防火墙
systemctl stop firewalld.service
#开启防火墙
systemctl start firewalld.service
#禁止开机启动启动防火墙
systemctl disable firewalld.service
iptables 相关的操作
#开启iptables防火墙
systemctl start iptables.service
#重启iptables防火墙
systemctl restart iptables.service
#关闭iptables防火墙
systemctl stop iptables.service
#查看iptables防火墙状态
systemctl status iptables.service
至此zookeeper安装启动全部完成,可以愉快的玩耍了。