読者です 読者をやめる 読者になる 読者になる

TweetStreamとTwitterというGemでUserstreamを使う

ruby プログラミング twitter

現在,rubyからtwitterAPIを叩くのに便利なGemとして,twitter gemがある.


sferik/twitter · GitHub


これでRESTのAPIはほぼすべて叩ける仕様になっている.



RESTのAPIはこれでいいのだが,ひとつ重要な機能として,Userstreamがある.

twitterAPI上限については,発表があるたびに厳しくなっていく一方で,正直タイムラインの取得にRESTのAPIを使うのは,どんどんつらくなっている.

だからこそ,常時接続が確立できるのであれば,出来る限りUserstreamを使いたいところだ.

特に,FavやRT,リプライ,DM,さらにはフォローイベントまでを取得しようと思うと,Userstreamを使うことは避けられない.


昔はTweetStream一択だった


以前は,rubyからUserstreamを触るとなると,tweetstream一択だった.


tweetstream/tweetstream · GitHub



もともとStreaming用に作られていることもあり,RESTのAPIを叩く機能は積んでいない.
Userstreamのみのために使うGemだった.


しかし,tweetstreamには大きな制約があって,

Userstreamを複数本張ることができない.



これは違うアカウントで,違うアクセストークンを使っていても,ひとつのサーバーから2つのUserstreamを張ることができない(もちろん,Consumer Keyが違うものであれば問題なく張ることができます).



同じアカウントであれば,それは拒否されても仕方ないのだが,別のアカウントでも切断されちゃうと不便だなー……というのがずっと不満なところだった.

twitter gemにUserstreamが実装された


ここで大きな変化が訪れ,最近,twitter gemにUserstreamを張る機能が実装された.


つまりこれで,RESTのAPIもUserstreamも,すべてtwitter gemひとつで完結することになる.

おまけに,なんとUserstreamを複数本張ることができる!!!



素晴らしい.


これでひとつのサーバーで複数アカウントのUserstreamを張ることができる!!



ただひとつ問題が……


Userstreamなので,tweetstreamもtwitter gemも基本的に無限ループに入る.
だから,エラーや例外が発生すると,その時点でプロセスが終了してしまって,もう一度Userstreamを張り直さないといけない.

