Hatena::ブログ(Diary)

駄文生産所 このページをアンテナに追加 RSSフィード

2013-06-10

Herokuのアカウントが止められたわけだが

土曜日(約48時間前)からHerokuで動かしていたサービスが止まりログインしようにも

Internal Server Error

Our apologies, but it appears that something has gone wrong. Our engineers have been notified. If this problem persists, please e-mail support.

と、利用不能になっていた。

週が明けて、極めて拙い問い合わせメールへの回答によると、ここ数日で行ったスパマー/フィッシングサイト対応に引っかかっていたそうだ。アプリバックアップされているそうだから、そのうち利用できるようになるはずだが、まだアカウントは回復していない。


スパマー/フィッシングサイトの検出アルゴリズムfixされるというから今後はどうなるか分からないが、今回どの程度のことをしてスパマー/フィッシングサイトに認定されたのかをここに記録する。

2013-05-31

リンク集からRSS/Atomフィードを収集するWebアプリ/スクリプト

Feed TrawlerというWebアプリ作成した。


「Feed Trawler」

http://feedtrawler.devgoodies.net/


ブログリンク集的なページのURLから、一段階分リンクをたどり、RSSAtomフィードがあった場合、「ページのタイトル」「リンクURL」「フィードURL」を抜き出し、リストする。

こんなものを使うのは、ろくでなしに決っているので、広告の配置もろくでもない感じにした。一度やってみたかった。


環境は、Groovy/Grails + Heroku。それに、GParsとjsoup。

すぐにHerokuの制限であるメモリ512MB、30秒の制限に引っかかってしまうのが残念なところ。

GParsで30スレッド並行で処理しようとすると、メモリが溢れ、強制停止されてしまった。いまはとりあえず5スレッドに設定している。


やると決めて制作に一日。作業時間は、コア部分、Web部分、見た目調整、雑務で、各3時間程度。

以下は最初期に殴り書きした整理前のスクリプトGroovyの実行環境が準備できるなら、これで十分役には立つ。



import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import org.jsoup.select.Elements
import groovyx.gpars.GParsPool

def url = "http://www.j-pfa.or.jp/blog"

Document rootDoc = Jsoup.connect(url).get()
Elements rootAnchors = rootDoc.getElementsByTag('a')

List candidates = []
rootAnchors.each {Element elem ->
    def text =  elem.text()
    def link = elem.attr("href")

    if (link =~ /^https?\:\/\//) {
        candidates << link
    }
}
candidates.unique() // 副作用

// 若いインデックスのものほど優先
List feedTypes = [
    'application/atom+xml',
    'application/rss+xml',
    'application/rdf+xml',
    'application/x.atom+xml',
    'application/xml',
    'text/xml',
]

def selectFeed(List types, List elements) {
    def selected = null
    types.each {type ->
        if (selected == null) {
            selected = elements.find {Element elem -> elem.attr('type') == type}
        }
    }
    return selected
}

GParsPool.withPool(30) {
    candidates.eachParallel {String linkUrl ->
        try {
            Document doc = Jsoup.connect(linkUrl).get()
            Elements links = doc.head().getElementsByTag('link').findAll {Element elem -> elem.attr('rel') == 'alternate'}

            List feedLinks = links.findAll {Element elem -> feedTypes.any {type -> elem.attr('type') == type}}
            feedElem = selectFeed(feedTypes, feedLinks)

            if (feedElem) {
                String feedUrl = feedElem.attr('href')
                if (! feedUrl.startsWith('http')) {
                    URL u = new URL(linkUrl)
                    def protocol = u.protocol
                    def host = u.host
                    feedUrl = "${protocol}://${host}${feedUrl}"
                }

                println ("${doc.title()}\t${linkUrl}\t${feedUrl}")
            }
        } catch (e) {
            // println(e.message)
        }
    }
}

2013-05-28

Groovy/Grailsでフィードアグリゲータを作成し、Herokuで運用してみた

ここ20日間ほど、Groovy/Grailsフィードアグリゲータを作っていた。

これにどれほどの需要があるかはさておき、自分の見たい情報が整理されて出てくるところまでは来た。


ハンドボールナビ」

http://handballnavi.com


JVMで動くアクション型のフレームワークを調査するところから始めて、練習課題的にフィードアグリゲータを設定し、10日後に公開、その後10日で肉付けして、現在に至る、というのはそう悪いペースではないだろう。


以下、チラシの裏


Webフレームワーク

もともとやりたいことは別にあって、それにはSeasideフレームワークが向いていないことは分かっていた。Smalltalk環境を活かした生産性は抜群で、実行速度も十分(Javaの1/5程度の速度)だと思うが、コンポーネントベースフレームワークは内向きのプロダクトに向いていて、外向きにはイマイチだ。現状、SmalltalkWeb開発となると、実質Seaside一択になってしまうのは辛いところで、WAFの自作からやるのは遠すぎる。


Webフレームワークは、以下の条件で調べていた。


