Computer >> คอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

การทดสอบประสิทธิภาพ ความเค้น และโหลดใน Rails

การทดสอบเป็นส่วนสำคัญของแอปพลิเคชัน Rails ที่ทำงานได้ดีที่สุด โดยที่การบำรุงรักษาไม่ใช่ฝันร้าย และมีการเพิ่มคุณสมบัติใหม่อย่างสม่ำเสมอ หรือคุณลักษณะที่มีอยู่ได้รับการปรับปรุง น่าเสียดายที่สำหรับหลาย ๆ แอปพลิเคชัน สภาพแวดล้อมการผลิตเป็นที่ที่พวกเขาอยู่ภายใต้ภาระงานหนักหรือการรับส่งข้อมูลที่มีนัยสำคัญเป็นครั้งแรก เป็นเรื่องที่เข้าใจได้เนื่องจากการทดสอบดังกล่าวมีค่าใช้จ่ายสูง

โชคดีที่ Rails มีการสนับสนุนที่ดีไม่เพียงแต่สำหรับการทดสอบยูนิต แบบ end-to-end และการรวมระบบเท่านั้น แต่ยังรวมถึงการทดสอบที่เกี่ยวข้องกับประสิทธิภาพและการโหลดด้วย ฉันจะครอบคลุมเนื้อหาทั้งหมดในบทความและแสดงตัวอย่างเชิงปฏิบัติที่จะช่วยให้คุณเข้าใจวิธีใช้เครื่องมือที่ทดสอบระดับประสิทธิภาพของแอปพลิเคชันอย่างมีประสิทธิภาพ

บทความแบ่งออกเป็นสองส่วน:

  • ทฤษฎี — ฉันจะแสดงให้คุณเห็นว่าเหตุใดการทดสอบจึงมีความจำเป็น ประเภทของการทดสอบที่เราสามารถทำได้ และตัวชี้วัดที่สำคัญเมื่อทำการทดสอบในแอปพลิเคชัน

  • ใช้ได้จริง — เราจะทำให้มือของเราสกปรกและเขียนการทดสอบสำหรับแอปพลิเคชันจริงเพื่อให้ได้ผลลัพธ์

หลังจากอ่านทั้งสองส่วนแล้ว คุณจะมีความเข้าใจอย่างลึกซึ้งยิ่งขึ้นเกี่ยวกับการทดสอบประเภทต่างๆ และวิธีดำเนินการในแอปพลิเคชัน Rails ของคุณ ฟังดูน่าสนใจ? มาเริ่มกันด้วยทฤษฎีเล็กน้อยเกี่ยวกับการทดสอบกัน

การทดสอบในทางทฤษฎี

การทดสอบควรเป็นส่วนหนึ่งของการพัฒนาแอปพลิเคชันทุกประเภทเสมอ หากคุณยังไม่มั่นใจหรือยังไม่ได้เขียนการทดสอบ ต่อไปนี้คือข้อโต้แย้งสำหรับการทดสอบที่จะช่วยคุณ:

  • แนะนำการเปลี่ยนแปลงโดยไม่ต้องกังวลว่าจะมีอะไรแตกหัก — นี่คือเหตุผลหลักว่าทำไมการทดสอบจึงมีความจำเป็น ลองนึกภาพการทำงานในแอปขนาดใหญ่ที่คุณต้องคลิกดูทั้งแอปเพื่อให้แน่ใจว่าไม่มีอะไรเสียหายทุกครั้งที่คุณแนะนำการเปลี่ยนแปลง แม้แต่การเปลี่ยนแปลงเล็กน้อย ด้วยการทดสอบ คุณเพียงแค่รันคำสั่งเดียวและกระบวนการตรวจสอบจะเป็นไปโดยอัตโนมัติและรวดเร็ว
  • กระบวนการปรับโครงสร้างใหม่อย่างง่าย — ฉันได้กล่าวไว้ข้างต้นว่าการทดสอบมีความสำคัญเมื่อเพิ่มคุณสมบัติใหม่หรือทำการเปลี่ยนแปลง เมื่อทำการทดสอบแล้ว คุณจะรู้สึกสบายใจกับการปรับปรุงโค้ดที่มีอยู่มากขึ้น
  • การทดสอบคือรูปแบบของเอกสาร — การทดสอบที่เขียนอย่างดีสามารถเป็นรูปแบบของเอกสารสำหรับชุดคุณลักษณะต่างๆ ในแอปพลิเคชัน พวกเขาไม่เพียงแต่อธิบายว่าคุณลักษณะนี้คืออะไร แต่ยังรวมถึงวิธีการทำงานด้วย
  • โอกาสในการคิดทบทวนการใช้งานใหม่ — เมื่อคุณเขียนการทดสอบ คุณมีโอกาสคิดอีกครั้งว่าวิธีที่คุณต้องการใช้โค้ดนั้นถูกต้องและสมเหตุสมผลหรือไม่ นอกจากนี้ คุณเพียงแค่ตรวจสอบว่าโค้ดของคุณทำงานตามที่คุณคาดหวังหรือไม่

