All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.leeyazhou.crpc.registry.zookeeper.ZookeeperRegistry Maven / Gradle / Ivy

The newest version!
/**
 * Copyright © 2016~2020 CRPC ([email protected])
 *
 * 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.
 */
/**
 * 
 */

package com.github.leeyazhou.crpc.registry.zookeeper;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import com.github.leeyazhou.crpc.registry.support.FailbackRegistry;
import org.I0Itec.zkclient.IZkChildListener;
import org.I0Itec.zkclient.IZkStateListener;
import org.I0Itec.zkclient.ZkClient;
import org.apache.zookeeper.Watcher.Event.KeeperState;

import com.github.leeyazhou.crpc.core.Constants;
import com.github.leeyazhou.crpc.core.URL;
import com.github.leeyazhou.crpc.core.logger.Logger;
import com.github.leeyazhou.crpc.core.logger.LoggerFactory;
import com.github.leeyazhou.crpc.core.util.URLUtil;

/**
 * @author leeyazhou
 */
public class ZookeeperRegistry extends FailbackRegistry {
  private static final Logger logger = LoggerFactory.getLogger(ZookeeperRegistry.class);

  // ConcurrentMap<服务接口, 服务提供者>
  private static final ConcurrentMap> providersMap = new ConcurrentHashMap>();
  private static final ConcurrentMap hasListener = new ConcurrentHashMap();
  private IZkChildListener childListener = null;
  private KeeperState state;
  private ZkClient zkClient;

  /**
   * 
   */
  public ZookeeperRegistry(URL registryURL) {
    super(registryURL);
    connect(registryURL);
    createPath(root, false);
  }

  /**
   * 路径添加监听
   * 
   * @param path 监听路径
   */
  private void addPathListener(String path) {
    int num = this.zkClient.countChildren(path);
    if (num > 0) {
      List servicePaths = this.zkClient.getChildren(path);
      if (servicePaths != null && !servicePaths.isEmpty()) {
        for (String p : servicePaths) {
          String temp = path + Constants.FILE_SEPARATOR + p;
          addPathListener(temp);
        }
      }
    }

    if (hasListener.containsKey(path)) {
      return;
    }
    hasListener.putIfAbsent(path, Boolean.TRUE);
    if (childListener == null) {
      childListener = createChildListener();
    }
    this.zkClient.subscribeChildChanges(path, childListener);
  }

  @Override
  public void doRegister(URL registryURL) {
    // example : /crpc/demoService/providers/
    String providersPath = toCategoryPath(registryURL);
    providersPath += Constants.FILE_SEPARATOR + URLUtil.encode(registryURL.getProviderPath());
    if (logger.isDebugEnabled()) {
      logger.debug("zookeeper register path : " + providersPath);
    }
    createPath(providersPath, true);
  }

  private String toServicePath(URL url) {
    return root + Constants.FILE_SEPARATOR + url.getParameter(Constants.SERVICE_INTERFACE, "");
  }

  /**
   * example : /crpc/demoService/providers/
   * 
   * @param url URL
   * @return Str
   */
  private String toCategoryPath(URL url) {
    return toServicePath(url) + Constants.FILE_SEPARATOR + "providers";
  }

  @Override
  public void close() {
    zkClient.close();
  }

  /**
   * 创建路径
   * 
   * @param path 路径
   * @param ephemeral 临时节点
   */
  private synchronized void createPath(String path, boolean ephemeral) {
    int ii = path.lastIndexOf('/');
    if (ii > 0) {
      createPath(path.substring(0, ii), false);
    }
    if (!zkClient.exists(path)) {
      if (ephemeral) {
        zkClient.createEphemeral(path);
      } else {
        zkClient.createPersistent(path);
      }
    }
    addPathListener(path);
  }

  @Override
  public void unregister(URL registryURL) {
    zkClient.delete(toServicePath(registryURL));
  }

