ひしだまの変更履歴

ひしだまHPの更新履歴。
主にTRPGリプレイの元ネタ集、プログラミング技術メモと自作ソフト、好きなゲームや音楽です。

AsakusaFW0.2.6の見どころ

2012-05-31 23:23:40 | PG(分散処理)

Asakusa Framework0.2.6がリリースされたので、個人的に興味があるところを見てみる。

まず、基本となる部分では、対象のHadoopのバージョンがCDH3u4になった。最近バージョンアップされた最新版ですね。 


 コーディングに関係するところでは、Direct I/OでSequenceFileが扱えるようになった。

と言っても、扱いたいWritableクラスAsakusaFWのModelクラスとの各フィールドの移送は自分で書かないといけないけど。CSVと違って色々な型や形式が採れるので、プログラマーが指定せざるを得ない。
でもこのおかげで、例えばSqoop生成するSeqFile・Writable等を自由に扱える。

あと、Direct I/Oで出力ファイル名を指定する際にランダムな値が指定できるようになった。
これで、(プロパティー(データ値)に依存せずに)複数のファイルに分割できる。
特に1ファイルにまとめる必要が無い場合は、複数ファイルに分けておく方が効率が良いことがあるからねぇ。

もうひとつ、コア演算子を扱うための便利クラスが追加になった。
今まではcore = new CoreOperatorFactory();とした上でcore.stop(in);の様に記述していたが、 CoreOperatorsというクラスが追加になり、各演算子はそのクラスのstaticメソッドとして用意されている。つまりCoreOperators.stop(in);という様に書ける。


内部実装でも効率改善の機能として、入力スプリットの結合機能が加わった。これはHadoopのCombineFileInputFormatに相当する機能。

通常だと、入力対象ディレクトリー内に複数のファイルがあると、Mapタスクはそのファイル数(およびブロック数)分作られる。しかしそれらのファイルが小さくて沢山あったとしたら、Mapタスク起動のオーバーヘッドの影響の方が大きくなってしまい、効率が悪い。
そういう時に、1つのMapタスクで複数のファイルを読み込むと効率が良くなる。
(そして結合してある程度大きいファイルになれば、後続処理でも効率が良くなる) 


目立たないかもしれないけど重要な実行時の機能として、YAESS(AsakusaFWアプリを実行するツール)が出力するログのログコードが規定された。
「YS-CORE-I01999」でバッチ、「YS-CORE-I02999」でジョブフロー、「YS-CORE-I03999」でフェーズ、「YS-CORE-I04999」でジョブ(ステージ)の終了メッセージが出る。elapsedでその処理にかかった実行時間も出るので、メトリクス収集に便利。(数値がカンマ区切りなのがちょっとだけ面倒だけど^^;)

ちなみに、Asakusa DSLがBatch DSL>Flow(FlowPart)DSL>Operator DSLという階層構造になっているのと似た感じで、実行時の階層がバッチ>ジョブフロー>フェーズ>ジョブ(ステージ)となっている。ジョブ(ステージ)がMapReduceジョブに相当する。
フェーズは各ジョブフロー内の初期処理・主処理・終了処理に相当する。(mainフェーズで複数のジョブ(stage0001~)を実行する)
(→フェーズ名の一覧) 

ちなみに、ジョブフロー単位で実行できるyaess-flow.shが加わっている。


実行計画に関係するものとしては、フローの図が出力される場所が変わった。
今まではコンパイルしたtargetディレクトリーのvisualize内にdotファイルが作られていたが、$ASAKUSA_HOME/batchapps/バッチID/opt/dsl-analysisの下に配置されるようになった。
つまり、YAESSで実行するために$ASAKUSA_HOME/batchappsにアプリのアーカイブを展開するが、その中に入っていることになる。

また、dotファイルの他にtxtファイルも生成されるようになった。ここには、フロー内に含まれるフェーズやステージのクラス名、さらにステージ内に含まれる演算子のクラス名・メソッド名が書かれている。
どのMapReduceでどの演算子が実行されるのかは、ここを見れば分かるわけだ。 

