OpenTSDB源码分析之TSDB-UID表操作(修改)

为了方便研究将tsdb-uid表的修改操作抽取出来,代码如下:

package net.opentsdb.tools;

import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.ConcurrentHashMap;

import net.opentsdb.core.TSDB;
import net.opentsdb.uid.NoSuchUniqueName;
import net.opentsdb.utils.Config;

import org.hbase.async.DeleteRequest;
import org.hbase.async.GetRequest;
import org.hbase.async.HBaseClient;
import org.hbase.async.HBaseException;
import org.hbase.async.KeyValue;
import org.hbase.async.PutRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.stumbleupon.async.Callback;
import com.stumbleupon.async.Deferred;

public class TestTsdbuidModify {

	private static final Logger LOG = LoggerFactory.getLogger(TestTsdbuidModify.class);

	private static final Charset CHARSET = Charset.forName("ISO-8859-1");
	private static final byte[] ID_FAMILY = toBytes("id");
	private static final byte[] NAME_FAMILY = toBytes("name");
	private static final short MAX_ATTEMPTS_PUT = 6;
	private static final short INITIAL_EXP_BACKOFF_DELAY = 800;

	private final HBaseClient client;
	private final byte[] table;
	private final byte[] kind;
	private final short id_width;

	private final ConcurrentHashMap name_cache = new ConcurrentHashMap();
	private final ConcurrentHashMap id_cache = new ConcurrentHashMap();

	private volatile int cache_hits;
	private volatile int cache_misses;

	public TestTsdbuidModify(final HBaseClient client, final byte[] table, final String kind, final int width) {
		this.client = client;
		this.table = table;
		if (kind.isEmpty()) {
			throw new IllegalArgumentException("Empty string as 'kind' argument!");
		}
		this.kind = toBytes(kind);
		if (width < 1 || width > 8) {
			throw new IllegalArgumentException("Invalid width: " + width);
		}
		this.id_width = (short) width;
	}

	public static void main(String[] args) throws Exception {
		args = new String[] { "rename", "metrics", "a1", "m1" };

		ArgP argp = new ArgP();
		CliOptions.addCommon(argp);
		CliOptions.addVerbose(argp);
		argp.addOption("--idwidth", "N", "Number of bytes on which the UniqueId is encoded.");
		argp.addOption("--ignore-case", "Ignore case distinctions when matching a regexp.");
		argp.addOption("-i", "Short for --ignore-case.");
		args = CliOptions.parse(argp, args);

		final byte[] table = argp.get("--uidtable", "tsdb-uid").getBytes();
		final short idwidth = (argp.has("--idwidth") ? Short.parseShort(argp.get("--idwidth")) : 3);
		if (idwidth <= 0) {
			System.exit(3);
		}
		final boolean ignorecase = argp.has("--ignore-case") || argp.has("-i");

		Config config = CliOptions.getConfig(argp);
		final TSDB tsdb = new TSDB(config);

		tsdb.getClient().ensureTableExists(config.getString("tsd.storage.hbase.uid_table")).joinUninterruptibly();
		argp = null;
		int rc;
		try {
			rc = runCommand(tsdb, table, idwidth, ignorecase, args);
		} finally {
			try {
				tsdb.getClient().shutdown().joinUninterruptibly();
				LOG.info("Gracefully shutdown the TSD");
			} catch (Exception e) {
				LOG.error("Unexpected exception while shutting down", e);
				rc = 42;
			}
		}
		System.exit(rc);
	}

	private static int runCommand(final TSDB tsdb, final byte[] table, final short idwidth, final boolean ignorecase, final String[] args) {
		/**
		 * 新增 assign metrics t1
		 */
		if (args[0].equals("assign")) {

		}

		/**
		 * 查找 grep t grep metrics t11
		 */
		if (args[0].equals("grep")) {
		}

		/**
		 * 修改
		 */
		if (args[0].equals("rename")) {
			return rename(tsdb.getClient(), table, idwidth, args);
		}

		/**
		 * 删除
		 */
		if (args[0].equals("delete")) {
		}
		return 0;
	}

