blog.Ring.idv.tw

Articles

林榆涵 - Ring

林榆涵 - Ring.她和筆者一樣都是在鹿港出生的~ 簡單來說~ 本站的網域名稱也就是因她而來的。

今年過年~ 我跟隨著十幾年沒見過面的姑姑一同回到家鄉鹿港,偶然地和表姐聊到才得知,原來她是我們的遠房親戚.. Orz 大約十幾年前我還是高一的時候,從電視上看到「My Today, My Tomorrow」的MV才開始注意到她,根據「(懷舊) Ring----林榆涵」得知,她當初是從TVBS-G主辦的小室哲哉選秀會中,由日本天王製作人小室哲哉在台灣萬人選秀,選中了當年僅13歲的林榆涵並隨即簽約出唱片(藝名:Ring),也許是同一個家鄉~ 所以筆者那時候就很喜歡她~ 上圖是筆者十幾年前買的正版CD還有她的親筆簽名(跑去台中金莎百貨的頂樓給她簽名XD),果然是夠喜愛..Orz 不過剛剛才得知她在去年2月已經結婚了~(相關資源"有結婚照")

而筆者也從那時候就開始用「@Ring@」這個ID在「星海爭霸」界闖蕩了幾年(星海2終於有beta..),也用了「Ring」在「AOE II」征戰了一陣子(果然是不愛唸書..Orz),接著就申請了「Ring.idv.tw」這個網域名稱一直沿用到現在~

2010-02-23 00:09:03 | Comments (5)

淺談HBase HTable API

在HBase中,如果我們要對一個Table進行操作的話,需要透過「HTable」物件來先初始化一些工作,而本文的重點就在於它初始化了哪些工作?為何第一次初始化就要耗費約「500ms」的時間呢?(以筆者電腦單機測試為例)

下述是一個簡單的程式:

HTable table = new HTable("UserTable");

HTable.java (lines:80~83)

public HTable(final String tableName)
  throws IOException {
    this(new HBaseConfiguration(), Bytes.toBytes(tableName));
  }

從「HTable」的原始碼來看,上述呼叫的Constructor會產生一個「HBaseConfiguration」Object,它是用來載入HBase的組態檔,如:「hbase-default.xml」和「hbase-site.xml」。

另外值得一提的是,產生「HBaseConfiguration」Object大約需要「200ms」的時間(以筆者電腦單機測試為例)。

HBaseConfiguration.java (lines:48-51)

  private void addHbaseResources() {
    addResource("hbase-default.xml");
    addResource("hbase-site.xml");
  }

接著再從呼叫另一個Constructor來看,它除了設定一些參數之外,最主要呼叫了兩個方法,它們分別為:「HConnectionManager.getConnection(conf)」和「connection.locateRegion(tableName, HConstants.EMPTY_START_ROW)」

HTable.java (lines:115~132)

 public HTable(HBaseConfiguration conf, final byte [] tableName)
  throws IOException {
    this.tableName = tableName;
    if (conf == null) {
      this.scannerTimeout = 0;
      this.connection = null;
      return;
    }

    this.connection = HConnectionManager.getConnection(conf);    
    this.scannerTimeout =
      conf.getInt("hbase.regionserver.lease.period", 60 * 1000);
    this.configuration = conf;
    this.connection.locateRegion(tableName, HConstants.EMPTY_START_ROW);
    this.writeBufferSize = conf.getLong("hbase.client.write.buffer", 2097152);
    this.autoFlush = true;
    this.currentWriteBufferSize = 0;
    this.scannerCaching = conf.getInt("hbase.client.scanner.caching", 1);
  }

先從「HConnectionManager.getConnection(conf)」來看,它主要產生一個「TableServers」物件,從變數名稱的隱喻來看它所指的是建立一個連線,並將這個connection先cache起來。

HConnectionManager.java (lines:96-106)

public static HConnection getConnection(HBaseConfiguration conf) {
    TableServers connection;
    synchronized (HBASE_INSTANCES) {
      connection = HBASE_INSTANCES.get(conf);
      if (connection == null) {
        connection = new TableServers(conf);
        HBASE_INSTANCES.put(conf, connection);
      }
    }
    return connection;
  }

再從「TableServers」Constructor來看,它除了設定基本組態之外,還透過「Class.forName()」來載入一個Interface資訊,此Interface為「org.apache.hadoop.hbase.ipc.HRegionInterface」,而該介面就是HBase Server和Client之間的RPC溝通介面。

所以到目前為止兩者其實尚未有實質化的連線。

HConnectionManager.java (lines:256-280)