targetの下だけでなくバッチのアーカイブにこれらのファイルが含まれている理由は、target内だとコンパイルする度に変わってしまうが、batchappsの下であれば、リリース時点の状態の図がそのまま残るから。したがって、障害発生時や後から性能を調査する時などに使える。


さて、一番最後に、一番の目玉。ジョブ実行クラスタの振り分け(マルチディスパッチ)
Hadoopクラスター自体を複数用意しておき、ジョブフロー毎に実行クラスターを指定できるというもの。
そんな環境あるんかいなー?と思うところではあるが^^;、主な想定は、通常のHadoopクラスター(分散環境)スタンドアローンモードHadoopという事らしい。

AsakusaFWで既存システムのバッチ全体を記述すると、少量データを扱うMapReduceがけっこう出来てしまうらしい。こうなると、MapReduceジョブを起動するオーバーヘッドの方が大きいということになる。
そこで、そういう小さいジョブをスタンドアローンHadoopで実行しよう(データはHadoopクラスター上のHDFSを使用する)、スタンドアローンモードだとオーバーヘッドが無くて(小さなデータなら)実行が速いから。ということらしい。
(確かに、自分が昔試しに測定したものも、スタンドアローンの方が速かったなぁ…。1ジョブで10秒の差があったとしたら、30ジョブあれば5分は変わってくるもんなぁ) 

なお、スタンドアローンHadoopを使うに当たっては、スタンドアローンHadoopが動くサーバー上にHadoopをキックする仕組みが必要ということで、用意されたのがJobQueue(地名じゃない!?)。
JobQueueクライアントをYAESS側に置き、JobQueueサーバーをスタンドアローンHadoopに置く。
JobQueueサーバーはサーブレットコンテナ(Tomcatとか)上で動いていて、HTTP(HTTPS)でジョブを受け取る仕組み。
ちなみに、JobQueueサーバーのアプリはPlay FrameworkScala)で作られているらしいぞ!w(warファイルの中にScalaやPlayのjarファイルが含まれている)
(JobQueueに関しては、Scalaが使われている事に一番興味があったりして(爆))


privateメンバーへアクセスする為のキャスト

2012-05-20 23:59:56 | PG(Java)

kmizuさんのエラーの例の意味が最初分からなかった。
(lessThanというメソッド名なのに==で判定しているのがおかしいのかな?と思ったけど、そこは本題ではなかった) 

cocoa_rutoさんのツイートで可視性に言及されていて、ようやく意味が分かった。
つまり、“自分を継承したサブクラス”として宣言した変数では、自分のprivateメンバーにアクセスできない。わざわざ自分自身にキャストしてやればアクセスできるようになる。 