	private static int rename(final HBaseClient client, final byte[] table, final short idwidth, final String[] args) {
		final String kind = args[1];
		final String oldname = args[2];
		final String newname = args[3];
		final TestTsdbuidModify uid = new TestTsdbuidModify(client, table, kind, (int) idwidth);
		try {
			uid.rename(oldname, newname);
		} catch (HBaseException e) {
			LOG.error("error while processing renaming " + oldname + " to " + newname, e);
			return 3;
		} catch (NoSuchUniqueName e) {
			LOG.error(e.getMessage());
			return 1;
		}
		System.out.println(kind + ' ' + oldname + " -> " + newname);
		return 0;
	}

	public void rename(final String oldname, final String newname) {
		final byte[] row = getId(oldname);
//		System.out.println(Arrays.toString(row));
//		System.out.println(bytes2Long(row));
//		System.exit(0);
		
		{
			byte[] id = null;
			try {
				id = getId(newname);
			} catch (NoSuchUniqueName e) {
				// OK, we don't want the new name to be assigned.
			}
			if (id != null) {
				throw new IllegalArgumentException("When trying rename(\"" + oldname + "\", \"" + newname + "\") on " + this + ": new name already"
						+ " assigned ID=" + Arrays.toString(id));
			}
		}

		final byte[] newnameb = toBytes(newname);

		// Update the reverse mapping first, so that if we die before updating
		// the forward mapping we don't run the risk of "publishing" a
		// partially assigned ID. The reverse mapping on its own is harmless
		// but the forward mapping without reverse mapping is bad.
		try {
			final PutRequest reverse_mapping = new PutRequest(table, row, NAME_FAMILY, kind, newnameb);
			hbasePutWithRetry(reverse_mapping, MAX_ATTEMPTS_PUT, INITIAL_EXP_BACKOFF_DELAY);
		} catch (HBaseException e) {
			LOG.error("When trying rename(\"" + oldname + "\", \"" + newname + "\") on " + this + ": Failed to update reverse" + " mapping for ID="
					+ Arrays.toString(row), e);
			throw e;
		}

		// Now create the new forward mapping.
		try {
			final PutRequest forward_mapping = new PutRequest(table, newnameb, ID_FAMILY, kind, row);
			hbasePutWithRetry(forward_mapping, MAX_ATTEMPTS_PUT, INITIAL_EXP_BACKOFF_DELAY);
		} catch (HBaseException e) {
			LOG.error("When trying rename(\"" + oldname + "\", \"" + newname + "\") on " + this + ": Failed to create the"
					+ " new forward mapping with ID=" + Arrays.toString(row), e);
			throw e;
		}

		// Update cache.
		addIdToCache(newname, row); // add new name -> ID
		id_cache.put(fromBytes(row), newname); // update ID -> new name
		name_cache.remove(oldname); // remove old name -> ID

		// Delete the old forward mapping.
		try {
			final DeleteRequest old_forward_mapping = new DeleteRequest(table, toBytes(oldname), ID_FAMILY, kind);
			client.delete(old_forward_mapping).joinUninterruptibly();
		} catch (HBaseException e) {
			LOG.error("When trying rename(\"" + oldname + "\", \"" + newname + "\") on " + this + ": Failed to remove the"
					+ " old forward mapping for ID=" + Arrays.toString(row), e);
			throw e;
		} catch (Exception e) {
			final String msg = "Unexpected exception when trying rename(\"" + oldname + "\", \"" + newname + "\") on " + this
					+ ": Failed to remove the" + " old forward mapping for ID=" + Arrays.toString(row);
			LOG.error("WTF?  " + msg, e);
			throw new RuntimeException(msg, e);
		}
		// Success!
	}

	private void hbasePutWithRetry(final PutRequest put, short attempts, short wait) throws HBaseException {
		// TO-DO(tsuna): Remove once this code is async.
		put.setBufferable(false);
		while (attempts-- > 0) {
			try {
				client.put(put).joinUninterruptibly();
				return;
			} catch (HBaseException e) {
				if (attempts > 0) {
					LOG.error("Put failed, attempts left=" + attempts + " (retrying in " + wait + " ms), put=" + put, e);
					try {
						Thread.sleep(wait);
					} catch (InterruptedException ie) {
						throw new RuntimeException("interrupted", ie);
					}
					wait *= 2;
				} else {
					throw e;
				}
			} catch (Exception e) {
				LOG.error("WTF?  Unexpected exception type, put=" + put, e);
			}
		}
		throw new IllegalStateException("This code should never be reached!");
	}

