[Seasar-user:19063] Re: [mobylet]m:img を適用するとGAEからhttp500エラーが返る

Shin Takeuchi [E-MAIL ADDRESS DELETED]
2009年 12月 25日 (金) 02:15:06 JST


海野さん

竹内(stakeuchi)です。

> 1.0.4-SNAPSHOTで、GAE上で表示できることを確認しました。

ご確認ありがとうございます。

> 渡さんに返信したあとずっと考えていたのですが、
> 正直、なぜサーブレットではだめで、FilterならOKなのか
> まだ理解できていません。^^;

種明かしをすると難しい話ではなくて
Servlet方式の場合は

HttpRequest→Servlet→HttpRequest→Image

という感じでServletから画像ファイルのURLFetchを行っているのですが
Filter方式の場合は

HttpRequest→(Filter[Proxy])→Image

という形になっているため
URLFetchを行っていない構図になります。
つまりURLFetchを行っていないので、問題が起きなくなった
ということです。


<m:img>タグが出力するURLを
Servlet方式とFilter方式の2パターン見比べて頂ければ分かると思いますが
Servlet方式はサーブレットへのパスに画像パスのパラメータが付与されている形ですが
Filter方式だと直接画像パスが指定されていて、
どう変換するのか、というパラメータが付いているだけなので
Filter方式の場合は画像へダイレクトにリクエストが飛んでいることになります。

#なのでURLFetchしなくて良いという訳です。


また、ご覧の通りになりますが
Filter方式の場合は、WebApplication配下にあるImage(Filterを通過するURL)でなければ
利用することが出来ません。
今回のようなBigtableから動的に画像を引き抜く場合は
filter-mapping可能なURLとなるので、このFilter方式が適用出来た訳ですが
外部URLの画像を動的に変換しようとすると
このFilter方式は全く使えませんし、
内部URLでもWebApplication管理下のURLでなければ
これまたFilter方式は使えません。
(Apache管理下の画像ファイル等はFilter方式が使えないということです)


Servlet方式とFilter方式は考え方が全然違うので
少し混乱するかもしれませんが
特別な技術を使っている訳ではありませんので
まっさらな頭で考えていただければ、きっとご理解頂けると思います。


よろしくお願い致します。