どう考えてもそこは外せないのに,twitter gemのUserstream,たまに例外吐きます.

 unexpected token at '{"created_at":"Wed Jan 21 11:46:32 +0000 2015","id":557866833886597121,"id_str":"557866833886597121","text":"\u304a\u3044\u3002\uff\
15\uff0d\uff13\u304a\u3044\u3002\u30dc\u30b9\u30c9\u30ed\u304f\u308c\u3088\u610f\u5473\u308f\u304b\u3093\u306d\u3048\u3088\uff01\uff01\uff01","source":"\u003ca href=\"http:\/\/twitter.com\" rel=\"nofollow\"\u\
003eTwitter Web Client\u003c\/a\u003e","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,\
"user":{"id":123117952,"id_str":"123117952","name":"\u3055\u306b\u308f\u3059\u305a\u63d0\u7763(\u5fa9\u5e30)","screen_name":"suzu_kousei","location":"\u672c\u547d\uff1a\u5927\u83c5\u53ca\u5ca9\u4eca\u624b\u91\
d1\u8352\u307e\u3053\u306f\u308b\u3000\u30ab\u30d5\u30a7\u30d1\u30ec\u7d44","profile_location":null,"url":null,"description":"\uff97\uff7c\uff9e\uff92\uff9dZN\u6b4c\u3046\u30b8\u30e7\u30cb\u30c1\u30ad\u3002SZ\
BH\/\u3046\u3089\uff27\u304a\/\u4e59\u30b2\/SHK\/\u68b6\u6d66\u7d44\/\u9280\u82f1\u4f1d\/\u5c0f\u91ce\u4e3b\u4e0a\/\u658e\u85e4\u5343\u548c\u69d8\u53a8\/\u8266\u3053\u308c\/\u5200\u4e71\u3000\u8150\u3063\u305\
f\u30aa\u30bf\u30af \u7537\u6027\u58f0\u512a\u3055\u3093\u304c\u597d\u304d\u3067\u3059\u3002\u3067\u3082\u5973\u6027\u58f0\u512a\u3055\u3093\u306e\u307b\u3046\u304c\u3082\u3063\u3068\u597d\u304d\u3067\u3059\u\
3002\u65e2\u5a5a\u5b50\u6301\u3061\u3002\u305f\u307e\u306b\u9375\u3002\u30d0\u30ec\u30fc\u3057\u3066\u81ea\u8ee2\u8eca\u3053\u3044\u3067\u6c34\u6cf3\u306e\u6cbc\u3092\u6cf3\u3050\u4eba\u3002\u30b7\u30e7\u30fc\
\u30c8\u30e9\u30f3\u30c9\u306e\u5e95\u8fba\u63d0\u7763\u3002\u30a2\u30a4\u30b3\u30f3\u306f\u3051\u3081\u3053\u3055\u307e\u304b\u3089\uff01","protected":false,"followers_count":294,"friends_count":236,"listed_\
count":25,"created_at":"Mon Mar 15 02:07:36 +0000 2010","favourites_count":2337,"utc_offset":32400,"time_zone":"Tokyo","geo_enabled":false,"verified":false,"statuses_count":102462,"lang":"ja","contributors_en\
abled":false,"is_translator":false,"is_translation_enabled":false,"profile_background_color":"FF6699","profile_background_image_url":"http:\/\/pbs.twimg.com\/profile_background_images\/468981819581267968\/FIe\
o9hN-.png","profile_background_image_url_https":"https:\/\/pbs.twimg.com\/profile_background_images\/468981819581267968\/FIeo9hN-.png","profile_background_tile":true,"profile_image_url":"http:\/\/pbs.twimg.co\
m\/profile_images\/476510662307246080\/Q9lXFjPz_normal.png","profile_image_url_https":"https:\/\/pbs.twimg.com\/profile_images\/476510662307246080\/Q9lXFjPz_normal.png","profile_banner_url":"https:\/\/pbs.twi\
mg.com\/profile_banners\/123117952\/1421504667","profile_link_color":"B40B43","profile_sidebar_border_color":"FFFFFF","profile_sidebar_fill_color":"E5507E","profile_text_color":"362720","profile_use_backgroun\
d_image":true,"default_profile":false,"default_profile_image":false,"following":null,"follow_request_sent":null,"notifications":null},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_co\
unt":0,"favorite_count":0,"entities":{"hashtags":[],"symbols":[],"user_mentions":[],"urls":[]},"favorited":false,"retweeted":false,"filter_level":"low","lang":"ja","timestamp_ms":"1421840792520"}'
2015-01-21T11:46:32.755Z 28827 TID-orsxhg424 WARN: /home/akira/.rbenv/versions/2.1.3/lib/ruby/2.1.0/json/common.rb:155:in `parse'
/home/akira/.rbenv/versions/2.1.3/lib/ruby/2.1.0/json/common.rb:155:in `parse'
/srv/www/whalebird.server/vendor/bundle/ruby/2.1.0/gems/twitter-5.12.0/lib/twitter/streaming/response.rb:27:in `block in on_body'
/srv/www/whalebird.server/vendor/bundle/ruby/2.1.0/gems/twitter-5.12.0/lib/twitter/streaming/response.rb:25:in `each'
/srv/www/whalebird.server/vendor/bundle/ruby/2.1.0/gems/twitter-5.12.0/lib/twitter/streaming/response.rb:25:in `on_body'
/srv/www/whalebird.server/vendor/bundle/ruby/2.1.0/gems/twitter-5.12.0/lib/twitter/streaming/response.rb:16:in `<<'
/srv/www/whalebird.server/vendor/bundle/ruby/2.1.0/gems/twitter-5.12.0/lib/twitter/streaming/response.rb:16:in `<<'
/srv/www/whalebird.server/vendor/bundle/ruby/2.1.0/gems/twitter-5.12.0/lib/twitter/streaming/connection.rb:22:in `stream'
/srv/www/whalebird.server/vendor/bundle/ruby/2.1.0/gems/twitter-5.12.0/lib/twitter/streaming/client.rb:116:in `request'
/srv/www/whalebird.server/vendor/bundle/ruby/2.1.0/gems/twitter-5.12.0/lib/twitter/streaming/client.rb:90:in `user'
/srv/www/whalebird.server/app/workers/userstream_worker.rb:20:in `perform'

これ,jsonのparseでエラーが起きているのですが,確実にGemの内側の話です.

https://github.com/sferik/twitter/blob/master/lib/twitter/streaming/connection.rb


このファイルで,返ってきた値のbodyをresponseに突っ込んでいるのですが,ここでjsonがparseできないみたいなんですね.

もうちょっと解析してみないとわからないですが…….


ただ,例外出すのが,すごくたまに(8時間に1回くらい)で,割りと普通のツイートを解析しようとして落ちているので,なかなか原因はわからないんですね.
あと,デバッグしようにもなかなか同じエラーに出くわさないので…….


ここに同じエラーの質問が立っているので,こちらの経過も見守ろうかと思います.

https://github.com/sferik/twitter/issues/641