專案
現在到了一個沒有 .NET 的工作環境,臨時趕一專案需要趕快做一個 EC 平台串金流。
基於對 Rails 的愛,選了 Rails 來開發這個專案。時間雖然很趕,但 Rails 的框架很多東西都做好了,開發起來就很輕鬆。
設施&佈局
佈署目標是 Google Computing Cloud, 本來打算用 Container Engine 但因為 SSL 已經設定好在一個 Computing Cloud VM 上。找了一下暫時找不到怎麼把 Computing Enging 的 vm 跟 Container engine 內網串起來。所以還是建了 Ubuntu VM 來佈署。
在已經掛了 SSL 的 Nginx 上做簡單的 Reverse Proxy:
upstream store_milife { server 10.x.x.x; server 10.x.x.x; server 10.x.x.x; } server { location / { proxy_pass http://store-sites; } }
透過內網把 Request 轉到我們部署了 Rails 的VM。這裡我們裝了另一個 Nginx 讓它先處理 Static asserts 再處理其他的 Request,我們把這些其他的 Request 轉到 unicorn 建立的 unix socket
upstream app { server unix:/opt/store/shared/sockets/unicorn.sock fail_timeout=0; } server { root /opt/store/public; try_files $uri/index.html $uri @app; location @app { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_redirect off; proxy_pass http://app; } ... }</pre> <h2>Session</h2> 這麼一來因為我們有了 Load Balance 的機制,Rails App 就得把 Session 往共用的儲存空間放,不能再放本機記憶體,<a href="https://github.com/rails/activerecord-session_store" target="_blank" rel="noopener">這個 GEM</a> 可讓你很輕鬆的設定資料庫為 Session store,在原本的資料庫多一個表格存放 Session gem 'activerecord-session_store'
送信功能
結帳後送信在 Rails 也是很簡單的,Rails 把送信的機制做在 Active Job 裏。 Active Job 讓 Rails App 在需要執行一些背景工作時可以透過一致的介面溝通。你可以選用你喜歡的 Queue 來處理在 Application 裡送出的 Job 。
Rails 預設的 Async Queue 是跟著 application 跑在同一個執行緒的。在開發這個專案的時候我選用了 Resque 。Resque 是把 Queue 存在 Redis 裡。
Email 的設定:
config.active_job.queue_adapter = :resque # 根據不同的環境來設定 Queue 可避免類似 QA 環境的 Job 被 Production 吃到的問題 config.active_job.queue_name_prefix = "store_#{Rails.env}" # 把要寄出的信件開在開發機上的瀏覽器預覽方便開發調整 config.action_mailer.delivery_method = :smtp # 正式環境下用 :smtp 沒問題,但是測試環境下我用了 `letter_opener` # 測試環境: config.action_mailer.delivery_method = :letter_opener
接下來我們可用
$ rails g mailer StoreMailer
來建立幫我們寄件的郵差,在 app/mailers/store_mailer.rb 裏我們可以設定要寄出 Email 的內容。
寄信對 Rails 來說,就像在 Render HTML 頁面一樣,在 app/views/store_mailer
裡我們將設定對應的 view 作為 Email 的內容。比較不同的是,Mailer 要使用 heleper 的話需要自己指定 helper 不會自已載入。
class StoreMailer < ApplicationMailer helper :application # Load app/helpers/application_helper.rb ... end
另外在 mailer class 裡我們需要設定內容,def order_notification
會對應到 app/views/store_mailer/order_notification.erb
def order_notification(user_session, order) @header = { :title => '訂購完成通知', :disclaimer => true, } @user = user_session @order = order mail(to: @user['email'], subject: @header[:title]) end
View 的寫法就與 HTML 頁面的 ERB 一樣
執行 Mailer Job
Resque config/initializers/resque.rb
可以載入 config/resque.yml
並藉此設定與 Redis 的連線
config/resque.yml
development: localhost:6379 test: localhost:6379 production: 10.x.x.x:6379
config/initializers/resque.rb
rails_root = ENV['RAILS_ROOT'] || File.dirname(__FILE__) + '/../..' rails_env = ENV['RAILS_ENV'] || 'development' resque_config = YAML.load_file(rails_root + '/config/resque.yml') Resque.redis = resque_config[rails_env]
執行 Resque 的工作步驟很簡單,因為在此專案裡我有使用 foreman 管理不同的 process, 所以在 Procfile 裏我多加了下列工作:
mailer: rake environment resque:work QUEUE=store_${RAILS_ENV}_mailers
執行時,只需要如下指令即可啟動郵差的執行緒
foreman start mailer
Active Job 背景執行的工作
系統長大的時候常常會遇到一些效能的瓶頸,比如說,我們要把資料庫裡的資料匯出成 Excel 讓人下載,資料庫那邊的效能瓶頸以外,就是處理效能的瓶頸了。Active Job 讓我可以把要做的事情丟到 Queue 裡讓另一個 process 來處理。 Rails Application 就不需要在 Request Timeout 前短短回應時間內把它做完。