Railsは実行速度で、PHP系統クロージャで外した。残るのはJVMGroovyScalaV8JavaScriptといったところだったが、JVMを選択した。JavaScriptクロージャは気に入らない。


Grails、Play、SpringMVC、SAStrutsの資料を眺め、いくらかサンプルを書いたところでGrailsに決めた。

SpringMVC、SAStrutsGroovyScalaから使うには冗長で、他の2つに比べて設定記述も多く、面倒な印象を受けた。

PlayはViewテンプレートは素晴らしいが、Javaから使うには冗長な記述で、Scalaの構文は読みにくかった。なにより検索結果にPlay1とPlay2の情報が錯綜するのに参った。

消去法的にGrails。受けが広い気がした。


そこそこの数の資料を見て回ったが、特に意志決定を後押ししたのは以下の3つ。

Grails vs. Play Framework comparison」にあった、 "Scala is for writing, not for reading"には、まぁそうよね、とPlayのサンプルを読み書きした後に軽く同意した。


Grails

ドメインオブジェクトリファクタリングに起因すると思われるHibernate周りの異常動作、変更時の再起動必要性の分かりにくさ、CoC(Conversion of Control)なフレームワーク記述が少ない反面「覚えゲー」なのではないかという疑問等々、引っかかる部分があるにはあったが、全体に良いモノだった。Seasideのように、問題発生時は潜って自力解決しつつ仕組みを徐々におさえていく、ということができないのは残念だが、公式ドキュメント書籍Blog、StackOverflow等々、日本語では少ないものの、必要情報がたいてい手に入るのは素晴らしい。


Grails in Action」にちゃんと目を通すのが良いようだが、必要なところだけつまんで、今のところはサボっている。

数年前にデブサミで頂いた「Grails徹底入門」は、M、V、Cで別々の章立てになっている関係や、コード解説が部分部分になっているためか、少々全体像が把握しづらかった。特にWebFlowのあたり。一緒に頂いたサンプル入りCD-ROMを紛失してしまったのが悔やまれる。


IDEにはIntelliJ Idea Ultimateを使用した。他は試していない。大層賢くて、Grails名前解決な部分もちゃんと補完し、リファクタリング時も追随してくれる。昨年使用していたEclipseに比べると雲泥の差だ。2日前に199ドルを支払ったが、応分価値はある。

とはいえ、デバッグ環境はイマイチだ。Java系はどれも及第点を与えられない。

昨年作成したGInspectorがそれなりに役立った。


Heroku

公開しないか、VPSのお試し期間で試すかで迷っていたが、Heroku無償で使えることが分かったのでそこに乗らせてもらった。コンパイル時にメモリオーバーでコケたり、ログが最新100行分しかとれなかったり(注:ちゃんとコマンドを調べれば取れることがわかる)、デプロイに失敗するがappを作り直すと同じコード成功するとか、なにかと不可解で厳しいこともあったが、無償には代えられない。


データは基本オンメモリ設定のH2 Databaseに保持し、残したいものは、これもHerokuRESTサーバをたて、PostgreSQLのTEXTにJSONを突っ込んでいる。無償範囲は10000行に制限されているが、データ容量の制限は明記されていないのでこのようにした。運用が面倒だ。失って困るデータはないので、あまり真剣対応していない。


Herokuは1時間アクセスのないサービスアイドリング状態にする。それを避けるため、cronで定期アクセスするのが定番のようだが、これもGrailsのquartz pluginを使用し、設定されたURLに定期アクセスするサーバを2つ立て、相互にアクセスさせるようにした。オーバーだが、Grailsに慣れる過程でやってみた。それなりに役立ってはいるし、WorkerDynoも要らない。


本体の更新は、2つのappに交互にデプロイし、初期設定と動作確認を終えた時点でDNSの設定を切り替えている。


Grails on Heroku無償

イニシャルの状態でもアプリサイズ(Slug)が80MBを超え、コンパイル時のメモリ使用量も制限の512MBを超えるのは難。

アプリサイズ最大200MB、メモリも基本512MB以内という制限があり、メモリ使用量がこの1.2倍(だったか?)を超えるとプロセスが止められてしまい、なにかとイライラさせられる。Railsだとアプリサイズで30MB(某所でちょっと目にしただけだが)程度に収まるようなので、当たり前だが、Ruby向けに作られたサービスということだろう。


Herokuの印象

ちょっと触っただけで言うのもなんだが、遠くて、小さい。

なにかと割り切りが必要で、継続的な前進や、日常的なメンテ必要となるシステムに、Herokuは使いにくい。

枠をはみ出す毎に課金必要になる。課金必要な程度にシステムが膨れた場合は、国内VPSクラウドサービスを借りるのが現実的だと思う。

フリーライダーがどの口で言うか、という話ではあるが印象はそうだ。


終わり

  • Grails/Groovyは良いバランスだと思う。Javaの肩に乗れるのも良い
    • Hibernateは気に入らない。テーブル3つJoinして、、、な処理は、いまのところあきらめて無駄SQLを発行している。本当に必要ならMyBatisを使う
  • Herokuは限定的に、割り切って使う