  @Override
  public List doGetProviders(URL registryURL) {
    List servicePathList = Collections.emptyList();
    String serviceInterface1 = null;
    if (registryURL == null) {
      servicePathList = zkClient.getChildren(root);
    } else {
      serviceInterface1 = registryURL.getParameter(Constants.SERVICE_INTERFACE, null);
      if (serviceInterface1 != null) {
        servicePathList.add(serviceInterface1);
      }
    }

    for (String serviceInterfaceItem : servicePathList) {
      String servicePath = root + Constants.FILE_SEPARATOR + serviceInterfaceItem;
      String path = servicePath + Constants.FILE_SEPARATOR + "providers";
      if (zkClient.exists(path)) {
        List children = zkClient.getChildren(path);
        for (String child : children) {
          child = URLUtil.decode(child);
          URL temp = URL.valueOf(child);
          String serviceInterface = temp.getParameter(Constants.SERVICE_INTERFACE, null);
          Map serviceList = providersMap.get(serviceInterface);
          if (serviceList == null) {
            providersMap.putIfAbsent(serviceInterface, new HashMap());
            serviceList = providersMap.get(serviceInterface);
          }
          serviceList.put(temp.getHost() + ":" + temp.getPort(), temp);
          path = servicePath + Constants.FILE_SEPARATOR + "consumers" + Constants.FILE_SEPARATOR
              + URLUtil.encode(temp.getConsumerPath());
          createPath(path, true);
        }
      }
    }
    return doGetProviders(registryURL, serviceInterface1);
  }

  private synchronized List doGetProviders(URL registryConfig, String serviceInterface) {
    List result = new ArrayList();
    if (registryConfig == null) {
      for (Map.Entry> entry : providersMap.entrySet()) {
        Iterator it = entry.getValue().values().iterator();
        while (it.hasNext()) {
          result.add(it.next());
        }
      }
    } else {
      Map temp = providersMap.get(serviceInterface);
      if (temp != null && !temp.isEmpty()) {
        Iterator it = temp.values().iterator();
        while (it.hasNext()) {
          result.add(it.next());
        }
      }
    }
    return result;
  }

  @Override
  public boolean isAvailable() {
    if (zkClient != null && state == KeeperState.SyncConnected) {
      return true;
    }
    return false;
  }

  /**
   * 服务列表监听
   * 
   * @return {@link IZkChildListener}
   */
  private IZkChildListener createChildListener() {
    return new IZkChildListener() {

      @Override
      public void handleChildChange(String parentPath, List currentChilds) throws Exception {
        logger.info("handleChildChange, parentPath : " + parentPath + ", currentChilds :  " + currentChilds);
        if (currentChilds == null || currentChilds.isEmpty()) {
          return;
        }
        if (parentPath.endsWith(Constants.DEFAULT_CATEGORY_PROVIDER)) {
          List localProviders = null;
          // need notify
          List notifyUrls = new ArrayList(currentChilds.size());
          for (String urlStr : currentChilds) {
            URL providerURL = URL.valueOf(urlStr);
            if (localProviders == null) {
              String serviceInterface = providerURL.getParameter(Constants.SERVICE_INTERFACE, null);
              // 获取本地缓存
              localProviders = doGetProviders(providerURL, serviceInterface);
            }
            if (localProviders == null || localProviders.isEmpty() || !localProviders.contains(providerURL)) {
              notifyUrls.add(providerURL);
            }
          }
          notifyListener(notifyUrls);
        }

        getProviders(null);// 更新本地缓存
      }

    };
  }

  @Override
  protected void connect(URL registryConfig) {
    try {
      this.zkClient = new ZkClient(getRegistryURL().getHost() + ":" + getRegistryURL().getPort(), 3000, Integer.MAX_VALUE);
      state = KeeperState.SyncConnected;
    } catch (Exception err) {
      logger.error("ZooKeeper connect fail", err);
    }

    zkClient.subscribeStateChanges(new IZkStateListener() {

      @Override
      public void handleStateChanged(KeeperState state) throws Exception {
        changeState(state);
      }

      @Override
      public void handleSessionEstablishmentError(Throwable error) throws Exception {
        changeState(KeeperState.Disconnected);
      }

      @Override
      public void handleNewSession() throws Exception {
        changeState(state = KeeperState.SyncConnected);
      }
    });
  }

  private void changeState(KeeperState state) {
    logger.info("ZooKeeper state is changed from [" + ZookeeperRegistry.this.state + "] to [" + state + "], registryURL : "
        + getRegistryURL());
    ZookeeperRegistry.this.state = state;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy