Zookeeper选举master可以保证一台服务器执行,在项目中运用到的是在没有专门的定时任务服务时,由于一个服务会启多个节点这样就会导致节点之前去抢定时任务,从而造成数据的不一致性。
根据之前说的情况,我用了Zookeeper选举Master的原理来保证同一时间只有一个服务在执行服务中的定时任务。
下面是实现代码:
第一个类
package com.baibei.pay.configurer;
import lombok.extern.slf4j.Slf4j;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.curator.utils.CloseableUtils;
/**
-
@author hwq
-
@date 2018/11/20
*/
@Slf4j
public class ZooKeeperConnector {
private String hosts;
private CuratorFramework client;
private static final int DEFAULT_SESSION_TIMEOUT_MS = 30000;
private static final int DEFAULT_CONNECTION_TIMEOUT_MS = 10000;
private int sessionTimeout = 30000;
private int connectionTimeout = 10000;public ZooKeeperConnector() {
}public void connect() {
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
this.client = CuratorFrameworkFactory.newClient(this.hosts, this.sessionTimeout, this.connectionTimeout, retryPolicy);
this.client.start();
log.info("Successfully connected to Zookeeper [{}] ", this.hosts);
}public void close() {
CloseableUtils.closeQuietly(this.client);
}public String getHosts() {
return this.hosts;
}public void setHosts(String hosts) {
this.hosts = hosts;
}public CuratorFramework getClient() {
if (this.client == null) {
this.connect();
}return this.client;
}
public CuratorFramework reconnect() {
this.connect();
return this.client;
}public void setClient(CuratorFramework client) {
this.client = client;
}public int getSessionTimeout() {
return this.sessionTimeout;
}public void setSessionTimeout(int sessionTimeout) {
this.sessionTimeout = sessionTimeout;
}public int getConnectionTimeout() {
return this.connectionTimeout;
}public void setConnectionTimeout(int connectionTimeout) {
this.connectionTimeout = connectionTimeout;
}
}
package com.baibei.pay.configurer;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
第二个类
/**
-
定时任务master节点判断配置
-
@author wenqing
*/
@Data
@Configuration
public class SchedulerMasterConfig {@Value("${ZOOKEEPER.CONNECTION.HOSTS}")
private String zk;@Value("${ZOOKEEPER.MASTER.ZKSESSIONTIMEOUT}")
private int zkSessionTimeout;@Value("${BUSINESS.TYPE}")
private String businessType;
}
核心类
package com.baibei.pay.configurer;
import lombok.extern.slf4j.Slf4j;
import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.ZkClient;
import org.I0Itec.zkclient.exception.ZkNodeExistsException;
import org.I0Itec.zkclient.serialize.SerializableSerializer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.UUID;
/**
-
@author hwq
-
@date 2018/11/20
-
定时任务使用ZK限制单点执行,只有master节点才执行定时任务
-
每个节点有不同的serverID,第1个把自己serverID写到ZK node上的为master,当某个节点down掉后,其他节点争抢master
*/
@Slf4j
@Service
public class SchedulerMasterCheck implements IZkDataListener {@Autowired
SchedulerMasterConfig schedulerMasterConfig;String MASTER_NODE = “/paymaster_”;
//ZK客户端
private ZkClient zk = null;//临时serverID
private String serverId = UUID.randomUUID().toString().toLowerCase();public void init(){
MASTER_NODE += schedulerMasterConfig.getBusinessType();
zk = new ZkClient(schedulerMasterConfig.getZk(), schedulerMasterConfig.getZkSessionTimeout(), schedulerMasterConfig.getZkSessionTimeout(), new SerializableSerializer());
log.info(“Temp serverId = {}”, serverId);
takeMaster();
zk.subscribeDataChanges(MASTER_NODE, this);
}//抢master
private void takeMaster(){
try{
zk.createEphemeral(MASTER_NODE, serverId);
log.info(“serverId {} take master SUCCESS”, serverId);
}catch(ZkNodeExistsException e){//节点已存在
log.info(“serverId {} take master FAILURE”, serverId);
if(zk.readData(MASTER_NODE) == null){//在读取过程中,发现master节点已经被释放
takeMaster();
}
}
}public boolean isMaster(){
try{
String data = zk.readData(MASTER_NODE);
if(serverId.equalsIgnoreCase(data)){
return true;
}
}catch(Exception e){
log.error(e.getMessage());
}
return false;
}@Override
public void handleDataChange(String s, Object o) throws Exception {}
@Override
public void handleDataDeleted(String s) throws Exception {
log.info(“Old Master is down, Take master…”);
takeMaster();
}
}
配置文件
应用
@Autowired
private SchedulerMasterCheck schedulerMasterCheck;
/**
* 五分钟执行一次
*/
@Scheduled(cron = “0 0/5 * * * ?”)
public void doUpQueryOrder() {
if (schedulerMasterCheck.isMaster()) {
log.info(“现在我在执行”);
queryOrder();
// payService.tetsZookeeper();
}
}