public TableServers(HBaseConfiguration conf) {
      this.conf = conf;

      String serverClassName =
        conf.get(REGION_SERVER_CLASS, DEFAULT_REGION_SERVER_CLASS);

      this.closed = false;
      
      try {
        this.serverInterfaceClass =
          (Class<? extends HRegionInterface>) Class.forName(serverClassName);
        
      } catch (ClassNotFoundException e) {
        throw new UnsupportedOperationException(
            "Unable to find region server interface " + serverClassName, e);
      }

      this.pause = conf.getLong("hbase.client.pause", 2 * 1000);
      this.numRetries = conf.getInt("hbase.client.retries.number", 10);
      this.maxRPCAttempts = conf.getInt("hbase.client.rpc.maxattempts", 1);
      this.rpcTimeout = conf.getLong("hbase.regionserver.lease.period", 60000);
      
      this.master = null;
      this.masterChecked = false;
    }

接下來再來探討「connection.locateRegion(tableName, HConstants.EMPTY_START_ROW)」所為何事?

這裡的「HConstants.EMPTY_START_ROW」常數值為「new byte [0];」,代表指定該Table第一個Row的Region位置。

HConnectionManager.java (lines:556-560)

 public HRegionLocation locateRegion(final byte [] tableName,
        final byte [] row)
    throws IOException{
      return locateRegion(tableName, row, true);
    }

接著就是要取得該Table相關的Region Information,從下述程式可以得知,它會呼叫「locateRegionInMeta」方法來取得「UserTable」和「.META.」這二個Table的資訊,至於ROOT則會呼叫「locateRootRegion()」來取得,根據測試得知「locateRootRegion()」大約會耗費「250ms」,這是因為內部需要透過ZooKeeper Server來取得ROOT的相關資訊。

不過其實下述程式中的「locateRegionInMeta()」方法又會呼叫「locateRegion()」,所以它的整個執行流程順序會先從「-ROOT-」取得資訊,接著是「.META.」最後才是「UserTable」。

HConnectionManager.java (lines:568~)

private HRegionLocation locateRegion(final byte [] tableName,
      final byte [] row, boolean useCache)
    throws IOException{
      if (tableName == null || tableName.length == 0) {
        throw new IllegalArgumentException(
            "table name cannot be null or zero length");
      }
            
      if (Bytes.equals(tableName, ROOT_TABLE_NAME)) {
        synchronized (rootRegionLock) {
          if (!useCache || rootRegionLocation == null) {
            this.rootRegionLocation = locateRootRegion();
          }
          return this.rootRegionLocation;
        }        
      } else if (Bytes.equals(tableName, META_TABLE_NAME)) {
        synchronized (metaRegionLock) {
          return locateRegionInMeta(ROOT_TABLE_NAME, tableName, row, useCache);
        }
      } else {
        synchronized(userRegionLock){
          return locateRegionInMeta(META_TABLE_NAME, tableName, row, useCache);
        }
      }
    }

2010-01-08 19:09:09 | Comments (2)

移軸攝影(Tilt-shift Photography)

臺北市立美術館

後製移軸攝影效果

上面兩張圖是用來呈現何謂移軸攝影(Tilt-shift photography),上圖是之前用LX3所拍攝的一般廣角影像,而下圖是筆者用photoshop後製移軸效果的影像,兩者看上去~ 下圖所呈現的效果有一種很像假的真影像,如同小人國般的感覺,而這就是移軸攝影。

不過這不是移軸攝影的主要原理和目的,真正的原理是當使用廣角鏡頭要取景的時候,如果我們將鏡頭朝上或朝下傾斜,這會造成相機內的感光元件沒有和要拍攝物體的平面保持平行進而導致變形,所以需要有一種鏡頭來處理這樣的現象,而這類的鏡頭就稱為「透視控制(Perspective Control Lens)」又稱「移軸鏡頭」,Nikon在去年就發表了一顆「Nikon PC-E Nikkor 24mm移軸鏡頭」,這顆鏡頭大約要NT$60,000吧... Orz

如果你想要有這樣的效果,但又不想買這類型的鏡頭,而又沒學過後製的話... 沒關係!

有好心人士用Adobe AIR開發了一個「TiltShift Generator」,或者如果你不想裝軟體的話,還有一個線上版的可以用「tiltshiftmaker.com」

相關資源

.冷門鏡頭PC-E Nikkor 24mm f/3.5D ED

.移軸鏡攝影

.移軸攝影與短片

.40張很有fu的移軸攝影

2009-12-27 19:49:58 | Add Comment

Flash® Professional CS5 beta will not be published!

昨天早上就收到這封信了,前幾天還在想說Adobe官方不是強調今年年底會公佈「 Flash® Professional CS5 beta」嗎?難道要給這些設計師或開發人員一個聖誕禮物?結果... 筆者想太多了!

