FC2ブログ

スポンサーサイト

このエントリーのカテゴリ : スポンサー広告

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

GoogleAppEngineのMemcachedとURLフェッチで負荷軽減

このエントリーのカテゴリ : GoogleAppEngine

【GAEでMapReduceを使おう!】ではMapReduceを使ってOutOfMemoryが発生しないようにしましたが、GoogleAppEngineの機能を活用してもっと改善していきたいと思います。

サイトはコチラ→http://monitorequest.appspot.com/traininfo/

電車遅延情報では、GoogleMapAPIを使って地図上に遅延している路線図を描いていますが、この路線情報は外部のWebサービスから取得しています。

Memcache導入前

htmlは電車遅延情報サイトから取得しますが、路線データ(Json)はJavaScriptから直接その外部のWebサービスからダウンロードするようになっています。
しかし、路線データは電車遅延報告のあった路線ごとに取得することになり、このままでは外部サイトの負荷を上げるので改善したいと思います。


Memcache導入後


路線データのjsonファイルを本サイトでメモリキャッシュし、Webサービスからjsonファイルを取得する回数を減らします。

ここで利用したGoogleAppEngineの機能は以下です。
1.Memcache
 http://code.google.com/intl/ja/appengine/docs/java/memcache/
 Memcaheはキーに対する値をメモリ上に保持できる機能です。
 今回はjsonファイル名に対し、Webサービスから取得したjsonファイルの内容をそのままキャッシュします。
 路線データはほぼ変わることがないし、そんなに容量もないので十分でしょう。

2.URLフェッチ
 http://code.google.com/intl/ja/appengine/docs/java/urlfetch/
 URLフェッチGoogleAppEngineから外部にHTTP通信できる機能です。
 今回のようなプロキシサーバっぽい動きをさせるには必須です。

実装は以下のようになりました。

1. Memcacheを利用した実装


 private void processRequest(HttpServletRequest req,
   HttpServletResponse res) throws ServletException, IOException {

  // 応答文字コードのセット
  res.setContentType("text/plain; charset=UTF-8");
  // 出力ストリームの取得
  PrintWriter out = res.getWriter();

  // jsonのURIでなければ通さない
  String uri = req.getRequestURI();
  String jsonUri = null;
  try {
   Pattern p = Pattern.compile(REGEX_JSON);
   Matcher m = p.matcher(uri);
   m.find();
   jsonUri = m.group();
  } catch (Exception e) {
   throw new ServletException("Illegal json path." + uri);
  }

  //キャッシュの実装はここから
  String jsonText = null;
  Cache cache;
  try {
   cache = CacheManager.getInstance().getCacheFactory().createCache(
     Collections.emptyMap());

   // json名をキーとしてキャッシュから取得
   String key = jsonUri;
   byte[] value = (byte[]) cache.get(key);

   if (value == null) {
    // JSONテキストを取得
    jsonText= getRailDataJson(jsonUri);  
    cache.put(key, jsonText.getBytes());
   }else{
    jsonText = new String(value);
   }
  } catch (CacheException e) {
   throw new ServletException("Illegal json path." + uri);
  }
  out.print(jsonText);

  // 出力ストリームを閉じる
  out.close();

 }



2. URLフェッチを利用した実装


 public String getRailDataJson(String jsonUri)
  throws MalformedURLException,IOException {

  BufferedReader reader = null;
  try {
   URL url = new URL("http://webservice.xx.xx/web/api/"+jsonUri);

   // ※ここで"UTF-8"を指定しないと文字化け!
   reader = new BufferedReader(new InputStreamReader(url.openStream(),"UTF-8"));
   String line;
   StringBuffer sb = new StringBuffer();

   while ((line = reader.readLine()) != null) {
    //※ここで改行させないとJavaScriptエラー!
    sb.append(line).append("\n");
   }

   return sb.toString();
  } catch (MalformedURLException e) {
   throw e;
  } catch (IOException e) {
   throw e;
  } finally {
   reader.close();
  }
 }



簡単に実装できました。
詰まったポイントは以下です。

・文字化け問題
 URLフェッチでjsonファイルを取得したら日本語が文字化けしていた。
 ヘッダでのエンコード指定かな?とも思ったがキャッシュに詰めるところで既に文字化け。
 InputStreamReaderから読み込むときにUTF-8を指定すれば治った。

・JavaScriptエラー
 文字化けは治ったけどJavaScriptエラーが出て表示されない。
 読み込むときに敢えて改行しなかったが、そこをちゃんと改行コード入れるようにすれば治った。

ところでMemcacheのキャッシュって複数サーバで同期とれてるのかな?
例えば、タイムスタンプみたいな値をキャッシュした場合、複数サーバでキャッシュするタイミングによって値が変わってしまうんだろうなぁ。
それとも、あるサーバでキャッシュしたら全サーバが同期とれるのだろうか?(OracleRACみたいだ!)

まあタイムスタンプみたいなのはキャッシュしないにしても、あるサーバで更新したら全サーバに通知されるようにはなってて欲しいな。


スポンサーサイト

テーマ : Google関連
ジャンル : コンピュータ

GoogleAppEngine+JavaScriptグラフライブラリ

このエントリーのカテゴリ : GoogleAppEngine

GoogleAppEngine上のアプリにグラフをつけてみました。

最近のJavaScriptライブラリは充実しており、もはやFlash不要ですね。
中でも見た目がイケてそうなHighChartsを使ってみました。

http://highcharts.com/

・棒グラフ、折れ線グラフ、円グラフなど一通り対応
・積み上げグラフや組み合わせグラフも対応
・日本語表示可能
・ポイントごとにラベル表示可能
・JavaScriptなので動的にポイント追加可能
・画像保存・印刷可能(でも、複雑なグラフだとSVGでエラーが出てしまう…)

結構すごいですね。
遊びで作った電車遅延の口コミbotが収集したデータをグラフ化してみました。

組み合わせグラフ

その他グラフも適当に作っています。

http://monitorequest.appspot.com/traininfo/

日別時間別・路線別で遅延情報を保持しているので、
こんなグラフ見てみたい、というのがあればリクエストお願いします。

テーマ : お仕事記録
ジャンル : 就職・お仕事

EclipseでGoogleAppEngineアプリを作ったときのハマりどころ

このエントリーのカテゴリ : GoogleAppEngine

EclipseでGoogleAppEngine上のアプリをJavaで構築していますが、そのときにハマりやすいところをメモしておきます。

アプリはこちら http://monitorequest.appspot.com/traininfo/



・日付がUTCになってずれてしまう

 日付文字列を解析したり、DateやCalendarで比較したりする場合は、
 ローカル環境でうまくいってもGAE上ではうまくいかない可能性があります。

 ローカル開発環境ではJCTなので、日付を解析・比較しても問題ないですが、
 GAE上ではUTCになるため、日本と比べると半日遅くなってしまいます。

 例:
  new SimpleDateFormat("yyyy/MM/dd").format(new Date())でフォーマットした場合、
     ローカル環境 →2011/1/1でもGAE環境 →2010/12/31となってしまう可能性があります。
 対策:TimeZoneを指定し、ユーティリティ関数を使う

  SimpleDateFormat df = new SimpleDateFormat("yyyy/MM/dd");
  df.setTimeZone(TimeZone.getTimeZone("Asia/Tokyo"));
  return df.parse(yyyyMMdd);
  
  とすれば問題ないので、この関数をStaticで用意し、サーブレットやJSPからはSimpleDateFormatを使わず、
  この関数を利用すれば環境依存の問題に対応できます。
  format、parseを用意しておけば問題ないでしょう。



・データストア経由の検索でエラーが出る(LazyLoading対策) 

 PersistenceManagerでGAE上のデータストアから検索してリストを利用する場合、
 「Object Manager has been closed」などのエラーがでることがあります。

 これはPersistenceManagerから取得したListはまだデータをすべてロードしておらず、
 forループなどで各要素の情報を取得する際にロードする遅延ロード機能を利用しているからだと思われます。

 したがって、closeした後でListにアクセスした際にコネクションがcloseしている、
 というエラーになっている模様です。

 サーブレットやJSPから直接PersistenceManagerにアクセスするのが嫌なので、
 別途List取得の関数を使った場合、どうしてもcloseするタイミングが先になってしまいます。

 この場合PersistenceManagerから取得したリストを別途ArrayListなどで作成し、
 addAllした後に複製したListを使いまわすようにすれば問題ありません。

 当然、遅延ローディングを戦略的に使う場合もあるかと思いますが、
 上記エラーが出た場合はまずはこれで対応できます。

テーマ : お仕事記録
ジャンル : 就職・お仕事

FacebookアプリをJavaで開発

このエントリーのカテゴリ : GoogleAppEngine

最近始めたFacebookのアプリをJavaで開発してみます。

どうもFacebookはPHPとかしか正式サポートしていないのですが、
Java屋なのでJavaAPIを探したところ、ここに書いてありました。

どうやらGoogleがソースを拡張していってるらしい。
まあこれで遊んでみよう。

Eclipse Galileoを使ってFacebook アプリケーションを構築する