ฉันหวังว่าข้อโต้แย้งข้างต้นจะโน้มน้าวให้คุณใช้การทดสอบในระหว่างการพัฒนาแอปใดๆ แม้ว่าการรู้ว่าเหตุใดจึงต้องทดสอบโค้ดจึงเป็นสิ่งสำคัญ แต่การเรียนรู้เกี่ยวกับการทดสอบประเภทต่างๆ ก็มีความสำคัญเช่นกัน

การทดสอบประเภทต่างๆ

มีการทดสอบหลักสามประเภทที่คุณสามารถเขียนเพื่อให้แน่ใจว่าประสิทธิภาพของแอปพลิเคชัน Rails ของคุณถูกต้องและโครงสร้างพื้นฐานทำงานได้ดีภายใต้ภาระงานหนัก ประเภทดังต่อไปนี้:

  • โหลดการทดสอบ — การทดสอบประเภทนี้จะตอบคำถามต่อไปนี้:จำนวนผู้ใช้ที่ระบบสามารถจัดการได้พร้อมกันในช่วงเวลาที่กำหนด ลองนึกภาพว่าคุณเปิดตัวผลิตภัณฑ์ที่มีคะแนนสูงสุดในเว็บไซต์ของคุณ และผู้ใช้หลายพันคนต้องการสั่งซื้อพร้อมกัน หากไม่มีการทดสอบการโหลดที่เหมาะสม คุณจะเสี่ยงต่อความผิดพลาดในช่วงเวลาที่สำคัญที่สุด
  • การทดสอบความเครียด — ด้วยการทดสอบประเภทนี้ คุณไม่ได้มุ่งเน้นที่การยืนยันจำนวนผู้ใช้ที่ระบบสามารถจัดการได้พร้อมกัน แต่อยู่ที่ว่าระบบจะทำงานอย่างไรเมื่อถึงขีดจำกัดของผู้ใช้
  • การทดสอบประสิทธิภาพ — ฉันจะบอกว่าการทดสอบประเภทนี้เป็นผู้ปกครองของการทดสอบความเครียดและโหลด วัตถุประสงค์หลักของการทดสอบดังกล่าวคือเพื่อให้ได้ชุดเมตริกที่เฉพาะเจาะจงซึ่งเราสามารถดำเนินการเพื่อปรับปรุงโค้ดของแอปพลิเคชันได้ ฉันจะพูดถึงเมตริกเหล่านั้นในอีกสักครู่

ดังที่กล่าวไปแล้ว ตอนนี้เราพร้อมที่จะย้ายไปยังขั้นตอนสุดท้ายของส่วนทฤษฎี:เรียนรู้ว่าตัวชี้วัดใดมีความสำคัญเมื่อทำการทดสอบประสิทธิภาพในแอปพลิเคชัน Rails หากไม่มีความรู้นั้น เราจะไม่ตีความผลการทดสอบอย่างถูกต้องและตัดสินใจว่าควรเปลี่ยนรหัสหรือไม่

ตัวชี้วัดที่สำคัญ