	public byte[] getId(final String name) throws NoSuchUniqueName, HBaseException {
		try {
			return getIdAsync(name).joinUninterruptibly();
		} catch (RuntimeException e) {
			throw e;
		} catch (Exception e) {
			throw new RuntimeException("Should never be here", e);
		}
	}

	public Deferred getIdAsync(final String name) {

		LOG.debug("--先从缓冲区中找---");
		/**
		 * 先从缓冲区中找
		 */
		final byte[] id = getIdFromCache(name);
		if (id != null) {
			cache_hits++;
			return Deferred.fromResult(id);
		}
		cache_misses++;

		class GetIdCB implements Callback {
			public byte[] call(final byte[] id) {
				LOG.debug("---处理2---对getIdFromHBase(name)查找结果进行处理:没找到抛出NoSuchUniqueName异常,找到将id,name放入缓存");
				if (id == null) {
					throw new NoSuchUniqueName(kind(), name);
				}
				if (id.length != id_width) {
					throw new IllegalStateException("Found id.length = " + id.length + " which is != " + id_width + " required for '" + kind() + '\'');
				}
				addIdToCache(name, id);
				addNameToCache(id, name);
				return id;
			}
		}

		/**
		 * 从HBase中查找 没有找到就抛出NoSuchUniqueName异常 找到的话就把(name,id)放入缓冲区 返回id
		 */
		LOG.debug("---查询2---利用name通过getIdFromHBase(name)查找标签是否存在");
		Deferred d = getIdFromHBase(name).addCallback(new GetIdCB());
		return d;
	}

	private byte[] getIdFromCache(final String name) {
		return name_cache.get(name);
	}

	/**
	 * 查找表中是否已存在标签
	 * 
	 * @param name
	 * @return
	 */
	private Deferred getIdFromHBase(final String name) {
		return hbaseGet(toBytes(name), ID_FAMILY);
	}

	/**
	 * Returns the cell of the specified row key, using family:kind.
	 */
	private Deferred hbaseGet(final byte[] key, final byte[] family) {

		final GetRequest get = new GetRequest(table, key);
		// kind为列簇修饰符metrics
		get.family(family).qualifier(kind);

		class GetCB implements Callback> {
			public byte[] call(final ArrayList row) {
				LOG.debug("---处理3---对HBaseClient.get(get)方法进行回调处理");
				if (row == null || row.isEmpty()) {
					return null;
				}
				return row.get(0).value();
			}
		}

		LOG.debug("---查询3---先利用HBaseClient.get(get)来在HBase中的tsdb-uid表中查找有没有行键这name.toByets()的行");
		return client.get(get).addCallback(new GetCB());
	}

	private void addIdToCache(final String name, final byte[] id) {
		byte[] found = name_cache.get(name);
		if (found == null) {
			found = name_cache.putIfAbsent(name, Arrays.copyOf(id, id.length));
		}
		if (found != null && !Arrays.equals(found, id)) {
			throw new IllegalStateException("name=" + name + " => id=" + Arrays.toString(id) + ", already mapped to " + Arrays.toString(found));
		}
	}

	private void addNameToCache(final byte[] id, final String name) {
		final String key = fromBytes(id);
		String found = id_cache.get(key);
		if (found == null) {
			found = id_cache.putIfAbsent(key, name);
		}
		if (found != null && !found.equals(name)) {
			throw new IllegalStateException("id=" + Arrays.toString(id) + " => name=" + name + ", already mapped to " + found);
		}
	}

	private static byte[] toBytes(final String s) {
		return s.getBytes(CHARSET);
	}

	public String kind() {
		return fromBytes(kind);
	}

	private static String fromBytes(final byte[] b) {
		return new String(b, CHARSET);
	}
	
	public static long bytes2Long(byte[] byteNum) {  
	    long num = 0;  
	    for (int ix = 0; ix < 3; ++ix) {  
	        num <<= 8;  
	        num |= (byteNum[ix] & 0xff);  
	    }  
	    return num;  
	}
}


你可能感兴趣的:(OpenTSDB,Hbase开源项目,OpenTSDB,HBase,hadoop)