Zookeeper 生成分布式唯一ID

296 阅读1分钟
  • 在过去的单库单表型系统中,通常可以使用数据库字段自带的auto_increment 属性来自动为每条记录生成一个唯一的ID。但是分库分表后,就无法在依靠数据库的 auto_increment属性来唯一标识一条记录了。此时我们就可以用zookeeper在分布式环 境下生成全局唯一ID
  • 设计思路
  1. 连接zookeeper服务器
  2. 指定路径生成持久有序节点
  3. 取序列号及为分布式环境下的唯一ID

1. 实验

public class GloballyUniqueId implements Watcher, Closeable {
  private String ip = null;  // ip及端口
  private static Integer timeOut = 5000;  // 超时时间,毫秒为单位
  private final static CountDownLatch countDownLatch = new CountDownLatch(1);
  private final static Logger log = Logger.getLogger(GloballyUniqueId.class);
  private ZooKeeper zooKeeper = null;
  private String parentPath = "/uniqueId";

  private GloballyUniqueId() {
  }

  public GloballyUniqueId(String ip) {
    this(ip, timeOut);
  }

  public GloballyUniqueId(String ip, Integer timeOut) {
    this.ip = ip;
    GloballyUniqueId.timeOut = timeOut;
    initZK(ip, timeOut);
  }

  public GloballyUniqueId setParentPath(String parentPath) {
    this.parentPath = parentPath;
    return this;
  }

  /**
   * 初始化zookeeper
   *
   * @param ip
   * @param timeOut
   * @throws IOException
   * @throws InterruptedException
   */
  private void initZK(String ip, Integer timeOut) {
    try {
      zooKeeper = new ZooKeeper(ip, timeOut, this);
      countDownLatch.await();
    } catch (IOException | InterruptedException e) {
      log.error("初始化zookeeper失败");
      e.printStackTrace();
    }
  }

  @Override
  public void process(WatchedEvent watchedEvent) {
    if (watchedEvent.getType() == Event.EventType.None) {
      switch (watchedEvent.getState()) {
        case SyncConnected:
          log.info("连接 " + ip + "成功");
          countDownLatch.countDown();
          break;
        case Disconnected:
          log.error("连接 " + ip + "已断开");
          break;
        case Expired:
          log.error("连接 " + ip + "已超时,需要重新连接服务器端");
          this.initZK(ip, timeOut);
          break;
        case AuthFailed:
          log.error("身份验证失败");
          break;
      }
    }
  }

  /**
   * 重点!!!用于通过持久顺序节点,生成新的ID,并返回
   * @return
   * @throws KeeperException
   * @throws InterruptedException
   */
  public String getUniqueID() throws KeeperException, InterruptedException {
    if (zooKeeper.exists(parentPath, null) == null) {
      zooKeeper.create(parentPath, "0".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
    }
    String path = zooKeeper.create(parentPath + parentPath, "0".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL);
    return path.substring(2 * parentPath.length() + 1);
  }

  @Override
  public void close() {
    try {
      zooKeeper.close();
      log.info("zooKeeper已关闭");
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}
public class GloballyUniqueIdTest {
  private final static String IP = "192.168.233.133:2181";  // ip及端口

  public static void main(String[] args) {
    ExecutorService threadPool = Executors.newCachedThreadPool();
    for (int i = 0; i < 4; i++) {
      threadPool.execute(new ProductId());
    }
    threadPool.shutdown();
  }

  @Slf4j
  static class ProductId implements Runnable {
    @Override
    public void run() {
      GloballyUniqueId globallyUniqueId = new GloballyUniqueId(IP);
      for (int i = 0; i < 30; i++) {
        try {
          String id = globallyUniqueId.getUniqueID();
          log.info(id);
          //Thread.sleep(1000);
        } catch (KeeperException | InterruptedException e) {
          e.printStackTrace();
        }
      }
      globallyUniqueId.close();
    }
  }
}