ประเภทของเมตริกที่คุณได้รับอาจแตกต่างกันไปตามเครื่องมือที่คุณใช้ในการทดสอบ แต่โดยทั่วไป เราสามารถจัดกลุ่มเมตริกเหล่านี้ให้เป็นชุดเมตริกที่ค่อนข้างธรรมดาได้:

  • เวลาตอบสนอง — เวลาระหว่างการร้องขอและการตอบกลับที่แสดงในเบราว์เซอร์ ตัวชี้วัดนี้แสดงให้เราเห็นว่าผู้ใช้ต้องรอนานแค่ไหนก่อนที่จะได้รับข้อมูลที่เขาขอ บางครั้งเรียกว่าเวลาดำเนินการ
  • การใช้หน่วยความจำ — จำนวนหน่วยความจำที่ใช้สำหรับคำขอที่กำหนด นี่เป็นข้อมูลสำคัญส่วนหนึ่งเนื่องจากจะชี้ให้คุณทราบถึงจุดที่คุณสามารถปรับปรุงโค้ดได้ เพื่อให้ระบบตอบสนองได้เร็วขึ้นและใช้ทรัพยากรน้อยลง
  • การจัดสรรวัตถุ — การจัดสรรหน่วยความจำสูงทำให้เกิดการใช้หน่วยความจำสูงและเวลาตอบสนองนาน เมตริกนี้จะนำคุณไปยังตำแหน่งที่แน่นอนในโค้ดซึ่งมีการจัดสรรออบเจ็กต์จำนวนมาก คุณจึงตรวจสอบได้ทันที

คุณสามารถมีตัววัดได้มากขึ้นเมื่อทำการทดสอบ แต่สามสิ่งนี้สำคัญที่สุดและจะใช้ได้กับแอปพลิเคชันใดๆ ที่คุณทดสอบ ตอนนี้เราเริ่มสกปรกและเขียนบททดสอบจริงได้แล้ว

แบบฝึกหัด

เราไม่สามารถเขียนการทดสอบโดยไม่ต้องทดสอบอะไรได้เลย นั่นเป็นเหตุผลที่ขั้นตอนแรกในส่วนการฝึกหัดคือการเขียนแอปพลิเคชัน Rails แบบง่ายๆ ที่เราสามารถเขียนแบบทดสอบได้

ตัวอย่างการใช้งานรางรถไฟ

ฉันจะใช้ Ruby 3.0.1 และ Rails 6.1.3.1 แต่คุณสามารถใช้เวอร์ชันใดก็ได้ที่คุณพอใจ หากคุณติดตั้ง Ruby และ Rails ไว้ ขั้นตอนต่อไปคือการสร้างโครงร่างของแอปพลิเคชัน:

rails new simpleapp -d=postgresql

สำหรับจุดประสงค์ของบทความ ฉันจะสร้างแอปที่แสดงรายชื่อผู้ใช้พร้อมกับชื่อสัตว์เลี้ยงของพวกเขา โครงสร้างดังกล่าวจะช่วยให้เราสร้างข้อความค้นหา N+1 ได้อย่างง่ายดาย ซึ่งจะให้ความสนุกสนานมากขึ้นเมื่อทำการทดสอบประสิทธิภาพ และตรวจสอบผลกระทบต่อความเร็วและเมตริกอื่นๆ ที่การเปลี่ยนแปลงจะเกิดขึ้น

ก่อนที่เราจะสร้างโมเดล เรามาสร้างฐานข้อมูลกันก่อน:

cd simpleapp/
bin/rails db:create

ตอนนี้ เราสามารถสร้างแบบจำลองได้:

rails g model User name:string
rails g model Animal name:string user:references
bin/rails db:migrate

การอัปเดตเพียงเล็กน้อยสำหรับ User โมเดลที่สะท้อนความสัมพันธ์กับ Animal รุ่น:

class User < ApplicationRecord
  has_many :animals
end

ตอนนี้เราเพิ่มเมล็ดใน db/seeds.rb . ได้แล้ว ไฟล์:

people = {
  'Tim' => ['Pinky', 'Rick'],
  'Martha' => ['Rudolph'],
  'Mark' => ['Niki', 'Miki', 'Bella'],
  'Tina' => ['Tom', 'Luna']
}
 
people.each_pair do |name, pets|
  user = User.create(name: name)
  pets.each do |pet_name|
    user.animals.create(name: pet_name)
  end
end

และโหลดข้อมูลลงฐานข้อมูล:

bin/rails db:seed

ฉันจะสร้างตัวควบคุมหนึ่งตัวพร้อมการมอบหมายของผู้ใช้ จากนั้นในมุมมอง ฉันจะแสดงรายการผู้ใช้ทั้งหมดที่มีชื่อสัตว์เลี้ยงของพวกเขา ฉันตั้งใจใช้โค้ดที่ทำให้เกิดปัญหาด้านประสิทธิภาพเพื่อให้คุณวัดผลการปรับปรุงได้ในภายหลัง

