おさかな日誌

魚類がプログラミング

ワンライナーで Rack app 走らせる and Rack のメモ

Rack、雑にワンライナーで走らせれて便利だった。

rackup -s webrick -b 'run(Proc.new { |env| ["200", {"Content-Type" => "text/html"}, ["hello"]] })'

use したい場合は -b オプションのスクリプトuse Foo; use Bar とか書いておくとよさそう。ライブラリ読む時は -r オプションで読んでくれる。

rackup -r rack/contrib -s webrick -b 'use Rack::Runtime; use Rack::ProcTitle; run(Proc.new { |env| ["200", {}, ["hello"]] })'

REAME にあるように Rack::Handler を使う場合、app を生成するのには Rack::Builer#to_app を使う。

ruby -rrack -e 'Rack::Handler::WEBrick.run(Rack::Builder.new { run Proc.new { |env| ["200", {"Content-Type" => "text/html"}, ["hello"]] } }.to_app)'

Ruby ラインタイムからサーバーを走らせたい場合だとRack::Server.startインターフェイスきれいでよさそう。

ruby -rrack -e 'Rack::Server.start(server: "webrick", app: proc { ["200", {}, ["hello"]] })'

今回の調査で人類が得たもの

  • Rack::Server.start 便利
  • config.ru に "#-p8080 -s weblick" とか書いておくとコマンドラインオプションとして Rack に渡せる
  • config.ru で warmup という app を走らせる前に一度だけ評価できる block を登録できる誰得メソッドが使える。

「今回の調査で我々は・・くっ・・何の成果も得られませんでしたぁぁ!!私が無能なばかりにただいたずらに兵を死なせ、Rack の正体を突き止めることが、できませんでしたぁぁ!!」


以下 rack のサーバー立ち上げるまでを読んだメモ

  • rackup はなにをしている?
  • config.ru はどう扱われている?

bin/rackup

Rack::Server.start を呼ぶだけ

Rack::Server.start

インターフェイス

Rack::Server.start(
  :app => lambda {|e| [200, {}, ['hello world']] },
  :server => 'cgi'
)

渡せるオプション

  • :app - a rack application to run (overrides :config)
  • :config - a rackup configuration file path to load (.ru)
  • :environment - this selects the middleware that will be wrapped around your application. Default options available are:
    • development: CommonLogger, ShowExceptions, and Lint
    • deployment: CommonLogger
    • none: no extra middleware
  • :server - choose a specific Rack::Handler, e.g. cgi, fcgi, webrick
  • :daemonize - if true, the server will daemonize itself (fork, detach, etc)
  • :pid - path to write a pid file after daemonize
  • :Host - the host address to bind to (used by supporting Rack::Handler)
  • :Port - the port to bind to (used by supporting Rack::Handler)
  • :AccessLog - webrick access log options (or supporting Rack::Handler)
  • :debug - turn on debug output ($DEBUG = true)
  • :warn - turn on warnings ($-w = true)
  • :include - add given paths to $LOAD_PATH
  • :require - require the given libraries

Rack::Server#app: app の生成

もし app オプションが渡されなかったら builder オプションを eval する。builder オプションもなかったら config スクリプト(config.ru のようなスクリプト) を探して eval する。

builder や config スクリプトから app を生成するときは Rack::Builder.new_from_stringRack::Builder.parse_file を使う。

Rack::Builder.new_from_string

config.ru で使う use とかを持ってるクラス。スクリプトを instance_eval しているかと思ったら違って .new_from_string

eval(
  "Rack::Builder.new {\n" + builder_script + "\n}.to_app",
  TOPLEVEL_BINDING,
  file,
  0
)

で builder 生成スクリプトから app を生成している。

Rack::Builder#parse_file

最後は new_from_string を呼ぶ。builder スクリプト文字列を取得するのにいくつ処理がある。

config オプションのファイル名が *.ru だったら

  • そのまま config ファイルをbuilder script として扱う
  • .ru ファイルの最初に出てくる #\-p8080 -s weblick のような行をコマンドラインオプションとして扱う
  • Ruby っぽく __END__ 以降は無視する

config オプションのファイル名が *.ru じゃなかったら

  • require してファイル名と同じコンスタントが app として扱う

Rack::Builder#warmup

builder で warmup {|app| p app } のようなやつが使える。名前の通り、app を走らせる前 (正確には to_app の時) に一度だけ評価できるスクリプトを登録できるみたい。インターネット探しても特に言及されてないし誰得メソッドだ...


おわり

「間違いだらけのソフトウェア・アーキテクチャ」読んだ

「間違いだらけのソフトウェア・アーキテクチャ ― 非機能要件の開発と評価」という本を読んだ。