・キャンバスURLが"/"で終わるかクエリパラメータを使わないとダメなため、無駄に?test=1とかつけといた。
・キャンバスページはFacebook上で見る場合のURL、キャンバスURLはFacebookから連携される場合のURL
・キャンバスページ上のiFrameから、キャンバスURLのサイトが表示されるイメージ
・キャンバスURLのサイトは直接URl指定するからdoGetでOKだが、Facebookから連携されるときはdoPostになる

サーブレットで作る部分はほぼリダイレクト周りかな。
アプリ的な部分はJavaScriptで作った方がよさそう。

テーマ : お仕事記録
ジャンル : 就職・お仕事

GoogleAppEngineでTwitterAPI(3)Twitter4JでOAuth認証を使う

このエントリーのカテゴリ : GoogleAppEngine

さて、Twitter4JでJSP化したものの、このままでは私のアカウントを使うことになるので、OAuthなるものを利用します。

Twitter4JOAuthのブラウザ版での情報が少ないため、oauth_tokenや
oauth_verifierに悩まされましたが、結構苦労してなんとかできました。

http://monitorequest.appspot.com/twitguestbook

1. http://twitter.com/oauth_clients/newでアプリケーションを登録
 ConsumerKeyとConsumerSecretKeyを使うのでメモしておきます。

2. OAuth認証用のTwitterオブジェクトを生成
 TwitterFactoryでConsumerKeyとConsumerSecretKeyを指定して作成します。

  TwitterFactory factory = new TwitterFactory();
  Twitter twitter = factory.getOAuthAuthorizedInstance(KEY,SECRET_KEY);

3. Twitter.getRequestTokenメソッドでRequestTokenを生成
 ここで、

  requestToken = twitter.getOAuthRequestToken();

 とするとドツボにはまります。

  requestToken = twitter.getOAuthRequestToken("http://monitorequest.appspot.com/twitguestbook");

 としないとoauth_verifierが取得できず、延々と悩まされます。

4. RequestToken.getAuthorizationURLで取得したURLにリダイレクト
 こんな感じです。

  String url = requestToken.getAuthenticationURL();
  resp.sendRedirect(url);

5. リダイレクト後にtwitterオブジェクトにAccessTokenを持たせる
 認証された後にリダイレクトされたときにoauth_verifier
 クエリパラメータで渡されるので、これを取得し、

  twitter.getOAuthAccessToken(requestToken,oauth_verifier);

 としてtwitterオブジェクトを承認させます。
 このときに使用するrequestTokenは
 3.や4.で利用したrequestTokenを利用する必要があるため、
 3.でセッションに保持し、5.でセッションから取得して利用します。

 3.でoauth_tokenとoauth_secretをtwitterサイトから取得しており、
 それを元に4.で認証をしてoauth_verifierが発行されるようなので、
 考えてみれば当たり前ですが。

6. twitterオブジェクトを使ってtimelineなどを取得
 この辺りは前の記事の
GoogleAppEngineでTwitterAPI(1)Twitter4Jを使う
 GoogleAppEngineでTwitterAPI(2)EclipsePluginでJSPと同じです。

やっとできたのがこちらです。
http://monitorequest.appspot.com/twitguestbook

最大の敗因は5.でoauth_verifierがクエリパラメータで渡ってこなかったことです。
GoogleでTwitter4JのOAuth認証のサンプルを探してもクライアントアプリ版のサンプルでPINコードがどうのこうの、とか、リダイレクトがなかったりしました。
また、ブラウザ版のサンプルがあってもTwitter4Jが古いバージョンで参考にならなかったりして苦労しました。

結局3.にあるように、「絶対パスでリダイレクト先を設定する」でなんとかしのいだのですが、1.のTwitterのアプリ申請でもCALLBACK_URLをわざわざ指定しているにも関わらず、また絶対パスを指定しなければならないのが気持悪いです。
相対パスにするとtwitterサイトの相対パスに遷移するのでこれまた微妙です。

ちなみにAccessTokenとかはデータベースに保持していないので安心してください。
(ユニクロみたいに悪用目的ではないので…)
そもそもOAuth認証は仮にAccessTokenをサイトに預けても拒否すれば無効になるみたいです。

そっけない画面ですが、そのうち頑張って装飾して行こうかと思っています。
誰がデザインがんばりたい人いないかなぁ。

続きを読む

テーマ : お仕事記録
ジャンル : 就職・お仕事

プロフィール

toronic

Author:toronic
IT関係で10年働いたのでそろそろ独立したいと考えているけどなかなか一歩が踏み出しきれないありきたりなプログラマ

カテゴリ
ブックマーク
最新記事
月別アーカイブ
検索フォーム
最新コメント
ブロとも申請フォーム

この人とブロともになる

メールフォーム

名前:
メール:
件名:
本文:

スポンサーリンク
リンク
一攫千金?
RSSリンクの表示
QRコード
QRコード
    助成金
    上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。