touch app/controllers/home_controller.rb
mkdir app/views/home
touch app/views/home/index.html.erb

ตัวควบคุมนั้นเรียบง่าย:

class HomeController < ApplicationController
  def index
    @users = User.all
  end
end

และมุมมองด้วย:

<h1>List</h1>
 
<ul>
  <% @users.each do |user| %>
    <li><%= user.name %> (<%= user.animals.count %>)
      <ul>
        <% user.animals.each do |animal| %>
          <li><%= animal.name %></li>
        <% end %>
      </ul>
    </li>
  <% end %>
</ul>

ขั้นตอนสุดท้ายคือการอัพเดต config/routes.rb เพื่อให้ Rails รู้ว่าเราต้องการเห็นอะไรเมื่อไปที่ URL หลัก:

Rails.application.routes.draw do
  root to: 'home#index'
end

โหลดการทดสอบด้วย JMeter

JMeter เป็นซอฟต์แวร์โอเพ่นซอร์สที่สร้างโดยมูลนิธิซอฟต์แวร์ Apache ซึ่งออกแบบมาเพื่อโหลดลักษณะการทำงานของการทดสอบ เนื่องจากเป็นโปรแกรมที่สร้างด้วย Java คุณจึงติดตั้งบนระบบปฏิบัติการใดก็ได้ คุณสามารถดาวน์โหลดไฟล์ได้ที่นี่:https://jmeter.apache.org/download_jmeter.cgi

หากคุณกำลังทำงานบนระบบ macOS คุณสามารถติดตั้ง JMeter ด้วย Homebrew ได้อย่างง่ายดาย:

brew install jmeter

หลังการติดตั้ง คุณสามารถรันโปรแกรมด้วยคำสั่งต่อไปนี้:

jmeter

การกำหนดค่าการทดสอบ

ขั้นตอนการกำหนดค่าประกอบด้วยขั้นตอนต่อไปนี้:

  • การเพิ่มกลุ่มเธรด — ระบุจำนวนผู้ใช้และระยะเวลาที่แต่ละคนจะเข้าชมเว็บไซต์ของคุณ
  • การกำหนดค่าคำขอ HTTP — ระบุปลายทางที่ JMeter ควรโดน
  • การตั้งค่าเมตริกที่เราสนใจ

มาดูการกำหนดค่าการทดสอบอย่างง่ายทีละขั้นตอนเพื่อจำลองคำขอของผู้ใช้รายเดียวไปยังหน้าหลักของแอปอย่างง่ายที่เราสร้างขึ้นก่อนหน้านี้

เพิ่มกลุ่มกระทู้

เลือก Add -> Threads (Users) -> Thread Group จากเมนูที่ขยายออกหลังจากที่คุณคลิกขวาที่ “Test Plan”:

ระบุจำนวนผู้ใช้และคุณสมบัติเพิ่มเติม:

กำหนดค่าคำขอ HTTP

คลิกขวาที่เธรดที่เราสร้างในขั้นตอนก่อนหน้าแล้วเลือก Add -> Sampler -> HTTP Request:

กำหนดค่าโปรโตคอล ชื่อเซิร์ฟเวอร์ พอร์ต และเส้นทางของคำขอ:

ระบุมุมมองผลลัพธ์

คลิกขวาที่คำขอ HTTP และเลือก เพิ่ม -> Listener -> ดูแผนผังผลลัพธ์:

ดำเนินการทดสอบ

การทดสอบได้รับการกำหนดค่าแล้ว และเราสามารถเรียกใช้งานได้ ในการดำเนินการนี้ เพียงคลิกที่ปุ่มเล่นสีเขียว:

อย่างที่คุณเห็น แอปพลิเคชันผ่านการทดสอบ แต่เป็นเพียงคำขอเดียว ผลลัพธ์จึงชัดเจน คุณสามารถทดลองกับจำนวนผู้ใช้และตัวเลือกการกำหนดค่าอื่นๆ เพื่อดูว่าแอปพลิเคชันทำงานอย่างไร จากการทดสอบของฉัน แอปธรรมดาเริ่มหยุดทำงานเมื่อมีผู้ใช้ประมาณ 200 คนเริ่มเข้าถึงพร้อมกัน

ขั้นตอนต่อไป

หลังจากทำการทดสอบโหลด คุณจะทราบจุดบกพร่องของแอปพลิเคชันของคุณ เมื่อเข้าใจขีดจำกัดของผู้ใช้แล้ว ตอนนี้คุณสามารถทำการทดสอบความเครียดเพื่อดูว่าแอปพลิเคชันทำงานอย่างไร

การทดสอบประสิทธิภาพกับ Ruby-prof

คุณลักษณะการทดสอบประสิทธิภาพมีอยู่ใน Rails จนถึงเวอร์ชัน 3 จากนั้นจึงแยกไปยัง gem ที่แยกต่างหาก https://github.com/rails/rails-perftest เนื่องจากฉันมีปัญหาในการใช้งานกับ Rails เวอร์ชันล่าสุด ฉันจึงตัดสินใจไม่รวมไว้ในบทความนี้ แต่ฉันจะใช้ไลบรารี่ ruby-prof ที่ทำงานได้ดีแทน

ตามปกติ ขั้นตอนแรกคือการเพิ่มอัญมณีลงในแอปพลิเคชันของเรา:

bundle add ruby-prof

ขั้นตอนที่สองและขั้นตอนสุดท้ายของกระบวนการกำหนดค่าคือการอัพเดต config/application.rb และใช้มิดเดิลแวร์สำหรับอัญมณี เพื่อให้ห้องสมุดสามารถตรวจสอบคำขอของเราโดยอัตโนมัติและสร้างรายงานโดยอิงจากคำขอเหล่านั้น:

module Simpleapp
  class Application < Rails::Application
    config.middleware.use Rack::RubyProf, :path => './tmp/profile'
  end
end

ขณะนี้คุณสามารถเข้าถึงแอปได้แล้ว และทุกครั้งที่คุณดำเนินการตามคำขอ เจมจะสร้างรายงานใหม่ ดูเหมือนว่านี้:

คุณสามารถค้นหาได้ภายใต้เส้นทางที่กำหนดค่าไว้ ซึ่งก็คือ tmp/profile ในกรณีของเรา รายงานที่สองก็ถูกสร้างขึ้นเช่นกัน และจะแสดง call stack ซึ่งเป็นตัวชี้วัดที่ค่อนข้างมีประโยชน์เมื่อทำการดีบักปัญหาด้านประสิทธิภาพในแอปพลิเคชัน Rails

สิ่งสำคัญคือต้องจำไว้ว่าการตั้งค่า cache_classes และ cache_template_loading ตั้งค่าเป็น true จะทำให้แอปพลิเคชันช้าลงและครอบงำเมตริกแอปพลิเคชันเนื่องจาก Rails จะพยายามโหลดไฟล์ที่จำเป็น

สรุป

การทดสอบเป็นส่วนสำคัญของทุกกระบวนการพัฒนา การตรวจสอบว่าโค้ดทำงานตามที่เราต้องการหรือไม่นั้นสำคัญพอๆ กับการตรวจสอบว่าโซลูชันของเรามีประสิทธิภาพที่ดีหรือไม่ การข้ามการทดสอบนำไปสู่ปัญหาร้ายแรงที่ส่งผลต่อประสิทธิภาพของแอปและความไว้วางใจของผู้ใช้ หวังว่าการทดสอบจะไม่ยากขนาดนั้น

ในบทความนี้ เราได้กล่าวถึงประเด็นสำคัญของการทดสอบดังต่อไปนี้:

  • เหตุผลที่คุณควรทดสอบโค้ดของคุณ
  • การทดสอบประสิทธิภาพประเภทต่างๆ
  • วิธีทดสอบประสิทธิภาพของแอป Rails

ฉันหวังว่าตอนนี้คุณจะมั่นใจมากขึ้นว่าทำไมการเขียนแบบทดสอบจึงสำคัญ เพราะคุณรู้ว่าทำไมและอย่างไร

หากคุณสนใจที่จะตรวจสอบประสิทธิภาพของแอปไม่เพียงแต่ในเครื่องเท่านั้น แต่ยังรวมถึงในสภาพแวดล้อมการใช้งานจริงหรือการแสดงละครด้วย คุณควรตรวจสอบ AppSignal ด้วย

ป.ล. หากคุณต้องการอ่านโพสต์ Ruby Magic ทันทีที่ออกจากสื่อ สมัครรับจดหมายข่าว Ruby Magic ของเราและไม่พลาดแม้แต่โพสต์เดียว!