GoogleAppEngine_CronSample

GoogleAppEngine_CronSample

概要

準備中。

Cronの設定と、Cron実行するファイルのセキュリティ

cron.yamlでとりあえず設定


cron: 
- description: cron action
  url: /cron/action
  schedule: every day 09:00
  timezone: Asia/Tokyo

WEB-INF/cron.xml

  • Cronの設定は、このファイルに書き込む


    • <url> 実行するURL
    • <description> 処理の説明
    • <schedule> 処理するタイミングの設定(日時などを書式に従って記述する)

<?xml version="1.0" encoding="UTF-8"?>
<cronentries>
  <cron>
    <url>/cron/action</url>
    <description>cron action</description>
    <schedule>every day 09:00</schedule>
  </cron>
  <cron>
    <url>/cron/action2</url>
    <description>cron action2</description>
    <schedule>every day 18:00</schedule>
  </cron>
</cronentries>

WEB-INF/web.xml

<web-app xmlns='http://java.sun.com/xml/ns/javaee' version='2.5'>
  <context-param>
    <param-name>rackup</param-name>
    <param-value>
Dir.chdir(&apos;..&apos;) if Dir.pwd =~ /WEB-INF$/; begin; require &apos;bundler_gems/environment&apos;; rescue LoadError; end;eval IO.read(&apos;config.ru&apos;), nil, &apos;config.ru&apos;, 1
    </param-value>
  </context-param>
  <filter>
    <filter-name>RackFilter</filter-name>
    <filter-class>org.jruby.rack.RackFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>RackFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  <listener>
    <listener-class>com.google.appengine.jruby.LazyContextListener</listener-class>
  </listener>
  <security-constraint>
    <web-resource-collection>
      <url-pattern>/cron/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
      <role-name>admin</role-name>
    </auth-constraint>
  </security-constraint>
</web-app>

Gemfile

# Critical default settings:
disable_system_gems
disable_rubygems
bundle_path ".gems/bundler_gems"
 
# List gems to bundle here:
gem "sinatra"
gem "json"
gem "appengine-apis"

config.ru

require 'appengine-rack'
 
AppEngine::Rack.configure_app(
  :application => 'twitter-bot-kanojo',
  :version => 1
)
 
require 'main'
 
run Sinatra::Application

main.rb

require 'rubygems'
require 'sinatra'
require 'yaml'
require 'appengine-apis/memcache'
require 'twitter'
 
before do
  @conf    = YAML.load_file('config.yaml')
  @twitter = Twitter.new(@conf['user']['username'], @conf['user']['password'])
end
 
get '/cron/return' do
  memcache = AppEngine::Memcache.new
  tweets = @twitter.user_timeline(@conf['kareshi']);
  tweets.each do |tweet|
    if tweet['text'] =~ /@#{@conf['user']['username']}/
      break if memcache.get('last_id') == tweet['id']
      messages = YAML.load_file('messages.yaml')['return']
      message = "@#{@conf['kareshi']} #{messages[rand(messages.length)]}"
      @twitter.update(message)
      memcache.set('last_id', tweet['id'])
      break
    end
  end
  return
end
 
get '/cron/auto' do
  messages = YAML.load_file('messages.yaml')['auto']
  message = "@#{@conf['kareshi']} #{messages[rand(messages.length)]}"
  @twitter.update(message)
  return
end

twitter.rb

require 'appengine-apis/urlfetch'
require 'json'
 
class Twitter
  def initialize(username, password)
    @req = Net::HTTP::Get.new('/')
    @req.basic_auth username, password
  end
 
  def update(body)
    url = 'http://twitter.com/statuses/update.json'
    request(url, 'POST', { :payload => "status=#{body}" })
  end
 
  def user_timeline(screen_name)
    url = "http://twitter.com/statuses/user_timeline/#{screen_name}.json"
    res = request(url)
    JSON.parser.new(res.body).parse
  end
 
  private
 
  def request(url, method = 'GET', options = {})
    options[:method]  = method
    options[:headers] = { 'Authorization' => @req['Authorization'] }
    AppEngine::URLFetch.fetch(url, options)
  end
end

参考資料

JRuby + Google App Engine の環境での Cron 実行

GAEにはCronの仕組みが用意されているので、Jrubyからはどうやって利用するのか調べてみた。

結論としてはとても簡単で、プログラム本体は何もする必要がなく、設定用の cron.xml というXMLファイルをWEB-INFフォルダの直下に置けばよいだけだった(APIリファレンス)。

ファイルの置き場所 = WEB-INF/cron.xml

cron.xmlの中身には起動したいメソッドに対応しているURLとタイミングを記述するだけ。description はGAEの管理画面で表示される cron ジョブの説明文となる。

/myapp/showというURLを毎分起動する場合

<cronentries>

<cron>

<url>/myapp/show</url>

<description>this is app description</description>

<schedule>every 1 minutes</schedule>

<timezone>Asia/Tokyo</timezone>

</cron>

</cronentries>

実行タイミング指定には多彩な記述方法が用意されている。

every 5 minutes

every 12 hours

2nd,third mon,wed,thu of march 17:00

every monday 09:00

1st monday of sep,oct,nov 17:00

every day 00:00

なお、時刻の指定に使う timezoneはタグで指定することができる(参考:wikipediaのzoneinfo情報)timezone を何もしていないと GMT になるそうだ。

cronが実行されてURLが叩かれる際のヘッダーには下記のような特殊ヘッダーが付加されるので、当該URLをcron以外の一般のユーザーには叩かれたくない場合の制御に使うことができる(他にも、web.xmlアクセス制限する方法もあるらしい)

X-AppEngine-Cron: true

appcfgでアプリは何もいじらず、cronの変更だけを反映することができるらしい。

AppCfg.rb.bat update_cron

cronはGAEの管理画面からは追加も削除もできないのだが、cronを削除をしたい場合は cron.xml の内容を下記のようにして更新すればよいらしい。

<cronentries/>

JRuby + Google App Engine の環境での Cron 実行:お題目うぉっち

GoogleAppEngine + JRubyでクリスマスまでに彼女をつくる方法 - KAYAC engineers' blog

クリスマスまでに彼女を作る方法? - Reinvention of the Wheel

GoogleAppEngine + JRubyで年越ししたけど彼女ができたよー - fujitaka's lifelog