kmizuさんのソースでは、型TはFooを継承したサブクラスとなっている。(ジェネリクスのextends
なので、Tの変数fooでは、FooのprivateメンバーであるaValueにアクセスできない(foo.aValueが不可視であり、コンパイルエラーになる)

うーん、こんな可視性の問題があったんだなぁ。Javaでもまだまだ知らない事が多い。 


Javaの例をScalaに変換して説明してみる

2012-05-19 11:46:56 | PG(Scala)

Javaのオブジェクト指向っぽい説明の例を、Scalaに書き換えてScalaを説明してみる。
Scalaはオブジェクト指向と関数型が融合された言語なので、ベースとしてJavaと似たオブジェクト指向(クラス)が使える。


Javaのインターフェースは、Scalaではトレイトで書く。

trait 政党 {
  def 政策を作成する() : 政策
}

defでメソッドを定義する。(Scalaは関数型言語でもあるが、クラスやトレイト内に定義するのは関数ではなくメソッド。場面に応じて、メソッドは関数に(自動的に)変換して使われる)
Javaとは違い(Pascalと似て)、メソッドの戻り値の型(や変数の型)はコロンで区切って後ろに書く。
抽象メソッドの宣言はabstractキーワードをつける必要は無く、メソッド本体を記述しないだけ。
また、可視性は、何も書かない場合はpublicになる。

世界に一つしか無い官僚機構は以下のような感じで書ける。

object 官僚 {
  def 政策を実施する(s: 政党) : 結果 = {
    try {
      val dummy =  s.政策を作成する()
    } catch {
      case e: Exception =>
    }
    new 天下り先()
  }
}

(トップレベルに書かれた)objectは、シングルトンオブジェクトとなる。つまりnewしなくても自動的にVM内に1つだけインスタンスが作られる。
(トップレベルでなくクラス内に書かれたobjectはクラスをインスタンス化する際にそのインスタンス毎に作られる(staticの代わりではない)ので注意)

メソッド本体は、戻り型の指定の後ろに「=」を付けてその後ろに書く。
各文の末尾のセミコロンは省略可能。Scalaでは普通は書かない。
メソッドの戻り値としては、波括弧でくくられたブロックの一番最後の値が返る。したがって「return」は書かない。(Javaと違って、メソッド内の途中でreturnで抜けることは基本的にしない)

try~catchの機能はJavaと同じだが、catch句の書き方がちょっと違う。(Scalaのmatch式の書き方に近い)
caseの「=>」の後ろに例外処理を書くのだが、官僚は握りつぶしている(何も処理しない)ので、例としては良くないな^^;


トレイトをミックスイン(「インターフェースを実装する」のと同じような意味)してクラスを書くには以下の様にする。

class 自党 extends 政党 {
  override def 政策を作成する() : 政策 = new 我田引鉄()
}

class 共党 extends 政党 {
  override def 政策を作成する() : 政策 = new 実現不可能な理想論()
}

メソッドをオーバーライドする際はoverrideを付ける。Javaでは@Overrideアノテーションだったが、Scalaでは言語内で規定されたキーワードとなっている。
(抽象メソッドをオーバーライドする際はoverrideを付けなくてもいい(付けないのが多数派だ)が、個人的には付けた方が良いと思っている。オーバーライドしていることが一目瞭然だし、親クラスのメソッド名を変えたときにコンパイルエラーになってくれるし)

メソッド本体が1つの式で収まる場合は、波括弧のブロックにする必要が無い。
(Scalaの構文としては、メソッド本体は1つの式を指定するだけ。複数の文を書きたい場合に波括弧のブロックを利用するという扱い)

インスタンスをnewで作ることはJavaと同じ。
ただし、コンストラクターの引数が無い場合は丸括弧も省略できる。

val 元祖自党 = new 自党()
var 亀下新党 = new 自党
var 石井新党 = new 自党
var 橋原新党 = new 自党

変数はvarまたはvalで定義する。
valは、Javaでのfinal変数と同じ。つまり、valで定義した変数には他の値を再代入することが出来ない。変数が別のインスタンスを指すことが出来ないというだけで、変数内のインスタンスの中の値が書き換えられないわけではない。Scala(関数型の考え方?)としては、インスタンス内が書き換えられないようにクラス(イミュータブルなクラス)を作るのが望ましい。

イミュータブルなクラスでは、インスタンス内の一部を書き換えたい場合は、書き換えずに新しいインスタンスを生成する形となる。
今回の政党の機能としては扱っていないが、政党間で党員の移動(自党から民党への移籍等)は非常に頻繁に起こるので、インスタンス内の党員の一覧は書き換え可能な方がやりやすいだろう。
(個人的には、現在のScalaがJavaVM上で動いている以上はインスタンスを大量に生成するのは効率が悪いので、大量に書き換えが発生しそうな用途ならミュータブルにした方がいいと思う。ぜひ、Scalaにはイミュータブルでも効率の良いScalaVMを作って欲しいw)

あと、自分は手続き型言語歴が長いので、変数へ「代入する」という言葉を使うが、Scalaでは「束縛する(bind?)」と言うのが正しいっぽい。
変数名やメソッド名などに値を束縛する(割り当てる・固定する)というニュアンスなんだと思う。


既存のクラスを継承したクラスは以下の様に書く。

class 民党 extends 自党

独自のメソッドやフィールドを定義しない場合は、クラス定義の波括弧が省略できる。


コンストラクターの引数で渡された値をフィールドに保持するだけの場合、クラス名の直後に引数(フィールド定義を兼ねる)を指定するような書き方が出来る。

class 委譲政党(protected val s: 政党) extends 政党 {
  override def 政策を作成する() = s.政策を作成する() //他の政党に委譲する
}

val 公党 = new 委譲政党(元祖自党)
val 社党 = new 委譲政党(元祖自党)

メソッドの戻り値の型がメソッド本体から推測できる場合は、defの型の指定は省略することが出来る。

ちなみに、Javaと同じような書き方をすると、以下の様にくどくなる。

class 委譲政党 extends 政党 {
  protected var s:政党 = null
  def this(s: 政党) = {
    this()
    this.s = s
  }
  override def 政策を作成する() = s.政策を作成する() //他の政党に委譲する
}

Scalaのクラスでは基本コンストラクターというものが必ず出来る。クラス名の直後に引数を書くとそれが基本コンストラクターの引数となる(省略された場合は引数なしのコンストラクターとなる)。
それ以外のコンストラクター(補助コンストラクター)はthisという名前のメソッドを定義するような書き方をする。
上記のthisメソッドでは、先頭でthis()を呼び出しているが。def thisで作った補助コンストラクターは必ず(自分より前に定義された)他のコンストラクターを呼び出す必要がある。ここら辺はJavaのコンストラクターとは考え方が違うところ。


基本的にはJavaのオブジェクト指向で出来る事はScalaでも出来るが、Scala独自の考え方で異なっている部分もある。

Scalaの勉強としては、次のステップはList操作(ScalaではSeqというクラス)を勉強するのが良いと思う。関数型を生かした便利メソッドがいっぱい出てくるがw、foreachが基本でfiltermap辺りがまず重要。


比喩でJavaのオブジェクト指向を説明してみる

2012-05-16 22:40:12 | PG(Java)

オブジェクト指向の説明をするのに色々な比喩が用いられ(、実際のプログラミングと離れていて結局分かりにくいと批判され)ているが、敢えて例えてみたい。


例として、政党を考えてみる。(以下、政党名や変数名はてきとーに架空のものを使用)

まず、共通のものとして「政党」を定義する。Javaで書くとこんな感じ。

public interface 政党 {
  public abstract 政策 政策を作成する();
}

「政策を作成する」というメソッドを持つインターフェースである。
インターフェースは「こういう処理を呼び出せる」というメソッド一覧を定義するもの。
実際の処理(ここでは「政策の中身」)は規定せず、「政党であれば政策を作成する機能を持つ」ということだけを定義している。
abstractは「抽象」と訳されている。実際の処理を書かないメソッドは抽象メソッドと呼ばれる。
(なお、インターフェースの抽象メソッドではabstractキーワードは省略することが出来る)
(抽象メソッドを持つクラスは抽象クラスと呼ばれる。インターフェースは特殊な抽象クラスとも言える) 

このようにしておくと、例えば政策を実施する官僚クラスからは以下のような感じで政党から政策を取得することが出来る。

private class 官僚 {
  public 結果 政策を実施する(政党 s) {
    try {
      政策 dummy =  s.政策を作成する();
    } catch(Exception e) {
    }
    return new 天下り先();
  }
}

実体がどの政党であるかに関わらず、メソッドを呼び出すように記述できる。これがオブジェクト指向(インターフェースを用いたプログラミング)の優れた点である。

なお、try~catchは例外(正常な結果でない出来事)が起きたときにそれを受け止めて何らかの処理を行う為のもの。今回はcatch句に何も書いていないが、こういうのは「例外を握り潰す」と言われ、良くないコーディングの典型である。(例外は「都合の悪い事」ではなく「発生しうる事」なので、それに応じた処理を行う必要がある。少なくとも、ログ出力などを行い、外部の人(運用者)に見えるようにすべきである)


そして、実際の個々の政党クラスは“政党インターフェース”を実装することになる。「実装」とは、具体的な処理を記述することである。
(具体的に処理が書かれているクラスを、(抽象クラスに対して)具象(ぐしょう)クラスと呼ぶ) 

例えば「自分が良ければ他人や将来の世代のことなんかどうでもいい党(以下、自党)」や「共に生産性を上げようなどと人類には不可能なことを言う党(以下、共党)」は以下のように書ける。

public class 自党 implements 政党 {
  @Override
  public 政策 政策を作成する() {
    return new 我田引鉄();
  }
}

public class 共党 implements 政党 {
  @Override
  public 政策 政策を作成する() {
    return new 実現不可能な理想論();
  }

継承元クラスに存在するメソッドに自分の処理を記述することを「オーバーライド(上書き)する」と言う。オーバーライドされたメソッドには@Overrideというアノテーションを付ける。

クラスの定義は、処理内容が記述されているだけであり、プログラム内で使う際には「インスタンス」を作る必要がある。インスタンスとは、いわば「処理を行う“中の人”」である。

インスタンスは「new」を使って作成する。以下の様になる。

政党 元祖自党 = new 自党();
政党 亀下新党 = new 自党();
政党 石井新党 = new 自党();
政党 橋原新党 = new 自党();

これらは、インスタンス(中の人)は違うが、クラスは同じなので実施する処理内容も全く同じである。

なお、変数の宣言は、実際のクラス名でなく、インターフェース名ですべき。そうすれば、後からnewするクラスをとっかえひっかえしても後続のプログラムは影響を受けない。


既存のクラスを継承して新しいクラスを作ることも出来る。
例えば、自党を継承した「民の為と称して好き勝手やる党(以下、民党)」は以下の様になる。

public class 民党 extends 自党 {
}

継承元と処理が同じ場合は、特に処理(メソッド)を書く必要は無い。

政党 小沢自党 = new 民党();


他の具象クラスを直接継承(extends)するのではなく、別インスタンスのメソッドを呼び出すような構造も作れる。
こういう仕組みを「委譲」と言う。

public class 委譲政党 extends 政党 {
  protected 政党 s;
  public 委譲政党(政党 s) { //コンストラクター
    this.s = s;
  }
  @Override
  public 政策 政策を作成する() {
    return s.政策を作成する(); //他の政党に委譲する(他の政党の政策をそのまま使う)
  }
}

コンストラクターは、インスタンスを作る際に引数を受け取ったりそのクラス独自の初期化を行ったりするもの。
コンストラクターで引数を受け取ってフィールド(クラス内で共通に使える変数)に保持するのは常套手段。
フィールド(「フィールド変数」という呼び方は間違い)とローカル変数で同じ変数名を使うことが出来る。同じ場所で使う場合、区別できるよう、フィールドには「this.」を付ける。
(同じ名前を付けることによって別変数にアクセスできなくなることをシャドーイングと言う) 

引数つきのコンストラクターを使ってインスタンスを作る場合は、newするときに引数を渡す必要がある。 

政党 公党 = new 委譲政党(元祖自党);
政党 社党 = new 委譲政党(元祖自党);


全然関係ないですが、つい最近、虚構新聞に釣られる人が出て話題になってましたね。
自分も虚構新聞には何度か騙されて^^;楽しませていただいていますがw、情報が正しいのか、どういう意図で書かれているのか・信じるのかは読み手が汲み取れるようにならないといけませんね。騙そうとする人(宗教や新聞テレビのプロパガンダ等)は、「嘘」と書かずに嘘をついたり誘導したりするので。(虚構新聞は虚構と書いてくれているので良心的w)
それがネットリテラシー、いやネットに限らずリテラシーとして必要なんでしょう、現代人には。
騙す方が悪いとは思いますが、犯罪者に倫理観を求めるのは不可能なので。


Sqoopで留意すべき事

2012-05-12 16:46:31 | PG(分散処理)

Sqoopを使う上でこれは注意した方がいいんじゃないかな~と思った点をつらつらと書いてみた。

SqoopはRDBに対してデータ転送する事に特化しているだけあって、「さすが色々考慮されているなー」と思うところと「やっぱこれはしょーがないよねー」というところ(データの偏りとか)がある^^;

あと、うっかり忘れていたダイレクトモードもちょびっと試してみた。
MySQLPostgreSQLで機能が違いすぎ^^;
一番肝心な速度はどうなんだろうなー、仮想分散環境しか持ってないからなー。