官方的說法是,有「太多」的開發人員對於用Flash CS5來開發iPhone應用程式很感興趣,所以決定不發佈beta version,希望此舉能讓正式版更早釋出。

呃... 說白話一點的就是,要在Flash CS5上開發iPhone應用程式就是沒有免費版可以先體驗就是了!!

相關資訊

.There will not be a beta for Flash Professional CS5

.What happened to the Flash CS5 beta?

2009-12-19 10:34:03 | Add Comment

Hadoop - 探討RunJar

通常要執行一個Hadoop Job時,會透過下述的指令來達成:

${HADOOP_HOME}/bin/hadoop jar your.jar mainClass args

當送出上述指令之後,透過「jps」指令可以觀察到有一個「org.apache.hadoop.util.RunJar」的程式正在執行:

19141 RunJar

而該「org.apache.hadoop.util.RunJar」的程式就是透過「${HADOOP_HOME}/bin/hadoop」shell來執行對應的Command「jar」,並啟動「org.apache.hadoop.util.RunJar」來進行Hadoop Job的第一步。

hadoop shell (lines:228-229)

elif [ "$COMMAND" = "jar" ] ; then
  CLASS=org.apache.hadoop.util.RunJar

從main method開始來看,它會從你所執行的「your.jar」來試著取得manifest的Main-Class屬性用來當作mainClassName,如果沒有指定的話就從參數取得。

RunJar.java (lines:94-107)

Manifest manifest = jarFile.getManifest();
    if (manifest != null) {
      mainClassName = manifest.getMainAttributes().getValue("Main-Class");
    }
    jarFile.close();

    if (mainClassName == null) {
      if (args.length < 2) {
        System.err.println(usage);
        System.exit(-1);
      }
      mainClassName = args[firstArg++];
    }
    mainClassName = mainClassName.replaceAll("/", ".");

接著該程式會將「your.jar」解壓縮在一個暫存的目錄裡面,該目錄的位置會取決於「hadoop.tmp.dir」的設定,從「${HAOOP_HOME}/src/core/core-default.xml」可以得知該設定的預設值為「/tmp/hadoop-${user.name}」,所以從下述的原始碼可得知,它一開始會試著建立「/tmp/hadoop-${user.name}」目錄(通常只有第一次執行時),然後再透過「File.createTempFile()」方法來建立一個「hadoop-unjar*」的暫存目錄,所以「your.jar」解壓縮後的class檔都會放在此目錄裡面,當執行結束之後也會一併刪除該目錄。

RunJar.java (lines:109-132)

File tmpDir = new File(new Configuration().get("hadoop.tmp.dir"));
    tmpDir.mkdirs();
    if (!tmpDir.isDirectory()) { 
      System.err.println("Mkdirs failed to create " + tmpDir);
      System.exit(-1);
    }
    final File workDir = File.createTempFile("hadoop-unjar", "", tmpDir);
    workDir.delete();
    workDir.mkdirs();
    if (!workDir.isDirectory()) {
      System.err.println("Mkdirs failed to create " + workDir);
      System.exit(-1);
    }
    
    Runtime.getRuntime().addShutdownHook(new Thread() {
        public void run() {
          try {
            FileUtil.fullyDelete(workDir);
          } catch (IOException e) {
          }
        }
      });
     
    unJar(file, workDir);

最後才會透過Reflection機制來達成動態載入「your.jar」的mainClass。

RunJar.java (lines:134-159)

ArrayList<URL> classPath = new ArrayList<URL>();
    classPath.add(new File(workDir+"/").toURL());
    classPath.add(file.toURL());
    classPath.add(new File(workDir, "classes/").toURL());
    File[] libs = new File(workDir, "lib").listFiles();
    if (libs != null) {
      for (int i = 0; i < libs.length; i++) {
        classPath.add(libs[i].toURL());
      }
    }
    
    ClassLoader loader =
      new URLClassLoader(classPath.toArray(new URL[0]));

    Thread.currentThread().setContextClassLoader(loader);
    Class<?> mainClass = Class.forName(mainClassName, true, loader);
    Method main = mainClass.getMethod("main", new Class[] {
      Array.newInstance(String.class, 0).getClass()
    });
    String[] newArgs = Arrays.asList(args)
      .subList(firstArg, args.length).toArray(new String[0]);
    try {
      main.invoke(null, new Object[] { newArgs });
    } catch (InvocationTargetException e) {
      throw e.getTargetException();
    }

2009-12-16 00:09:37 | Add Comment

Next Posts~:::~Previous Posts
Copyright (C) Ching-Shen Chen. All rights reserved.

::: 分類 :::

::: 最新文章 :::

::: 最新回應 :::