スキップしてメイン コンテンツに移動

[Python][Google App Engine]Backendsで無限ループ実装

先日のGoogle App Engine SDK 1.5のリリースで処理時間に制限のないBackendsがリリースされ、さらにこれまで処理で利用したCPU時間に対して課金される仕組みが今後Instanceの起動(アサイン)時間に対して課金されるようになることが発表されました。

つまり、Instanceが1時間起動している間に100回リクエストを捌こうと10回捌こうと金額は変わらないということです。

従来であれば、cronでの最小間隔である1分間に1回処理を実行すると最初の数秒はCPU時間を消費して処理を実行して、残りの40~50秒は何もしない、イコール課金されないという方が理にかなっていました。

しかし、Instance時間になるとたとえ40~50秒間処理を行わなてもInstanceが起動されている間は常に課金されてしまいます。
そうなると、いかにCPUが働いていない40~50秒間を有効に使うか、ということが問題になります。

そこで考えたのが、無限ループです。
特定の処理を無限にループする事によって、Instanceの起動時間全てをループ内の処理に割り当てることができ、Instance時間を有効に活用することができます。

あいにく、まだInstance時間は通常のスケールするInstanceに対しては適用されませんが、BackendsではInstance時間でのQuotaの計算がされています。

将来的な変化に慣れるためにもちょっと実装してみました。

今回ははじめてFlaskというフレームワークを使って書いてみました。

これです。
==========ここから==========
@app.route('/')
def put_queue():
    taskqueue.add(url=url_for('work'),target='test')
    return 'Done'

@app.route('/work',methods=['POST'])
def work():
    for var in range(3600):
        now = datetime.datetime.now() + datetime.timedelta(hours=9)
        str_now = datetime.datetime.strftime(now,'%Y%m%d%H%M%S')
        logging.info(str_now)
        key = 'SPOTS:'+str_now
        logging.info(key)
        rate = GetCurrentRate()
        logging.info(str(rate))
        SPOTS(key_name=key
              ,update=now
              ,USDJPY = rate['USDJPY']
              ,EURJPY = rate['EURJPY']
              ,EURUSD = rate['EURUSD']
              ,AUDJPY = rate['AUDJPY']
              ,GBPJPY = rate['GBPJPY']
              ,NZDJPY = rate['NZDJPY']
              ,CADJPY = rate['CADJPY']
              ,CHFJPY = rate['CHFJPY']
              ,HKDJPY = rate['HKDJPY']
              ,GBPUSD = rate['GBPUSD']
              ,USDCHF = rate['USDCHF']
              ,ZARJPY = rate['ZARJPY']).put_with_cache(key_name=key)
        time.sleep(0.7)
    return 'Done'
==========ここまで==========

簡単なもんです。
まず、通常のcronでput_queue()を叩きます。
次にBackends側でTaskQueueのTaskを受け取り(POST)、3600回繰り返し実行します。

ループの中では為替レートの取得、DataStoreへのput()を行っています。

ログで見る限り、DataStoreへのput()にかかるCPU時間が400~600ms程度。
そこにtime.sleep(0.7)で0.7秒=700msの待ちを加えて約1000~1300ms間隔で実行します。

DataStoreを見るとループ1回で実質1.1秒程度かかっていて、ちょうど1秒間隔の為替レートが取得できています。

上記実行回数(3600回)を考えればもうお分かりのように、1時間に1回cronでput_queue()を叩いてやるように設定してやるだけです。

これでBackendsでの無限ループ処理を簡単に実装できますね。

コメント

このブログの人気の投稿

[Python]redis-pyでRedis Pub/Sub実装

前から面白そうと思っていたRedisのPub/Sub機能。 redis-pyでどう実装すれば使えるか確認してみた。 ■Pub/Subについて http://ja.wikipedia.org/wiki/出版-購読型モデル ■pub.py from redis import StrictRedis def publish(channel,msg): """ redis-pyにはPUBLISHするためのメソッドがないので、 Redisのコマンドをそのまま実行する為のクラスを使う。 """ sr=StrictRedis() """ 第1引数にRedisのコマンド、第2引数以降は そのコマンドの引数をそのままセット """ sr.execute_command("PUBLISH",channel,msg) if __name__=="__main__": # チャネル"hoge"に"Hello"というメッセージを出版(Publish) publish("hoge","Hello") ■sub.py from redis import Redis def listen(channel): r=Redis() ps=r.pubsub() ps.subscribe(channel) while True: for i in ps.listen(): print i["data"] if __name__=="__main__": # チャネル"hoge"を購読(Subscribe) listen("hoge") ■実行方法・実行結果 (ターミナル1-準備) [user@localhost ~]# python sub.py 1 # => チャネ

[OS]VirtualBoxでCentOS6.5(x86_64)がKernel panicを起こした

完全に対処療法。 恥ずかしながら自分では原因がわかっていない。 忘れないようにメモしておく。 いろいろ設定したあと、何度目かの再起動のタイミングでKernel panicが発生して起動しないという事象が発生。 こちらの方法でなんとか起動させて。 [CentOS] SELinux 無効化後のカーネルパニック http://kuni255.blogspot.jp/2013/04/centos-selinux.html こちらの通り、/boot/grub/grub.confに設定を追加。 Kernel panic – not syncing: Attempted to kill init! http://h2np.net/mynotebook/post/130 設定変更後、以下の操作をそれぞれ10回ほど試した限りでは再発していません。  (1)halt+起動 (2)reboot 追記:と思ったらまた発生した。なんかもう解決できる気がしない。

[Haskell]Database.HDBC.Sqlite3でSQLite3のデータを読む。

関数型言語のHaskellというものに手を出してみました。 今回は、システムトレードで使っているSQLite3からHaskellで時系列データ(日足)を読んでみます。 備忘録として、コードを上げておきます。 import Database.HDBC import Database.HDBC.Sqlite3 main = do conn <- connectSqlite3 "investment.dbf" closes <- getHistoricalPrices conn "2712" "CLOSE" print [zenjitsuhi |(x,y) <- (zip (init closes) (tail closes)),let zenjitsuhi = ((x-y)/y)*100] hist <- getHistoricals conn "8411" print hist disconnect conn return () getHistoricals :: IConnection c => c -> String -> IO [(String, String, String, Double, Double, Double, Double, Double)] getHistoricals conn code = do stmt <- prepare conn "SELECT S.DATE,S.CODE,S.MARKET,S.OPEN,S.HIGH,S.LOW,S.CLOSE,S.VOLUME FROM STOCK_DAILY S,MARKET_LISTINGS L WHERE L.CODE=? AND L.MAIN_MARKET='true' AND L.CODE=S.CODE ORDER BY S.DATE DESC" execute stmt [(toSql code)] rows <- fetchAllRows s