/* * (C) 2007-2012 Alibaba Group Holding Limited. * * Licensed 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. * Authors: * wuhua <wq163@163.com> , boyan <killme2008@gmail.com> */ package com.taobao.metamorphosis.server; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.IOException; import java.util.Properties; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.I0Itec.zkclient.IZkStateListener; import org.I0Itec.zkclient.ZkClient; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.zookeeper.Watcher.Event.KeeperState; import com.taobao.gecko.core.util.ConcurrentHashSet; import com.taobao.metamorphosis.cluster.Broker; import com.taobao.metamorphosis.cluster.json.TopicBroker; import com.taobao.metamorphosis.network.RemotingUtils; import com.taobao.metamorphosis.server.utils.MetaConfig; import com.taobao.metamorphosis.server.utils.TopicConfig; import com.taobao.metamorphosis.utils.MetaZookeeper; import com.taobao.metamorphosis.utils.Utils; import com.taobao.metamorphosis.utils.ZkUtils; import com.taobao.metamorphosis.utils.ZkUtils.ZKConfig; /** * Broker��ZK������ע��broker��topic�� * * @author boyan * @Date 2011-4-25 * @author wuhua * @Date 2011-6-24 * */ public class BrokerZooKeeper implements PropertyChangeListener { private final MetaConfig config; private ZkClient zkClient; private ZKConfig zkConfig; private String brokerIdPath; private final String masterConfigChecksumPath; private final Set<String> topics = new ConcurrentHashSet<String>(); private final ConcurrentHashMap<String, TopicConfig> cloneTopicConfigs = new ConcurrentHashMap<String, TopicConfig>(); static final Log log = LogFactory.getLog(BrokerZooKeeper.class); // private DiamondManager diamondManager; private volatile boolean registerBrokerInZkFail = false; private MetaZookeeper metaZookeeper; public ZkClient getZkClient() { return this.zkClient; } @Override public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName().equals("configFileChecksum") && !this.config.isSlave()) { try { ZkUtils.updateEphemeralPath(this.zkClient, this.masterConfigChecksumPath, String.valueOf(this.config.getConfigFileChecksum())); } catch (Exception e) { log.error("Update master config file checksum to zk failed", e); } } } MetaConfig getConfig() { return this.config; } public MetaZookeeper getMetaZookeeper() { return this.metaZookeeper; } public BrokerZooKeeper(final MetaConfig metaConfig) { this.config = metaConfig; this.zkConfig = metaConfig.getZkConfig(); if (this.zkConfig == null) { this.zkConfig = this.loadZkConfigFromDiamond(); } this.start(this.zkConfig); this.resetBrokerIdPath(); this.masterConfigChecksumPath = this.metaZookeeper.masterConfigChecksum(this.config.getBrokerId()); this.config.addPropertyChangeListener("configFileChecksum", this); } /** * ��ʱ��zk.properties�����.Ϊ�˷��㵥Ԫ���� * * @return */ private ZKConfig loadZkConfigFromDiamond() { Properties properties; try { properties = Utils.getResourceAsProperties("server.ini", "GBK"); final ZKConfig zkConfig = new ZKConfig(); if (StringUtils.isNotBlank(properties.getProperty("zk.zkConnect"))) { zkConfig.zkConnect = properties.getProperty("zk.zkConnect"); } if (StringUtils.isNotBlank(properties.getProperty("zk.zkSessionTimeoutMs"))) { zkConfig.zkSessionTimeoutMs = Integer.parseInt(properties.getProperty("zk.zkSessionTimeoutMs")); } if (StringUtils.isNotBlank(properties.getProperty("zk.zkConnectionTimeoutMs"))) { zkConfig.zkConnectionTimeoutMs = Integer.parseInt(properties.getProperty("zk.zkConnectionTimeoutMs")); } if (StringUtils.isNotBlank(properties.getProperty("zk.zkSyncTimeMs"))) { zkConfig.zkSyncTimeMs = Integer.parseInt(properties.getProperty("zk.zkSyncTimeMs")); } return zkConfig; } catch (final IOException e) { log.error("zk����ʧ��", e); return null; } // ���Դ�diamond��ȡ // this.diamondManager = // new DefaultDiamondManager(this.config.getDiamondZKGroup(), // this.config.getDiamondZKDataId(), // new ManagerListener() { // @Override // public void receiveConfigInfo(final String configInfo) { // log.info("Receiving new diamond zk config:" + configInfo); // log.info("Closing zk client"); // try { // BrokerZooKeeper.this.unregisterBrokerInZk(); // BrokerZooKeeper.this.unregisterTopics(); // BrokerZooKeeper.this.zkClient.close(); // final Properties properties = new Properties(); // properties.load(new StringReader(configInfo)); // final ZKConfig zkConfig = DiamondUtils.getZkConfig(properties); // Thread.sleep(zkConfig.zkSyncTimeMs); // BrokerZooKeeper.this.start(zkConfig); // BrokerZooKeeper.this.reRegisterEveryThing(); // log.info("Process new diamond zk config successfully"); // } // catch (final Exception e) { // log.error("��diamond����zk����ʧ��", e); // } // // } // // // @Override // public Executor getExecutor() { // return null; // } // }); // return null;// DiamondUtils.getZkConfig(this.diamondManager, 10000); } private void start(final ZKConfig zkConfig) { log.info("Initialize zk client..."); this.zkClient = new ZkClient(zkConfig.zkConnect, zkConfig.zkSessionTimeoutMs, zkConfig.zkConnectionTimeoutMs, new ZkUtils.StringSerializer()); this.zkClient.subscribeStateChanges(new SessionExpireListener()); this.metaZookeeper = new MetaZookeeper(this.zkClient, zkConfig.zkRoot); } /** * ע��broker��zk * * @throws Exception */ public void registerBrokerInZk() throws Exception { if (!this.zkConfig.zkEnable) { return; } try { log.info("Registering broker " + this.brokerIdPath); final Broker broker = this.getBroker(); ZkUtils.createEphemeralPath(this.zkClient, this.brokerIdPath, broker.getZKString()); // �����Ͽͻ��ˣ���ʱ���� if (!this.config.isSlave()) { ZkUtils.updateEphemeralPath(this.zkClient, this.metaZookeeper.brokerIdsPath + "/" + this.config.getBrokerId(), broker.getZKString()); log.info("register for old client version " + this.metaZookeeper.brokerIdsPath + "/" + this.config.getBrokerId() + " succeeded with " + broker); } log.info("Registering broker " + this.brokerIdPath + " succeeded with " + broker); this.registerBrokerInZkFail = false; } catch (final Exception e) { this.registerBrokerInZkFail = true; log.error("ע��brokerʧ��"); throw e; } } // cache it. private Broker broker = null; public Broker getBroker() throws Exception { if (this.broker != null) { return this.broker; } else { final String hostName = this.getBrokerHostName(); this.broker = new Broker(this.config.getBrokerId(), hostName, this.config.getServerPort(), this.config.getSlaveId()); return this.broker; } } public String getBrokerString() { if (this.broker != null) { return this.broker.toString(); } else { try { return this.getBroker().toString(); } catch (Exception e) { return null; } } } public String getBrokerHostName() throws Exception { final String hostName = this.config.getHostName() == null ? RemotingUtils.getLocalHost() : this.config.getHostName(); return hostName; } public void registerMasterConfigFileChecksumInZk() throws Exception { if (!this.zkConfig.zkEnable) { return; } try { if (!this.config.isSlave()) { ZkUtils.createEphemeralPath(this.zkClient, this.masterConfigChecksumPath, String.valueOf(this.config.getConfigFileChecksum())); } } catch (final Exception e) { this.registerBrokerInZkFail = true; log.error("ע��brokerʧ��"); throw e; } } private void unregisterBrokerInZk() throws Exception { if (this.registerBrokerInZkFail) { log.warn("�ϴ�ע��brokerδ�ɹ�,����Ҫunregister"); return; } ZkUtils.deletePath(this.zkClient, this.brokerIdPath); if (!this.config.isSlave()) { ZkUtils.deletePath(this.zkClient, this.masterConfigChecksumPath); } // �����Ͽͻ��ˣ���ʱ����. if (!this.config.isSlave()) { try { ZkUtils.deletePath(this.zkClient, this.metaZookeeper.brokerIdsPath + "/" + this.config.getBrokerId()); } catch (final Exception e) { // ��slaveʱ��ɾ������,д����ֵ��ȥ ZkUtils.updateEphemeralPath(this.zkClient, this.metaZookeeper.brokerIdsPath + "/" + this.config.getBrokerId(), ""); } log.info("delete broker of old client version " + this.metaZookeeper.brokerIdsPath + "/" + this.config.getBrokerId()); } } private void unregisterTopics() throws Exception { for (final String topic : BrokerZooKeeper.this.topics) { this.unregisterTopic(topic); } } public void unregisterEveryThing() { try { this.unregisterBrokerInZk(); this.unregisterTopics(); } catch (Exception e) { log.error("Unregister broker failed", e); } } private void unregisterTopic(final String topic) { try { int brokerId = this.config.getBrokerId(); final String brokerTopicPath = this.metaZookeeper.brokerTopicsPathOf(topic, brokerId, this.config.getSlaveId()); final String topicPubPath = this.metaZookeeper.brokerTopicsPathOf(topic, true, brokerId, this.config.getSlaveId()); final String topicSubPath = this.metaZookeeper.brokerTopicsPathOf(topic, false, brokerId, this.config.getSlaveId()); // Be compatible with the version before 1.4.3 ZkUtils.deletePath(this.zkClient, brokerTopicPath); // added by dennis,since 1.4.3 ZkUtils.deletePath(this.zkClient, topicPubPath); ZkUtils.deletePath(this.zkClient, topicSubPath); } catch (Exception e) { log.error("Unregister topic " + topic + " failed,but don't worry about it.", e); } } /** * ע��topic�ͷ�����Ϣ��zk * * @param topic * @param force * TODO * @throws Exception */ public void registerTopicInZk(final String topic, boolean force) throws Exception { if (force) { // This block is not synchronized,because we don't force to register // topics frequently except reloading config file. TopicConfig oldConfig = this.cloneTopicConfigs.get(topic); TopicConfig newConfig = this.config.getTopicConfig(topic); if (this.compareTopicConfigs(newConfig, oldConfig)) { return; } else { this.unregisterTopic(topic); this.topics.add(topic); this.registerTopicInZkInternal(topic); } } else { if (!this.topics.add(topic)) { return; } else { this.registerTopicInZkInternal(topic); } } } private boolean compareTopicConfigs(TopicConfig c1, TopicConfig c2) { if (c1 == null && c2 != null) { return false; } if (c2 == null && c1 != null) { return false; } // If old and new configs are all null,we have to register it. if (c1 == null && c2 == null) { return false; } return c1.equals(c2); } public ZKConfig getZkConfig() { return this.zkConfig; } public Set<String> getTopics() { return this.topics; } public void reRegisterEveryThing() throws Exception { log.info("re-registering broker info in ZK for broker " + BrokerZooKeeper.this.config.getBrokerId()); BrokerZooKeeper.this.registerBrokerInZk(); log.info("re-registering broker topics in ZK for broker " + BrokerZooKeeper.this.config.getBrokerId()); for (final String topic : BrokerZooKeeper.this.topics) { BrokerZooKeeper.this.registerTopicInZkInternal(topic); } this.registerMasterConfigFileChecksumInZk(); log.info("done re-registering broker"); } private void registerTopicInZkInternal(final String topic) throws Exception { if (!this.zkConfig.zkEnable) { log.warn("zkEnable is false,so we don't talk to zookeeper."); return; } int brokerId = this.config.getBrokerId(); int slaveId = this.config.getSlaveId(); final String brokerTopicPath = this.metaZookeeper.brokerTopicsPathOf(topic, brokerId, slaveId); final String topicPubPath = this.metaZookeeper.brokerTopicsPathOf(topic, true, brokerId, slaveId); final String topicSubPath = this.metaZookeeper.brokerTopicsPathOf(topic, false, brokerId, slaveId); final TopicConfig topicConfig = this.config.getTopicConfig(topic); Integer numParts = topicConfig != null ? topicConfig.getNumPartitions() : this.config.getNumPartitions(); numParts = numParts == null ? this.config.getNumPartitions() : numParts; log.info("Begin registering broker topic " + brokerTopicPath + " with " + numParts + " partitions"); final TopicBroker topicBroker = new TopicBroker(numParts, brokerId + (slaveId >= 0 ? "-s" + slaveId : "-m")); log.info("Register broker for topic:" + topicBroker); // Be compatible with the version before 1.4.3 ZkUtils.createEphemeralPath(this.zkClient, brokerTopicPath, String.valueOf(numParts)); // added by dennis,since 1.4.3 String topicBrokerJson = topicBroker.toJson(); if (topicConfig.isAcceptPublish()) { ZkUtils.createEphemeralPath(this.zkClient, topicPubPath, topicBrokerJson); } else { ZkUtils.deletePath(this.zkClient, topicPubPath); } if (topicConfig.isAcceptSubscribe()) { ZkUtils.createEphemeralPath(this.zkClient, topicSubPath, topicBrokerJson); } else { ZkUtils.deletePath(this.zkClient, topicSubPath); } this.cloneTopicConfigs.put(topic, topicConfig.clone()); log.info("End registering broker topic " + brokerTopicPath); } public void close(boolean unregister) { try { if (unregister && this.zkConfig.zkEnable) { this.unregisterBrokerInZk(); this.unregisterTopics(); } } catch (final Exception e) { log.warn("error on unregisterBrokerInZk", e); } finally { if (this.zkClient != null) { log.info("Closing zk client..."); this.zkClient.close(); } } } private class SessionExpireListener implements IZkStateListener { @Override public void handleNewSession() throws Exception { BrokerZooKeeper.this.reRegisterEveryThing(); } @Override public void handleStateChanged(final KeeperState state) throws Exception { // do nothing, since zkclient will do reconnect for us. } } /** * ���¼���brokerIdPath */ public void resetBrokerIdPath() { this.brokerIdPath = this.metaZookeeper.brokerIdsPathOf(this.config.getBrokerId(), this.config.getSlaveId()); } }