Zookeeper实现哨兵机制

65 篇文章 0 订阅

master选举使用场景及结构
 现在很多时候我们的服务需要7*24小时工作,假如一台机器挂了,我们希望能有其它机器顶替它继续工作。此类问题现在多采用master-salve模式,也就是常说的主从模式,正常情况下主机提供服务,备机负责监听主机状态,当主机异常时,可以自动切换到备机继续提供服务(这里有点儿类似于数据库主库跟备库,备机正常情况下只监听,不工作),这个切换过程中选出下一个主机的过程就是master选举。
对于以上提到的场景,传统的解决方式是采用一个备用节点,这个备用节点定期给当前主节点发送ping包,主节点收到ping包后会向备用节点发送应答ack,当备用节点收到应答,就认为主节点还活着,让它继续提供服务,否则就认为主节点挂掉了,自己将开始行使主节点职责。如图1所示:

Maven依赖信息

<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>2.0.0.RELEASE</version>
</parent>
<dependencies>
	<dependency>
		<groupId>com.101tec</groupId>
		<artifactId>zkclient</artifactId>
		<version>0.10</version>
		<exclusions>
			<exclusion>
				<artifactId>slf4j-api</artifactId>
				<groupId>org.slf4j</groupId>
			</exclusion>
			<exclusion>
				<artifactId>log4j</artifactId>
				<groupId>log4j</groupId>
			</exclusion>
			<exclusion>
				<artifactId>slf4j-log4j12</artifactId>
				<groupId>org.slf4j</groupId>
			</exclusion>
		</exclusions>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
</dependencies>

IndexController

@RestController
public class IndexController {
	// 获取服务信息
	@RequestMapping("/getServerInfo")
	public String getServerInfo() {
		return ElectionMaster.isSurvival ? "当前服务器为主节点" : "当前服务器为从节点";
	}
}

MyApplicationRunner

@Component
public class MyApplicationRunner implements ApplicationRunner {

	// 创建zk连接
	ZkClient zkClient = new ZkClient("127.0.0.1:2181");
	private String path = "/election";
	@Value("${server.port}")
	private String serverPort;

	public void run(ApplicationArguments args) throws Exception {
		System.out.println("项目启动完成...");
		createEphemeral();
		// 创建事件监听
		zkClient.subscribeDataChanges(path, new IZkDataListener() {

		// 节点被删除
		public void handleDataDeleted(String dataPath) throws Exception {
			// 主节点已经挂了,重新选举
			System.out.println("主节点已经挂了,重新开始选举");
			createEphemeral();
		}

		public void handleDataChange(String dataPath, Object data) throws Exception {

		}
	});

}

	private void createEphemeral() {
		try {
			zkClient.createEphemeral(path, serverPort);
			ElectionMaster.isSurvival = true;
			System.out.println("serverPort:" + serverPort + ",选举成功....");
		} catch (Exception e) {
			ElectionMaster.isSurvival = false;
		}
	}

}

ElectionMaster

@Component
public class ElectionMaster {

	// 服务器info信息 是否存活
	public static boolean isSurvival;

}

Zookeeper实现分布式配置中心

什么是分布式配置中心
项目中配置文件比较繁杂,而且不同环境的不同配置修改相对频繁,每次发布都需要对应修改配置,如果配置出现错误,需要重新打包发布,时间成本较高,因此需要做统一的分布式注册中心,能做到自动更新配置文件信息,解决以上问题。

常用分布式配置中心框架
首选为disconf,可支持KV存储以及配置文件形式存储,使用和开发更为简便。并且本身也是基于zookpeer的分布式配置中心开发,方便部署使用,并且支持实时更新通知操作,但是部署相对复杂。
Diamond(daɪəmənd)基本可以放弃,一般做KV的存储配置项,做配置文件不是很好的选择。
Spring Cloud Config因为依赖git,使用局限性较大,需要在各个环境中安装git,并且不支持KV存储,功能方面略差于disconf
分布式配置中心实现原理

注册中心 配置存储 时效性 数据模型 维护性 优点 缺点
disconf zookpeer 实时推送 支持传统的配置文件模式,亦支持KV结构数据 提供界面操作 基于分布式的Zookeeper来实时推送稳定性、实效性、易用性上均优于其他 源码较多,阅读和使用起来相对较复杂
zookpeer zookpeer 实时推送 支持传统的配置文件模式,亦支持KV结构数 命令操作 实时推送稳定性、实效性 开发量大
diamond mysql 每隔15s拉一次全量数据 只支持KV结构的数据 提供界面操 简单、可靠、易用 数据模型不支持文件,使用不方便
Spring Cloud Config git 人工批量刷新 文件模式 git操作 简单、可靠、易用 需要依赖GIT,并且更新GIT

基于Zookeeper实现分布式配置中心
Maven依赖信息

<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>2.0.0.RELEASE</version>
</parent>
<dependencies>
	<dependency>
		<groupId>com.101tec</groupId>
		<artifactId>zkclient</artifactId>
		<version>0.10</version>
		<exclusions>
			<exclusion>
				<artifactId>slf4j-api</artifactId>
				<groupId>org.slf4j</groupId>
			</exclusion>
			<exclusion>
				<artifactId>log4j</artifactId>
				<groupId>log4j</groupId>
			</exclusion>
			<exclusion>
				<artifactId>slf4j-log4j12</artifactId>
				<groupId>org.slf4j</groupId>
			</exclusion>
		</exclusions>
	</dependency>
	<dependency>
		<groupId>org.projectlombok</groupId>
		<artifactId>lombok</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	<dependency>
		<groupId>org.apache.commons</groupId>
		<artifactId>commons-lang3</artifactId>
	</dependency>
</dependencies>

application.yml

test:
key: demo

ConfigUtils
读取配置文件信息

@Data
@Component
public class ConfigUtils {
	@Value("${test.key}")
	private String testKey;
}

MyApplicationRunner
项目启动创建zk节点

@Component
public class MyApplicationRunner extends BaseZookeeper implements ApplicationRunner {
	@Autowired
	private ConfigUtils configUtils;
	// 启动后执行方法
	public void run(ApplicationArguments args) throws Exception {
		System.out.println("项目启动成功...");
		String testValue = configUtils.getTestKey();
		String testKey = "/testKey";
		try {
			// 创建节点信息
			zkClient.createEphemeral(testKey, testValue);
		} catch (Exception e) {
			e.printStackTrace();
		}
		zkClient.subscribeDataChanges(testKey, new IZkDataListener() {
			public void handleDataDeleted(String dataPath) throws Exception {
			}
			// 当值发生变化的时候
			public void handleDataChange(String dataPath, Object data) throws Exception {
				System.out.println("dataPath:" + dataPath + ",data:" + data);
				final String strData = (String) data;
				configUtils.setTestKey(strData);
			}
		});

	}

}

UpdateInfoService
修改Zookeeper节点信息

@Service
public class UpdateInfoService extends BaseZookeeper {
	public String updateInfo(String key, String value) {
		try {
			zkClient.writeData("/" + key, value);
			return "success";
		} catch (Exception e) {
			return "fail";
		}
	}
}

IndexController
获取配置文件信息

@RestController
public class IndexController {
	@Autowired
	private ConfigUtils configUtils;
	@Autowired
	private UpdateInfoService updateInfoService;

	@RequestMapping("/getInfo")
	public String getInfo() {
		return configUtils.getTestKey();
	}

	@RequestMapping("/updateInfo")
	public String updateInfo(String key, String value) {
		String updateInfo = updateInfoService.updateInfo(key, value);
		return updateInfo;
	}
}

Zookeeper集群选举策略

Zookeeper集群选举原理
Zookeeper的角色

1.领导者(leader),负责进行投票的发起和决议,更新系统状态
2.学习者(learner),包括跟随者(follower)和观察者(observer),follower用于接受客户端请求并想客户端返回结果,在选主过程中参与投票
3.Observer可以接受客户端连接,将写请求转发给leader,但observer不参加投票过程,只同步leader的状态,observer的目的是为了扩展系统,提高读取速度
4.客户端(client),请求发起方Zookeeper的核心是原子广播,这个机制保证了各个Server之间的同步。实现这个机制的协议叫做Zab协议。Zab协议有两种模式,它们分别是恢复模式(选主)和广播模式(同步)。当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多数Server完成了和leader的状态同步以后,恢复模式就结束了。状态同步保证了leader和Server具有相同的系统状态。

为了保证事务的顺序一致性,zookeeper采用了递增的事务id号(zxid)来标识事务。所有的提议(proposal)都在被提出的时候加上了zxid。实现中zxid是一个64位的数字,它高32位是epoch用来标识leader关系是否改变,每次一个leader被选出来,它都会有一个新的epoch,标识当前属于那个leader的统治时期。低32位用于递增计数。

Zookeeper的读写机制
 » Zookeeper是一个主多个server组成的集群
 » 一个leader,多个follower
 » 每个server保存一份数据副本
 » 全局数据一致
 » 分布式读写
 » 更新请求转发,由leader实施
Zookeeper的保证
» 更新请求顺序进行,来自同一个client的更新请求按其发送顺序依次执行
» 数据更新原子性,一次数据更新要么成功,要么失败
» 全局唯一数据视图,client无论连接到哪个server,数据视图都是一致的
» 实时性,在一定事件范围内,client能读到最新数据

Zookeeper节点数据操作流程
Zookeeper leader 选举

Zookeeper leader 选举

半数通过
    – 3台机器 挂一台 2>3/2
    – 4台机器 挂2台 2!>4/2
 • A提案说,我要选自己,B你同意吗?C你同意吗?B说,我同意选A;C说,我同意选A。(注意,这里超过半数了,其实在现实世界选举已经成功了。

但是计算机世界是很严格,另外要理解算法,要继续模拟下去。)
  • 接着B提案说,我要选自己,A你同意吗;A说,我已经超半数同意当选,你的提案无效;C说,A已经超半数同意当选,B提案无效。
  • 接着C提案说,我要选自己,A你同意吗;A说,我已经超半数同意当选,你的提案无效;B说,A已经超半数同意当选,C的提案无效。
• 选举已经产生了Leader,后面的都是follower,只能服从Leader的命令。而且这里还有个小细节,就是其实谁先启动谁当头。

Zookeeper 集群环境搭建  
1.安装jdk运行jdk环境
上传jdk1.8安装包

2.安装jdk1.8环境变量

vi /etc/profile
export JAVA_HOME=/usr/local/jdk1.8.0_181
export ZOOKEEPER_HOME=/usr/local/zookeeper
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
export PATH=$JAVA_HOME/bin:$ZOOKEEPER_HOME/bin:$PATH

刷新profile文件
source /etc/profile

关闭防火墙

3.下载zookeeper安装包

wget https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/zookeeper-3.4.10/zookeeper-3.4.10.tar.gz

4.解压Zookeeper安装包

tar -zxvf zookeeper-3.4.10.tar.gz 

5.修改Zookeeper文件夹名称

重命名: mv zookeeper-3.4.10 zookeeper
  1. 修改zoo_sample.cfg文件

    cd /usr/local/zookeeper/conf
    mv zoo_sample.cfg zoo.cfg
    修改conf: vi zoo.cfg 修改两处
    (1) dataDir=/usr/local/zookeeper/data(注意同时在zookeeper创建data目录)
    (2)最后面添加
    server.0=192.168.212.154:2888:3888
    server.1=192.168.212.156:2888:3888
    server.2=192.168.212.157:2888:3888
    修改Zookeeper文件夹名称

  2. 创建服务器标识
    服务器标识配置:
    创建文件夹: mkdir data
    创建文件myid并填写内容为0: vi
    myid (内容为服务器标识 : 0)

复制zookeeper
进行复制zookeeper目录到hadoop01和hadoop02
还有/etc/profile文件
把hadoop01、 hadoop02中的myid文件里的值修改为1和2
路径(vi /usr/local/zookeeper/data/myid)
启动zookeeper
启动zookeeper:
路径: /usr/local/zookeeper/bin
执行: zkServer.sh start
(注意这里3台机器都要进行启动)
状态: zkServer.sh
status(在三个节点上检验zk的mode,一个leader和俩个follower)
常用命令
zkServer.sh status 查询状态

关闭所有防火墙
systemctl stop firewalld

/usr/local/jdk1.8.0_181

JAVA_HOME=/usr/local/jdk1.8.0_181
CLASSPATH=$JAVA_HOME/lib/
PATH=$PATH:$JAVA_HOME/bin
export JAVA_HOME CLASSPATH PATH

/etc/profile

export ZOOKEEPER_HOME=/usr/local/zookeeper
export PATH=$PATH:$ZOOKEEPER_HOME/bin:$ZOOKEEPER_HOME/conf
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值