全体的な感想としては、システムのアーキテクチャを考える人、つまりアーキテクトとして振る舞う人が最初にざっくり全体観をつかむために読むとよさそうだな、と思った。例えばエリック・エヴァンスの DDD 本とかあるいはマーティン・ファウラーの PofEAA 本とか読んだけどなんかしっくり来ない、みたいな人が読むとそれらの本のコンテキストが理解できるようになる、みたいな感じ。

個人的に印象を受けたポイント。ぼくは設計をすることにいっぱいいっぱいになってたんだけど、アーキテクチャの役割が理解できて、なんで設計するのかをわかるようになった気がするので、これからはより価値が高い設計ができそう。以前、職場の上司に「まずはユースケースは必ず抑えておこう。そうすればなにかしら使えるものができるから。」というアドバイスをもらったのだけど、それが自分の言葉で実感を持てるようになった。システムを作る時には、そのシステムが解決したいビジネス上の課題を明確にすると良くて、それがユースケースを記述することなんだ、という紐付けが得られた。以前はユースケースを記述する時に粒度とかってケースバイケースだし難しいな〜〜って思ってたけど、目的が自分の中ではっきり理解出来た気がするから次はあまり迷わなそう。

この本のコンテキストは上流工程とかアーキテクトの単価とかそんな感じなので、ぼくたちの Web サービス開発にはこの本で得られた知見は直接そのまま持ち込めない。これから自分の開発に持ち込んで練り練りして昇華するのが楽しみ。

以上レポっす。

memo

  • dyna book 構想: メインフレームとかしかなかった時代に、アラン・ケイが個人のエンパワメントをするようなコンピュータを考えて実現した
  • アーキテクチャ: インフラの上にアプリケーション、アプリケーションの上にはソリューション、ソリューションの上にはビジネスがある
  • 主要なほうがインターフェイスを提供する。例えばドメインロジックと物理層であればドメインロジックのほうが主要。ステレオシステムとマイク/スピーカーの例だとステレオシステムが主要。コンセントとステレオシステムの例だとコンセントが主要。
  • 普通のアプリケーションアーキテクチャはロジックような内部は関心外。でも著者は踏み込んでアプリケーションアーキテクチャのために改善していくべきだと考えている。
  • アーキテクチャの役割
    • システムの理解を助けなければならない
    • 組織の構造に影響を与えなければならない
    • 将来の見通しを良くしなければならない
    • ソースコードの実装ルールを提供しなければならない
    • システムの品質を実現しなければならない
  • 品質とは: システム品質とビジネス品質、アーキテクチャ品質 (p59)
  • 要件定義は品質目標も達成しなければならない (まぁこのへんはわりとやってる感覚あるな...)

最近 golang 書いてる

社内でパフォーマンスチューニングコンテストが開催されてテンション⤴⤴になったので Ruby 実装だった認証認可サーバーの負荷の高い部分を golang で書いてみるのやってみた。

net/http で素のまま書こうと思ったけど、ちょっと検索したら Gin Web Framework というのがあって、よさそうだったので使ってみた。よさそうポイント1、Rack middleware みたいなものがあって before/after filter みたいなのが楽そうだった。2、JSON レスポンスの生成が楽そうだった。適当な HashMap 渡してエンコードしてくれる。最終的にはちゃんとレスポンスも型付けしたほうがいいんだけど楽なやりかたもあると golang 初心者的にうれしい。3、なんかパフォーマンス⤴⤴みたいな空気感じた。あと、そんなに複雑じゃなくていつでも net/http 実装とかに移れそうなのも ++。

DB に関してはいくつか迷った。最初は golang だしなるべく薄くしたほうがよさそうという先入観があって SQL builder 使おうと思って Squirrel を選んだんだけど、最初の MySQL サーバーにつなぐのに手こずってたぶん自分がなんか間違えてたんだけど「まじかー」ってなって別のを探し始めた。検索してたら gorm が今っぽいのでは?という電波を受け取ったので gorm でセットアップしてみたらうまく MySQL サーバーにつなげたので gorm でいくことにした。

あと memcached サーバーにも繋ぐ必要があったので、探してみたけどあんまり選択肢ないのか gomemcache にすぐ決まった。使用感はバッチリ。単純で良さがある。

