*1
ということで@twistoireの管理用にAppEngine-MapReduceのrevision150を使ってみた。
用意されている公式ドキュメント読めばわりと問題なく使えるが、まだまだ洗練されてはいないので細かいところで苦労する感じ。以下に使うまでの流れをまとめる。
jarを作る
trunkからコード落としてantでjarを作るだけ。6つjarができるので、projectのWEB-INF/libに突っこんでCLASSPATHに追加してやる。
# for Mac OS X 10.6.6 cd /tmp svn co http://appengine-mapreduce.googlecode.com/svn/trunk/java mapreduce cd mapreduce ant open dist/lib
Mapperを実装する
公式サンプルを参考にすれば良い。自分は以下3点に引っかかった。Datastoreの低レベルAPIに慣れてればほぼ問題ないはず。
- AppEngineMapper<Key, Entity, NullWritable, NullWritable>を継承する。
- Key, Entityは入力として使用しているDatastoreInputFormatによって決定されているものなので、変更しない。
- 後半2つはReduceが実装されたら使えるようになるっぽい?
- @twistoireはEntityの扱いにJPAを使っているが、MapReduceは低レベルAPIを使う必要がある。
- int[]のフィールドをEntity#getProperty(String)するとList<Integer>が返ってくる。
- CounterGroupはTreeMap
として実装されているためか、たくさんのグループに少ないカウンタが属するよりも、少ないグループにたくさんのカウンタが属する方が管理画面表示が高速になる。
@twistoireではユーザのカウントとフォロー/リムーブ回数の合計に使用している。実装イメージは以下のとおり。
public class UserCountMapper extends AppEngineMapper<Key, Entity, NullWritable, NullWritable> { private final Logger logger = Logger.getLogger(this.getClass().getName()); @Override public void map(Key key, Entity value, Context context) { if (!value.getKind().equals("User")) { logger.warning("unknown entity kind"); return; } context.getCounter("user", "all").increment(1); if (Boolean.TRUE.equals(value.getProperty("notifyFollow"))) { context.getCounter("user", "notifyFollow").increment(1); } } }
今まではbulk loaderでCSVをダウンロードしてからExcelで調べる方法しか無かったので、結構便利。CPUをかなり使うので1日に10数回も実行するのは難しそうだ。
XMLに設定を書く
web.xmlにServletを叩くためのMappingを書いてやる。
<servlet> <servlet-name>mapreduce</servlet-name> <servlet-class>com.google.appengine.tools.mapreduce.MapReduceServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>mapreduce</servlet-name> <url-pattern>/mapreduce/*</url-pattern> </servlet-mapping>
MapReduceは高負荷だし見せたくないデータも扱うので、管理者権限でのみ実行できるように制限するといい。
<security-constraint> <web-resource-collection> <!-- 略 --> <url-pattern>/mapreduce/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>admin</role-name> </auth-constraint> </security-constraint>
次はMapReduce独自の設定。mapreduce.xmlをWEB-INFに作り、公式サンプルを参考に書いてやる。
コピペしてMapperとKindの名前だけ変えてやれば問題ない。Mapperに独自の設定を渡したい場合はXMLに
<property> <name human="my option">option</name> <value template="optional">10</value> </property>
とした上でMapperに
String myOption = context.getConfiguration().get("option");
と書いてやれば良い。
私はさらにappengine-web.xmlに設定を書いて、appengineの管理画面からアクセスできるようにした。
<admin-console> <page name="mapreduce" url="/mapreduce/status"/> </admin-console>
こうすると管理画面の左ペインにリンクが追加される。
実行
まずはローカルで試す。
ローカルではshardが1つしか使えないので並列処理にはならないが、とりあえず走るかどうかはわかる。例外が出ているときはそれを参考にコードを修正すること。