libhdfsを使う
libhdfsのAPIは$(HADOOP_HOME)/libhdfs/docs/apiに詳しいドキュメントがあるので、そこを見ながらやると簡単です。サンプルが必要ないくらいに色々書いてあります。ただちょっと良く分からなかった点があったので、いくつかメモを残しておきます。
Connect, Disconnect
HDFSへはhdfsConnectを使って接続します。この関数にはhost,portを与えます。サンプルプログラムでは"default",0を指定していますが、このように指定するとローカルファイルシステムが使用されました。HDFSのhost,portを指定したらちゃんとHDFSが使用されました。
hdfsConnectで作成したhdfsFSはhdfsDisconnectで解放します。
相対パスの扱われ方
libhdfsで関数に相対パスを与えると、デフォルトでは/user/ユーザー名/からの相対パスとして解釈されるようです。この挙動はhdfsSetWorkingDirectoryという関数で変更可能です。現在のWorkingDirectoryはhdfsGetWorkingDirectoryで取得可能です。
hdfsSetWorkingDirectory(fs, "");
のように空のものを指定すると次のようなエラーメッセージをstderrに吐きました。
Exception in thread "main" java.lang.IllegalArgumentException: Can not create a Path from an empty string at org.apache.hadoop.fs.Path.checkPathArg(Path.java:82) at org.apache.hadoop.fs.Path.<init>(Path.java:90) Can't construct instance of class org.apache.hadoop.fs.Path for
エラーメッセージは出力されましたが、hdfsSetWorkingDirectoryは普通に-1を返すだけでプログラムは終了しませんでした。その後fsを使いまわしても特になんの問題もありませんでした。
hdfsFileInfo
hdfsGetPathInfoやhdfsListDirectoryが返すhdfsFileInfoの中身が謎だったので調査してみました。mKind以外は名前通りなので省略。
mKindは良く分かりませんが、ファイルだと70、ディレクトリだと68でした。二進法に直すとそれぞれ1000110と1000100です。謎。ドキュメントにはhdfsFileInfoについて詳しくかかれていません。mKindはtObjectKind型(enum)らしいです。tObjectKindについてドキュメントには
typedef enum tObjectKind tObjectKind
port
ソースを読めという暗黙的ですが親切なメッセージが書いてあります。見てみました。
typedef enum tObjectKind { kObjectKindFile = 'F', kObjectKindDirectory = 'D', } tObjectKind;
/(^o^)\
さっきのportという謎のメッセージは、下のように書いたせいで出力されていたようです。
typedef int32_t tSize; /// size of data for read/write io ops typedef time_t tTime; /// time type typedef int64_t tOffset;/// offset within the file typedef uint16_t tPort; /// port typedef enum tObjectKind { kObjectKindFile = 'F', kObjectKindDirectory = 'D', } tObjectKind;
doxygenコメントって、"//!<"こう書かないと次の行に対するコメントになってしまうのでは。
ちなみに、mNameは次のようなhdfs://から始まる絶対パスでした。
hdfs://localhost:50000/user/nobu/test/test.txt
ファイルへの書き込み
hdfsOpenFileでファイルを開いてそれに書き込む形になります。
hdfsFile hdfsOpenFile(hdfsFS fs, const char* path, int flags, int bufferSize, short replication, tSize blocksize )
これは通常のファイル関係のAPIと同じで、ファイル名や読み書きフラグなどを引数に与えます。それに加えてHDFS上でのブロックサイズやレプリケーション数なども与えることができるようです。この辺は0を指定するとデフォルトの値が使用されます。
そんで読み書きフラグがちょっと気になったのでhdfs.hを見てみたのですが、
#ifndef O_RDONLY #define O_RDONLY 1 #endif #ifndef O_WRONLY #define O_WRONLY 2 #endif
となっていました。fcntl.hでは
#define O_RDONLY 00 #define O_WRONLY 01
こうなってます。これはさすがにヤバいですね…。既に問題になっていたようです。でも、hdfs.hではfcntl.hを先にincludeしているので、LinuxではO_RDONLYは0, O_WRONLYは1になるそうです。それもそれでヤバいのではw まあLinuxだけで使っている分には大丈夫そうですね。とりあえず下手に自分でaliasなどを作らない方が良さそうです。
それでは本題のファイル書き込み。hdfsWriteを使って書き込みます。書き込んだ結果はhdfsFlushかhdfsCloseを呼び出すと反映されます。既存のファイルを書き込みモードで開くとO_TRUNC的な挙動をします。追記はできません。追記はできませんが、hdfsWriteの呼び出しは何度かに分けて行うことができます。
hdfsWrite(fs, file, "test\n", 5); hdfsWrite(fs, file, "test2\n", 6); hdfsCloseFile(fs, file);
次のように、hdfsFlushを複数回呼んでも大丈夫でした。
hdfsWrite(fs, file, "test\n", 5); hdfsFlush(fs, file); hdfsWrite(fs, file, "test2\n", 6); hdfsFlush(fs, file); hdfsCloseFile(fs, file);
さらに、hdfsCloseFileを呼び出さずにプログラムを終了しても大丈夫でした(絶対やらないけど)。さすが。追記ができないのであまり嬉しくはないですが、書き込み途中に不慮の事故などでプログラムが落ちてしまっても大丈夫そうです。