計3日間ほど書いた Web API サーバー書いたので感想まとめとく。

  • チュートリアルがよく出来てて言語仕様もコンパクトなので初めての人でもすぐに書けるの最高。
  • シンプルなロジック書くには golang くらいの型システムでも気持よくかける。今回書いたものだと多相性とかなかったのでコンパイラの型チェックの恩恵受けながら気持よく書けた。
  • エラー処理で多値を返して片方の nil チェックとかするの書く前は厳しすぎるという先入観があったけど、書いてみるとそんなに書きづらくない。コード量は確実に多くてダルさはある。
  • ゼロ値と失敗表現がなんか微妙だなぁと思う。null にまつわる失敗表現の課題を解決したいのかな?って感じたけど、あんまり失敗をうまく表せてない気がする。あと失敗表現に一貫性がなくて気持よくない。os.Getenv で失敗した時は空文字列が返ってきてウッってなる。失敗を表現する型がほしい気がする。ただよく知られてるやり方だと合わないと思うから、golang っぽいなにかを期待している。
  • 多値を返しにくい設計の時に、失敗の時に nil 返して nil チェック忘れて実行時にアアッってなる場面が何回かあった。コンパイル時になんとかしたいと思う。「Go: The good parts」とか出たら bad parts の章で紹介されるような悪いパターンなのかもしれない。でも多値返したくない∧失敗したい∧ゼロ値だと不十分なケースはあるような気がする...
  • 圧倒的ライブラリ不足。業務アプリケーションを素早く書くのは厳しい。
  • 型まわりで不自由さを感じることが多い。型付けする楽しさが薄い。

個人的な不満はあるけど、言語としての思想は共感できるし書いてて楽しかったです。あとパフォーマンス意識して書かれた RoR アプリケーションを雑に書きなおして雑に10倍くらい速くて体験。

Ruby 用天気予報ライブラリの weather_jp V2 出した

天気予報取得用の Ruby gem の weather_jp という gem を2年半前くらいに作ったのだけど、MSN が RSS 配信を止めたらしく動かなくなったので Twitter でメンションが来た。バックエンドを大幅に変えなくてはいけなさそうなのでせっかくだし V2 にした。

"V2 upgrade guides" という言葉を自分のライブラリで使う日が来てうれしい。使ってくれる人がいないと V2 を出すモチベーションがわかないから、V2 になれるくらいは貢献してるんだなぁ良かったぁ、という感じ。

2年半前の自分が書いたコードというのはプログラミング初心者(半年目)の自分が書いたコードというやつで、かわいがりがあるコードだった。

で、今回は前よりはまともに書けるようになったわけなんだけど、以前は困ったことがなんで今回困らなくなったか考えてみた。

前はまず「どの処理をどこに置くのがいいのか」ということにひたすら悩んだ。結果ドメインモデルのコンストラクタでデータフェッチする、みたいなひどいことになっていた。こういう API call されて、リクエストを元にデータフェッチして結果を返す、みたいな場合は大体層状アーキテクチャを頭に置いておいたらうまくいくような気がする。この処理はリクエストをパースするような処理なのか、ディスパッチするような処理なのか、データフェッチなのか、ドメインモデルへの変換処理なのか。DDD と DDD本読むために必要な知識群が役に立ちましたね...

あと当時は初めて自動テストを書いたので、なにがなんだかさっぱりだった。最初は自動テスト中に HTTP リクエストをサーバーに飛ばしていた。これくらいの粒度のライブラリをてっとりばやくテストを仕上げるには、レスポンスを予め保存しておいてデータフェッチの部分でスタブして保存したレスポンスを返して 、あとは一番外側のインターフェースからざっくりとテスト書いていって、C0 カバレッジを100%にするようにスタブしたりクエリを変えたりテスト追加すると C0 カバレッジが 100% になるころには大体バグが潰れている。ソフトウェアがシンプルだとテストもシンプルでいいしとても楽だ。

あとリファクタリングリファクタリングで自動テスト壊さずに進めるの大事だと思うんだけど、けっこう難しかったりする。今回はわりと上手くいってテスト壊さずに進めれたから精神的に楽だった。V2に合わせていろいろ変更する前に、気になるところを冗長でもいいから単にリファクタリングするだけにとどめておいたのが良かったのかなぁって思ってる。

MySQL のサブクエリで気をつけとくこと

MySQL のクエリ最適化のために調べてたらサブクエリの話題が出てて、「相関関係のあるサブクエリは遅い」とか書いてあって、サブクエリ周りで知っておくといいこと得られたので書いておく。なぜMySQLのサブクエリは遅いのか。 を読んで手元のデータベースで適当に試してなるほどなるほど言った記録。


MySQL の EXPLAIN 氏によるサブクエリの分け方としては

  • PRIMARY 外部クエリを示す。
  • SUBQUERY 相関関係のないサブクエリ。
  • DEPENDENT SUBQUERY 相関関係のあるサブクエリ。
  • UNCACHEABLE SUBQUERY 実行する度に結果が変わる可能性のあるサブクエリ。
  • DERIVED FROM句で用いられているサブクエリ。

