Rails チュートリアル 1章 勉強メモ15

Rails チュートリアルの勉強メモ

railstutorial.jp

  • 前回までのあらすじ
    • application_controller を読んでたら protect_from_forgery が気になって脱線してしまった

protect_from_forgery

  • 前回眠すぎてよくわかんなかった origin ちょっとググってみた

qiita.com

これ見ると前回の

 => 127:   before_action :verify_authenticity_token, options
    128:   append_after_action :verify_same_origin_request

が token 発行して同一の origin かどうか検証してるってことがなんとなくわかる。

hello world

class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception

  def hello
    render html: "hello, world!"
  end
end
  • protect_from_forgery に釣られましたが無事なんとか終了

config/routes.rb

Rails.application.routes.draw do
  root 'application#hello'
end
  • ルートを application コントローラーの hello メソッドに設定している
  • rails s でサーバーを立ち上げて、ブラウザなどでhttp://localhost:3000 に入ると hello, world! と表示される

backtrace

  • hello で backtrace をしてみて色々気になってきた
  • Puma からみてみる

Puma::ThreadPool

puma-3.12.1/lib/puma/thread_pool.rb @ line 135 Puma::ThreadPool#spawn_thread:

    130:           if @clean_thread_locals
    131:             ThreadPool.clean_thread_locals
    132:           end
    133: 
    134:           begin
 => 135:             block.call(work, *extra)
    136:           rescue Exception => e
    137:             STDERR.puts "Error reached top of thread-pool: #{e.message} (#{e.class})"
    138:           end
    139:         end
    140: 
  • 一番深いメソッド
  • thread_pool.rb のコメントを読んでみる

  • Puma の worker は、 thread pool を持っている

  • まず、クライアントへの接続はPuma::Serverで行われる
  • それは Puma::Client にラップされ、Puma::Reactorに渡される?
  • リクエストの準備が整うとPuma::ThreadPool#<<を介して、スレッドプールに渡され@todoに保存される
  • プール内の各スレッドは内部ループを持ち、@todo からリクエストをプルして進めていく

  • def spawn_thread をさらっとみてみる

  • @spawned で立ち上っているスレッドの数がわかる
[1] pry(#<Puma::ThreadPool>)> @spawned
=> 5
  • Thread.new でスレッドを作る
  • Thread.list で立ち上がっているスレッドを確認できる
 #<Thread:0x00007fd32c272fc0@puma 001@$HOME/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/puma-3.12.1/lib/puma/thread_pool.rb:89 sleep_forever>,
 #<Thread:0x00007fd32c272e80@puma 002@$HOME/mitsuki/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/puma-3.12.1/lib/puma/thread_pool.rb:89 sleep_forever>,
 #<Thread:0x00007fd32c272cf0@puma 003@$HOME.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/puma-3.12.1/lib/puma/thread_pool.rb:89 run>,
 #<Thread:0x00007fd32c272b60@puma 004@$HOME/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/puma-3.12.1/lib/puma/thread_pool.rb:89 sleep_forever>,
 #<Thread:0x00007fd32c272a48@puma 005@$HOME/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/puma-3.12.1/lib/puma/thread_pool.rb:89 sleep_forever>,
  • これら以外にもスレッドは立ち上がっていたが、puma のスレッドは5つ
          mutex.synchronize do
            while todo.empty?
              if @trim_requested > 0
                @trim_requested -= 1
                continue = false
                not_full.signal
                break
              end

              if @shutdown
                continue = false
                break
              end

              @waiting += 1
              not_full.signal
              not_empty.wait mutex
              @waiting -= 1
            end

            work = todo.shift if continue
          end
  • todo が空の場合
  • mutex.synchronize は ブロックの中の処理の間はロックされる
  • 削除要求が ある場合
    • 削除要求を-1する
    • continue を false に
      • mutex.synchronize 抜けた後の処理を続けるかどうかのフラグ
    • not_full.signal
      • not_full, not_empty は状態変数
      • not_full は スレッドが full の時に処理を止めるのかな?
      • not_full.signal は not_full状態変数で待っているスレッドを再開させる
      • スレッド終了するからmutex空くってことかな?

rurema.clear-code.com

  • シャットダウンのとき
    • プールのすべてのスレッドを終了するのでそのまま終了
  • todo が空で、削除要求もなし、シャットダウンでもない場合
    • @waiting を +1
    • スレッドがフルでないことを知らせる
    • スレッドが空なので待機
    • not_empty.signal がきたら @waiting を -1 して再開
  • work に todo の要素を取り出して継続

  • @clean_thread_locals が true の時

    • Thread.current[key] = nil してる
  • block.call(work, *extra)

    • block は puma-3.12.1/lib/puma/server.rb の run
    • server の 実行をする
    • SSLError, HttpParserError, ConnectionError, EOFError の時の処理をする
    • 今回は通る、そして process_now は true なので process_client client, buffer
    • ここの client, buffer は block.call(work, *extra) での work, extra
  • puma-3.12.1/lib/puma/server.rb

    • ここではクライアントへの接続を行なっている
    • handle_request(client, buffer)
      • リクエストを受け取る処理
  • puma-3.12.1/lib/puma/server.rb @ line 660 Puma::Server#handle_request

    • status, headers, res_body = @app.call(env)
    • env にはリクエストのいろんな情報がある
      • "HTTP_HOST"=>"localhost:3000"とか
      • "HTTP_USER_AGENT"=> safariから〜みたいなのとか
  • puma-3.12.1/lib/puma/configuration.rb @ line 227 Puma::Configuration::ConfigMiddleware#call

    class ConfigMiddleware
      def initialize(config, app)
        @config = config
        @app = app
      end

      def call(env)
        env[Const::PUMA_CONFIG] = @config
        @app.call(env)
      end
    end
  • Configuration オブジェクトに env を入れる
  • app は rails の app
    • ここでは hello_app
  • @app.call(env) でついに hello_app を呼び出す

  • backtrace で application_controller の hello までの道のり

    • イマココ --> #58 Puma::Configuration::ConfigMiddleware.call(env#Hash) at $HOME/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/puma-3.12.1/lib/puma/configuration.rb:227
    • 深すぎる