2009年12月25日1:27 Taro Unno <[E-MAIL ADDRESS DELETED]>:
> 竹内さん
>
> 1.0.4-SNAPSHOTで、GAE上で表示できることを確認しました。
>
> 渡さんに返信したあとずっと考えていたのですが、
> 正直、なぜサーブレットではだめで、FilterならOKなのか
> まだ理解できていません。^^;
>
> よく調べて整理してからまた質問させていただくかもしれません。
> ありがとうございました。
>
> 海野
>
>
>
>> 海野さん
>>
>> 竹内(stakeuchi)です。
>>
>> お問い合わせの件ですが
>> ご指摘頂いている通りの問題だと思います。
>>
>> 実際にはサーブレットを介してURLFetchを行っていることが
>> GAE上で問題となっていると思われるのですが
>> mobyletには元々FilterでProxy的に画像変換処理を行うロジックもあり、
>> これを復帰させることでGAE上でも動的なリサイズを行うことが出来るようです。
>>
>> 参考: http://mobyletsample.appspot.com/top
>>
>> #Filter方式を閉じたのは
>> #Servlet方式に比べて機能が制限されることと
>> #Proxyのような処理を行うので
>> #ロジック的に少々処理が重くなるため
>> #現状は推奨していませんでした。
>>
>>
>> Filter方式はしばらくメンテナンスをしていなかったのですが
>> 今回の事例を受けて再度インターフェースを用意致しましたので
>> こちらのSNAPSHOTを適用して頂けますでしょうか?
>>
>> http://maven.seasar.org/maven2-snapshot/org/seasar/mobylet/mobylet-core/1.0.4-SNAPSHOT/mobylet-core-1.0.4-20091224.152304-1.jar
>> http://maven.seasar.org/maven2-snapshot/org/seasar/mobylet/mobylet-taglibs/1.0.4-SNAPSHOT/mobylet-taglibs-1.0.4-20091224.152904-1.jar
>>
>>
>> また、インターフェースとしてtaglibsも改修し、次のような記述を行うことで
>> Filter方式の画像リサイズ処理を行うことが出来ます。
>>
>> <m:img src="/getimage" magniWidth="0.8" useFilter="true" codec="GIF" />
>>
>> #「codec」は上記のようなURLに拡張子が付いてない場合等に
>> #明示的にFilterにContentTypeを教える意味で必要です。
>> #逆に「src」にイメージの拡張子が付いているパスであれば「codec」の指定は不要
>> です。
>>
>>
>> また、全てにuseFilter="true"を記述することが面倒な場合は
>> mobylet.image.properties内に記述している
>> image.scale.servlet.pathの値を削除することで
>> Filterに全て変換処理を行わせることが可能です。
>>
>>
>> 注意点としてはこの方式ではキャッシュが行えないため
>> 毎回Bigtableにアクセスして変換処理を行いますので
>> 高負荷になった場合においては懸念が残る形ではありますので
>> 念頭において頂ければ幸いです。
>>
>>
>> よろしくお願い致します。
>>
>>
>> 2009年12月24日23:13  <[E-MAIL ADDRESS DELETED]>:
>>> 渡さん
>>>
>>> こちらこそはじめまして。
>>>
>>> なるほど、その視点がまったくありませんでした。
>>>
>>> 「URL フェッチ Java API 概要」
>>> http://code.google.com/intl/ja/appengine/docs/java/urlfetch/overview.html
>>> の「リクエストの作成」に
>>>
>>> 「URL フェッチ サービスへのコールは同期されており、サービスがリモート ホスト
>>> からレスポンスを受け取るまで結果は返されません。リモートホストが応答する前に、
>>> アプリケーションのリクエストタイマーの時間が経過してしまう場合もあります。リ
>>> クエストの発行後にキャンセルすることはできません。」
>>>
>>> としっかり書いてありました。これのことですね。
>>>
>>> とはいえ、まだ理解が浅いため、よく考えてから
>>> 回避策などをどうするか決めたいと思います。
>>>
>>> たいへん助かりました。
>>> ありがとうございました。
>>>
>>> 海野
>>>
>>>
>>>> 海野さん
>>>>
>>>> 渡といいます。初めまして。
>>>>
>>>> Google App Engineでは、リクエスト処理にローカル環境で
>>>> エミュレートできていない、いくつかの制限があります。
>>>> アプリケーションが自分自身に対してURLFetchを利用してリクエストした場合、
>>>> ある限定的な条件で失敗すると推測されます。
>>>>
>>>> A.インスタンスが立ち上がっていない状態から、アプリへリクエストする。
>>>> ⇒一つインスタンスが立ち上がる。
>>>> B.アプリのリクエスト処理でURLFetchService#fetchを呼び出す。
>>>> ⇒リクエストがキューに積まれ、今の処理とは別のインスタンスを利用しようと
>>>> する。
>>>>
>>>> Aのリクエストはインスタンス起動の処理と同一のスレッドで処理されますが、
>>>> URLFetchServiceの呼び出しによりこのスレッドがブロックされるため、
>>>> Bのリクエストを処理するためのインスタンスの起動が行なえません。
>>>> ある程度インスタンスが立ち上がった(スケールアウトした)状態ですとうまく動い
>>>> てしまうので、
>>>> テスト時には表面化しなかったのかもしれません。
>>>>
>>>> 一般的な回避方法として、インスタンス起動のリクエストでは自分自身に対する
>>>> URLFetchを使わない、もしくは処理そのものを回避する(slim3ではインスタンス
>>>> 起動リクエストではredirectさせるような処理があるようです)ぐらいかと思います。
>>>>
>>>> mobyletは触ったことがないので、回避するには m:imgを使わない、
>>>> http://xxx.appspot.com/imagelist/image?key=99999
>>>> を外部のサーバにする、程度しか私には思いつかないのですが、ご参考まで。
>>>>
>>>> 2009年12月23日17:21  <[E-MAIL ADDRESS DELETED]>:
>>>>> 海野です。
>>>>> お世話になります。
>>>>>
>>>>> mobylet + t2 + S2 + Slim3Datastore
>>>>> という構成でGAE/Jでアプリを作ってみているのですが、、
>>>>>
>>>>> あらかじめBigTableに格納した100k程度のjpg画像を
>>>>> mobyletの画像リサイズ機能を使って表示しようとして、
>>>>>
>>>>> <m:img src="http://xxx.appspot.com/imagelist/image?key=99999"
>>>>> magniWidth="0.8"/>
>>>>>
>>>>> というタグをjspからhtmlに出力しています。
>>>>>
>>>>> すると、ローカル環境では表示されるのですが
>>>>> GAE上にデプロイすると表示できない
>>>>> という現象に出くわしてます。
>>>>> (GAEからはhttpの500エラーが返ります。)
>>>>>
>>>>> GAEのログを見る限り、MobyletImageReader.getStreamで
>>>>> ストリームがオープンできなくて、その原因は
>>>>> com.google.appengine.api.urlfetch.URLFetchServiceImpl.fetch
>>>>> でタイムアウトが起きたから、と読めます。
>>>>>
>>>>> ローカル上のデバッグで確認したところ、
>>>>> imageScalerにはGaeImageScalerが適用されてます。
>>>>>
>>>>> 切り分けの為に、m:imgに指定した
>>>>> http://xxx.appspot.com/imagelist/image?key=99999
>>>>> をブラウザから直接リクエストすると、表示されます。
>>>>>
>>>>> もひとつためしに、画像を5kくらいの小さなものに変えても
>>>>> 現象は同じです。
>>>>>
>>>>> さらにためしに、m:imgではなく、普通のimgタグに変えると
>>>>> 表示できます。
>>>>>
>>>>> ここまでのお試しで、m:imgを適用したことによって
>>>>> GAE上でローカルとは異なる何かが原因で
>>>>> getStreamが失敗しているのだろう
>>>>> ということしか見当がつきません。
>>>>>
>>>>> 何か原因が思いつきましたら、アドバイスいただけませんでしょうか。
>>>>>
>>>>> バージョンは次の通りです。
>>>>> appengine-java-sdk-1.3.0
>>>>> mobylet 1.0.3
>>>>> t2 0.6.2-ga
>>>>> Seaser 2.4.40
>>>>> Slim3Datastore EA1-SNAPSHOT
>>>>>
>>>>> よろしくお願い致します。
>>>>> 海野
>>>>>
>>>>> <以下、GAEの管理画面で確認したスタックトレース>−−−
>>>>> Nested in javax.servlet.ServletException: javax.servlet.ServletException: org.mobylet.core.MobyletRuntimeException: ストリームをオープンできません path = http://xxx.appspot.com/imagelist/image?k=20091223070604826:
>>>>> javax.servlet.ServletException: org.mobylet.core.MobyletRuntimeException: ストリームをオープンできません path = http://xxx.appspot.com/imagelist/image?k=20091223070604826
>>>>>        at
>>>>> org.t2framework.t2.handler.impl.GlobalExceptionHandlerImpl.handleException(GlobalExceptionHandlerImpl.java:79)
>>>>>        at
>>>>> org.t2framework.t2.filter.T2Filter.handleException(T2Filter.java:284)
>>>>>        at
>>>>> org.t2framework.t2.filter.T2Filter.doFilter(T2Filter.java:217)
>>>>>        at
>>>>> org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)
>>>>>        at
>>>>> org.t2framework.t2.filter.MultiPartRequestFilter.doFilter(MultiPartRequestFilter.java:161)
>>>>>        at
>>>>> org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)
>>>>>        at
>>>>> org.mobylet.core.http.MobyletFilter.processFilter(MobyletFilter.java:115)
>>>>>        at
>>>>> org.mobylet.core.http.MobyletFilter.doFilter(MobyletFilter.java:69)
>>>>>        at
>>>>> org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)
>>>>>        at
>>>>> com.google.apphosting.utils.servlet.ParseBlobUploadFilter.doFilter(ParseBlobUploadFilter.java:97)
>>>>>        at
>>>>> org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)
>>>>>        at
>>>>> com.google.apphosting.runtime.jetty.SaveSessionFilter.doFilter(SaveSessionFilter.java:35)
>>>>>        at
>>>>> org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)
>>>>>        at
>>>>> com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:43)
>>>>>        at
>>>>> org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)
>>>>>        at
>>>>> org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:360)
>>>>>        at
>>>>> org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
>>>>>        at
>>>>> org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181)
>>>>>        at
>>>>> org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:712)
>>>>>        at
>>>>> org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:405)
>>>>>        at
>>>>> com.google.apphosting.runtime.jetty.AppVersionHandlerMap.handle(AppVersionHandlerMap.java:238)
>>>>>        at
>>>>> org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:139)
>>>>>        at org.mortbay.jetty.Server.handle(Server.java:313)
>>>>>        at
>>>>> org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:506)
>>>>>        at
>>>>> org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:830)
>>>>>        at
>>>>> com.google.apphosting.runtime.jetty.RpcRequestParser.parseAvailable(RpcRequestParser.java:76)
>>>>>        at
>>>>> org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:381)
>>>>>        at
>>>>> com.google.apphosting.runtime.jetty.JettyServletEngineAdapter.serviceRequest(JettyServletEngineAdapter.java:135)
>>>>>        at
>>>>> com.google.apphosting.runtime.JavaRuntime.handleRequest(JavaRuntime.java:235)
>>>>>        at
>>>>> com.google.apphosting.base.RuntimePb$EvaluationRuntime$6.handleBlockingRequest(RuntimePb.java:5235)
>>>>>        at
>>>>> com.google.apphosting.base.RuntimePb$EvaluationRuntime$6.handleBlockingRequest(RuntimePb.java:5233)
>>>>>        at
>>>>> com.google.net.rpc.impl.BlockingApplicationHandler.handleRequest(BlockingApplicationHandler.java:24)
>>>>>        at
>>>>> com.google.net.rpc.impl.RpcUtil.runRpcInApplication(RpcUtil.java:363)
>>>>>        at com.google.net.rpc.impl.Server$2.run(Server.java:838)
>>>>>        at
>>>>> com.google.tracing.LocalTraceSpanRunnable.run(LocalTraceSpanRunnable.java:56)
>>>>>        at
>>>>> com.google.tracing.LocalTraceSpanBuilder.internalContinueSpan(LocalTraceSpanBuilder.java:536)
>>>>>        at com.google.net.rpc.impl.Server.startRpc(Server.java:793)
>>>>>        at
>>>>> com.google.net.rpc.impl.Server.processRequest(Server.java:368)
>>>>>        at
>>>>> com.google.net.rpc.impl.ServerConnection.messageReceived(ServerConnection.java:448)
>>>>>        at
>>>>> com.google.net.rpc.impl.RpcConnection.parseMessages(RpcConnection.java:319)
>>>>>        at
>>>>> com.google.net.rpc.impl.RpcConnection.dataReceived(RpcConnection.java:290)
>>>>>        at
>>>>> com.google.net.async.Connection.handleReadEvent(Connection.java:466)
>>>>>        at
>>>>> com.google.net.async.EventDispatcher.processNetworkEvents(EventDispatcher.java:759)
>>>>>        at
>>>>> com.google.net.async.EventDispatcher.internalLoop(EventDispatcher.java:205)
>>>>>        at
>>>>> com.google.net.async.EventDispatcher.loop(EventDispatcher.java:101)
>>>>>        at
>>>>> com.google.net.rpc.RpcService.runUntilServerShutdown(RpcService.java:251)
>>>>>        at
>>>>> com.google.apphosting.runtime.JavaRuntime$RpcRunnable.run(JavaRuntime.java:394)
>>>>>        at java.lang.Thread.run(Unknown Source)
>>>>> Caused by: org.mobylet.core.MobyletRuntimeException: ストリームをオープンできません path = http://xxx.appspot.com/imagelist/image?k=20091223070604826
>>>>>        at
>>>>> org.mobylet.core.image.impl.MobyletImageReader.getStream(MobyletImageReader.java:85)
>>>>>        at
>>>>> org.mobylet.core.http.image.MobyletImageScaleServlet.doGet(MobyletImageScaleServlet.java:77)
>>>>>        at javax.servlet.http.HttpServlet.service(HttpServlet.java:693)
>>>>>        at javax.servlet.http.HttpServlet.service(HttpServlet.java:806)
>>>>>        at
>>>>> org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:487)
>>>>>        at
>>>>> org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1093)
>>>>>        at
>>>>> org.slim3.datastore.DatastoreFilter.doFilter(DatastoreFilter.java:53)
>>>>>        at
>>>>> org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)
>>>>>        at
>>>>> org.t2framework.t2.contexts.impl.ChainImpl.doFilter(ChainImpl.java:53)
>>>>>        at
>>>>> org.t2framework.t2.navigation.PassThrough.execute(PassThrough.java:71)
>>>>>        at
>>>>> org.t2framework.t2.filter.T2Filter.handleNotT2Request(T2Filter.java:323)
>>>>>        at
>>>>> org.t2framework.t2.filter.T2Filter.handleNavigation(T2Filter.java:268)
>>>>>        at
>>>>> org.t2framework.t2.filter.T2Filter.doFilter(T2Filter.java:215)
>>>>>        ... 45 more
>>>>> Caused by: java.io.IOException: Timeout while fetching:
>>>>> http://xxx.appspot.com/imagelist/image?k=20091223070604826
>>>>>        at
>>>>> com.google.appengine.api.urlfetch.URLFetchServiceImpl.convertApplicationException(URLFetchServiceImpl.java:119)
>>>>>        at
>>>>> com.google.appengine.api.urlfetch.URLFetchServiceImpl.fetch(URLFetchServiceImpl.java:47)
>>>>>        at
>>>>> com.google.apphosting.utils.security.urlfetch.URLFetchServiceStreamHandler$Connection.fetchResponse(URLFetchServiceStreamHandler.java:409)
>>>>>        at
>>>>> com.google.apphosting.utils.security.urlfetch.URLFetchServiceStreamHandler$Connection.getInputStream(URLFetchServiceStreamHandler.java:290)
>>>>>        at
>>>>> org.mobylet.core.image.impl.MobyletImageReader.getStream(MobyletImageReader.java:83)
>>>>>        ... 57 more
>>>>>
>>>>>
>>>>> <以下、web.xmlのfilter-mappingとサーブレット設定>−−−
>>>>>        <filter-mapping>
>>>>>            <filter-name>mobyletfilter</filter-name>
>>>>>            <url-pattern>/*</url-pattern>
>>>>>            <dispatcher>REQUEST</dispatcher>
>>>>>            <dispatcher>FORWARD</dispatcher>
>>>>>            <dispatcher>INCLUDE</dispatcher>
>>>>>        </filter-mapping>
>>>>>        <filter-mapping>
>>>>>                <filter-name>uploadFilter</filter-name>
>>>>>                <url-pattern>/*</url-pattern>
>>>>>        </filter-mapping>
>>>>>        <filter-mapping>
>>>>>                <filter-name>t2</filter-name>
>>>>>                <url-pattern>/*</url-pattern>
>>>>>        </filter-mapping>
>>>>>        <filter-mapping>
>>>>>                <filter-name>datastoreFilter</filter-name>
>>>>>                <url-pattern>/*</url-pattern>
>>>>>                <dispatcher>REQUEST</dispatcher>
>>>>>        </filter-mapping>
>>>>>
>>>>>        <servlet>
>>>>>            <servlet-name>imageScaleServlet</servlet-name>
>>>>>            <servlet-class>org.mobylet.core.http.image.MobyletImageScaleServlet</servlet-class>
>>>>>        </servlet>
>>>>>        <servlet-mapping>
>>>>>            <servlet-name>imageScaleServlet</servlet-name>
>>>>>            <url-pattern>/imageScaler</url-pattern>
>>>>>        </servlet-mapping>
>>>>>
>>>>>
>>>>>
>>>>> _______________________________________________
>>>>> Seasar-user mailing list
>>>>> [E-MAIL ADDRESS DELETED]
>>>>> https://ml.seasar.org/mailman/listinfo/seasar-user
>>>>>
>>>> _______________________________________________
>>>> Seasar-user mailing list
>>>> [E-MAIL ADDRESS DELETED]
>>>> https://ml.seasar.org/mailman/listinfo/seasar-user
>>>>
>>>>
>>>
>>>
>>> _______________________________________________
>>> Seasar-user mailing list
>>> [E-MAIL ADDRESS DELETED]
>>> https://ml.seasar.org/mailman/listinfo/seasar-user
>>>
>>
>>
>>
>> --
>> ―Lei Hau'oli Co.,Ltd.―――――――――――
>>
>>  竹内 真(TAKEUCHI SHIN)
>>  Tel: 080-3300-9888
>>  Mail: [E-MAIL ADDRESS DELETED]
>>
>>  株式会社Lei Hau'oli
>>  〒150-0001 東京都渋谷区神宮前4丁目18番6号
>>  Tel: 03-5775-0315 Fax: 03-5775-0314
>> _______________________________________________
>> Seasar-user mailing list
>> [E-MAIL ADDRESS DELETED]
>> https://ml.seasar.org/mailman/listinfo/seasar-user
>>
>>
>
>
> --
> Taro Unno : [E-MAIL ADDRESS DELETED]
>
> _______________________________________________
> Seasar-user mailing list
> [E-MAIL ADDRESS DELETED]
> https://ml.seasar.org/mailman/listinfo/seasar-user
>



-- 
―Lei Hau'oli Co.,Ltd.―――――――――――

 竹内 真(TAKEUCHI SHIN)
 Tel: 080-3300-9888
 Mail: [E-MAIL ADDRESS DELETED]

 株式会社Lei Hau'oli
 〒150-0001 東京都渋谷区神宮前4丁目18番6号
 Tel: 03-5775-0315 Fax: 03-5775-0314


Seasar-user メーリングリストの案内