があるようだ。

DEPENDENT SUBQUERYおよびUNCACHEABLE SUBQUERYの場合はサブクエリが行の評価の度に実行される

と書いてあったので、なるべく避けたい存在かもしれない、と思って Dependent SubQuery を調べてみた。

手元のデータベースで適当にサブクエリをいくつか書いていて「あれ?これ相関関係あるの?毎回実行しなくてもいいんじゃ」というクエリがあった。調べてみると:

MySQLは内部的にINを直接処理することができないので、EXISTSに変換することでSQL的には相関のないサブクエリも相関サブクエリになってしまうのである。

なるほど。適当にサブクエリを書くとまず IN (SELECT ....) の形が思い浮かんで IN 句を使っていた。

サブクエリでインデックスをつけるべきカラムは、サブクエリが結果を返すカラム

なるほど。

おすすめされたもの

いつもおすすめ情報聞いたら iPhone のブラウザで開いておいてメモ代わりにしてるんだけど、多くなってきたのでメモ残しておく。

恵比寿 京しずく

恵比寿周辺でのおすすめ和食。値段はディナーで¥10,000~¥14,999(食べログ情報)!!しかしコスパは良い、とのこと。いつか行ってみたい...

浅草 やっこ

おすすめ鰻屋。特別うまいというわけでないが、相性がいい日に食べると涙出るくらいにはおいしい。ぼくもそんなお店見つけたい。

帝国ホテル レセゾン

特別な日におすすすめのフレンチ。料理だけでなくサービスも最高なようだ。フレンチにかかるコスト、半分は料理で半分はワインだからノンアルコールだとおいしい料理が食べれる。

雑誌 料理王国

料理系専門誌初心者におすすめ。おおきな書店でも見つけれる。

ニュートロールス

クラシックとロックの融合。いわゆるグルーヴではなくクラシックから派生して、曲の組み立てをしているらしい。

クラフトワーク

電子音楽の先駆け。いろんなミュージシャンに影響与えてる。

Ruby でも Zipkin がしたい! 戀

Ruby でも Zipkin がしたい!の続き

前回は1つのアプリケーションで Zipkin collector に情報をおくるところまでやった。今回は関連がある2つのアプリケーションのログを Zipkin collcetor に送る。client <-> test-app2 <-> test-app という経路。

まずは HTTP client で Zipkin の規約にそった HTTP request header を送らなければならない。faraday middleware が良さそうだなとおもって実装しようとしたら、すでに実装されているものを見つけた。https://github.com/Oscil8/faraday-zipkin

とりあえず faraday-zipkin を使って送ることにしてみる。test-app を port: 4567 で立てて、test-app2 を port: 3000 で立ててリクエストハンドラ内で test-app のデータをフェッチするようにする。

require 'base64'
require 'open-uri'

require 'pry'
require 'sinatra'
require 'zipkin-tracer'
require 'faraday'
require 'faraday/zipkin'

set :port, 3000

config = {
  service_name: 'test-app2',
  service_port: 3000,
  sample_rate: 1,
}

use ZipkinTracer::RackHandler, config

get '/' do
  conn = Faraday.new(url: 'http://localhost:4567') do |faraday|
    faraday.use  Faraday::Zipkin::TraceHeaders, 'test-app2'
    faraday.response :logger
    faraday.adapter  Faraday.default_adapter
  end
  r = conn.get
  r.body + '!!!!'
end

しかし bundler の依存解決ができない。とりあえず gemspec を書き換えてみる。

お次は、client_recv アノテーションの binary annotation の送信で encode 関係で実行時エラーになる。調べてみると faraday-zipkin は i16 で http status を送信しているつもりが、thrift ライブラリの内部では string として送信しようとしてバイト列を無理やり UTF8 に変換しようとしてダメっぽい。なので、ひとまず http status は string で送信することにした。このへんの work around 込みのブランチは https://github.com/taiki45/faraday-zipkin/tree/zipkin-tracer-compatible にある。

work around を入れて実際にサーバーへリクエストを送ると無事 Zipkin collector に送信された。

Zipkin web で見てみるとこんな感じ。

f:id:aladhi:20141102030356p:plain

f:id:aladhi:20141102030422p:plain

アノテーションも見れる (けど2重で送られているような気がする...)

f:id:aladhi:20141102030444p:plain

アノテーションの内容で検索もできる。

f:id:aladhi:20141102030519p:plain

実際にサービスに組み込むならもうちょっとアノテーション増やして問題発生時に特定のリクエストを追えるようにしたいなーという感じ。faraday middleware 書くか〜。