Khám phá sức mạnh của Hotwire trong Ruby on Rails: Turbo Drive, Turbo Frames, Turbo Streams và Turbo Morph. Hướng dẫn toàn diện giúp bạn xây dựng giao diện động, phản hồi nhanh mà không cần JavaScript phức tạp, với các ví dụ thực tế và lời khuyên từ chuyên gia.
Hướng dẫn chi tiết cách triển khai xác thực người dùng trong Rails 8 API kết hợp với React, bao gồm cấu hình CORS, CSRF, và quản lý session an toàn.
Bạn đã chán Devise? Khám phá cách triển khai xác thực người dùng trong dự án React + Rails API với Rails 8, từ A đến Z, siêu dễ hiểu và vui vẻ. Tìm hiểu về Authentication Concern, DB-backed Sessions, Current và cách kết nối frontend-backend!
Khám phá cách Cursor AI với tính năng 'Rules for AI' giúp chuẩn hóa code Ruby on Rails, đặc biệt trong việc di chuyển từ RSpec sang MiniTest một cách nhanh chóng và hiệu quả. Biến AI thành 'trợ lý' đắc lực của bạn!
Chào bạn, nhớ bài viết trước mình từng "tám" về độ phức tạp trong phần mềm không? Mình có một châm ngôn luôn tâm niệm thế này: "Mã mình 'đụng' vào phải dễ hiểu và dễ bảo trì hơn lúc ban đầu—trừ khi mình viết mới hoàn toàn." Nghe có vẻ "nghiêm túc" nhưng thực ra đây là kim chỉ nam giúp mình luôn giữ code sạch đẹp đó! <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/clean_desk_code.png' alt='Bàn làm việc gọn gàng và code sạch sẽ'> Khi làm việc với OOP (Lập trình hướng đối tượng), mình luôn bắt đầu từ những đơn vị nhỏ nhất. Với một "dân Ruby" như mình, thì đó chính là các **hàm** (function) đó bạn! Đối với mình, hàm chính là những viên gạch xây dựng cơ bản nhất của một lớp (class). Kể cả khi bạn có một lớp dài "một ngàn lẻ một dòng" đi chăng nữa, nếu các hàm bên trong nó rõ ràng và cấu trúc tốt, thì mọi thứ vẫn "chơi" được! Ít nhất là sau này muốn "đại tu" (refactor) cũng dễ thở hơn rất nhiều. Hôm nay, mình muốn bật mí 3 bí kíp "ruột" mà mình luôn áp dụng để viết ra những hàm vừa đẹp, vừa dễ bảo trì. Chuẩn bị sổ bút nha! <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/coding_principles.png' alt='Các nguyên tắc viết code'> ### 1️⃣ Hàm ơi, "ngắn thôi đừng dài"! (Số dòng code) Ở Ruby, việc giữ cho các hàm ngắn gọn là một "nét văn hóa" rồi đó bạn. Hồi mình còn làm Trưởng nhóm kỹ thuật, mình hay đặt ra một "giới hạn mềm" là 10-12 dòng code cho một hàm. Nhưng mà, tùy vào "level" của đội ngũ mà con số này có thể hơi "khó nhằn" và làm chậm tiến độ. Sau nhiều phen "thử và sai", mình tìm ra một giải pháp "hòa bình" hơn: **20 dòng code là một mức tối đa hợp lý.** Con số này giúp hàm của bạn luôn đơn giản, dễ đọc và dễ bảo trì, mà lại không gây ra "áp lực" không cần thiết cho cả team. Cứ như một câu chuyện mini vậy, mỗi hàm chỉ kể một phần nhỏ thôi! <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/short_code.png' alt='Mã nguồn ngắn gọn'> #### 📌 Ví dụ: "Trước và Sau" khi cắt tỉa Cùng xem một ví dụ "kinh điển" nhé! **Trước đây:** Một hàm dài "lê thê" và khó hiểu, cứ trộn lẫn đủ thứ công việc với nhau. Bạn xem này, nó giống như một "siêu nhân" gánh vác mọi nhiệm vụ cùng lúc vậy: ```ruby def process_payment(user, order_id) order = Order.find(order_id) raise "Order not found" unless order payment_gateway = PaymentGateway.new(user.payment_token) payment_result = payment_gateway.charge(order.amount_cents) if payment_result.success? order.update!(status: :paid, paid_at: Time.current) AnalyticsLogger.log_payment_success(user.id, order.id) NotificationService.send_payment_confirmation_email(user, order) if user.referral_code.present? reward_service = ReferralRewardService.new(user.referral_code) reward_service.process_reward_for(user) end SendThankYouGiftJob.perform_later(user.id) if order.amount_cents > 100_000 else order.update!(status: :payment_failed) ErrorTracker.notify("Payment failed", user_id: user.id, order_id: order.id) end end ``` **Và đây là Sau khi "biến hình":** Hàm đã được "tách lớp" rõ ràng, mỗi hàm chỉ tập trung làm một việc duy nhất. Giờ thì "siêu nhân" đã trở thành một "biệt đội chuyên gia", mỗi người một nhiệm vụ, phối hợp cực ăn ý! ```ruby def process_payment(user, order_id) order = Order.find(order_id) raise "Order not found" unless order if charge_order(user, order) handle_successful_payment(user, order) else handle_failed_payment(user, order) end end def charge_order(user, order) result = PaymentGateway.new(user.payment_token).charge(order.amount_cents) return false unless result.success? order.update!(status: :paid, paid_at: Time.current) true end def handle_successful_payment(user, order) log_payment_success(user, order) notify_user_of_payment(user, order) reward_referral_if_applicable(user) send_thank_you_gift_if_high_value(user, order) end def handle_failed_payment(user, order) order.update!(status: :payment_failed) ErrorTracker.notify("Payment failed", user_id: user.id, order_id: order.id) end def log_payment_success(user, order) AnalyticsLogger.log_payment_success(user_id: user.id, order_id: order.id) end def notify_user_of_payment(user, order) NotificationService.send_payment_confirmation_email(user, order) end def reward_referral_if_applicable(user) return unless user.referral_code.present? reward_service = ReferralRewardService.new(user.referral_code) reward_service.process_reward_for(user) end def send_thank_you_gift_if_high_value(user, order) return unless order.amount_cents > 100_000 SendThankYouGiftJob.perform_later(user.id) end ``` <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/refactor_flow.png' alt='Sơ đồ refactoring của hàm process_payment'> ### 2️⃣ Một nhiệm vụ, một lý do để thay đổi (Single Responsibility Principle) Nguyên tắc này nói rằng: **Một hàm chỉ nên có MỘT lý do duy nhất để thay đổi.** Nghe có vẻ "trừu tượng" nhưng thực ra nó chính là anh em song sinh với Nguyên tắc Đơn nhiệm (Single Responsibility Principle - SRP) đó bạn. Để kiểm tra nhanh gọn lẹ, bạn cứ tự hỏi mình: "Cái hàm này làm gì?" Nếu câu trả lời của bạn có từ "và" (ví dụ: "Nó xử lý thanh toán **và** gửi email xác nhận"), thì xin chia buồn, hàm của bạn đang ôm đồm quá nhiều việc rồi đấy! Nó giống như một đầu bếp vừa nấu ăn, vừa lau dọn, vừa phục vụ bàn vậy – mỗi khi có yêu cầu mới, anh ta sẽ rất bận rộn và dễ sai sót. Trong ví dụ `process_payment` ban đầu, hàm này có "ti tỉ" lý do để thay đổi: logic thanh toán, ghi log, gửi thông báo, xử lý thưởng giới thiệu, hay cả các tác vụ chạy nền (background jobs). Giờ đây, mỗi hàm con chỉ tập trung vào duy nhất một nhiệm vụ, việc thay đổi trở nên dễ dàng và ít rủi ro hơn rất nhiều. Sướng! <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/single_responsibility.png' alt='Mỗi người một việc, mỗi hàm một nhiệm vụ'> ### 3️⃣ Cùng "tầm nhìn", cùng "chiều cao" (Consistent Level of Abstraction) Nguyên tắc này nghe có vẻ "triết lý" nhưng lại cực kỳ hiệu quả đó nha! Nếu một hàm cứ "nhảy cóc" giữa các cấp độ trừu tượng khác nhau, nó sẽ làm bạn "mệt não" khi cố gắng hiểu nó hoạt động thế nào. Tưởng tượng bạn đang đọc một câu chuyện, mà tác giả cứ lúc thì mô tả tổng quan cả thành phố, lúc lại đi sâu vào chi tiết cái lông mày của một nhân vật. Khó theo dõi đúng không? Trong phiên bản hàm `process_payment` "Before", hàm của chúng ta cứ "nhảy múa" đủ mọi cấp độ: * Truy cập database (cấp độ thấp - chi tiết) * Gọi API thanh toán bên ngoài (hạ tầng - chi tiết) * Áp dụng logic nghiệp vụ (cấp độ trung bình - tổng quan hơn) * Gửi thông báo (tương tác đầu vào/đầu ra - chi tiết) * Kích hoạt các tác vụ nền (hạ tầng - chi tiết) Đó là cả một "mớ bòng bong" các ngữ cảnh, khiến bạn phải chuyển đổi tư duy liên tục! Nhưng ở phiên bản "After", hàm chính `process_payment` chỉ đứng ở cấp độ "điều phối" (orchestration). Nó giống như một "chỉ huy" giao việc cho từng "chuyên gia" (các hàm con). Mỗi hàm phụ lại giữ nguyên một "độ cao" nhất định, khiến toàn bộ luồng xử lý trở nên dễ theo dõi và phát triển hơn rất nhiều. Cứ như bạn đang xem một bản đồ có nhiều lớp vậy, mỗi lớp hiển thị một loại thông tin ở một cấp độ chi tiết nhất định. <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/abstraction_levels.png' alt='Các cấp độ trừu tượng trong lập trình'> Đó là 3 bí kíp "xịn xò" mà mình muốn chia sẻ với bạn. Bạn có "chiêu" nào hay ho để giữ cho hàm của mình luôn "sạch sẽ" và dễ bảo trì không? Comment xuống dưới để chúng ta cùng "tám" nhé! 👇
So sánh chi tiết Event Sourcing và Blockchain, phân tích ưu nhược điểm, khi nào nên dùng từng loại hoặc kết hợp để tối ưu hệ thống, tránh những sai lầm thường gặp.
Tìm hiểu 3 nguyên tắc vàng giúp bạn viết hàm (function) gọn gàng, dễ hiểu và dễ bảo trì trong lập trình, từ việc giữ code ngắn gọn đến áp dụng Nguyên tắc Trách nhiệm Duy nhất và cấp độ trừu tượng nhất quán.
Khám phá Kamal-Proxy, giải pháp proxy tùy chỉnh giúp Kamal triển khai ứng dụng Docker dễ dàng, mượt mà, không gián đoạn, và bảo mật tự động.
Tìm hiểu cách kết hợp sức mạnh của Rust và ScyllaDB với Charybdis ORM để xây dựng backend hiệu suất cao, phân tán, đồng thời so sánh với ưu nhược điểm của Ruby on Rails và cơ sở dữ liệu SQL truyền thống. Khám phá sự khác biệt về lưu trữ (LSM vs B+ Tree), khả năng mở rộng và mô hình dữ liệu.
Tìm hiểu cách Hotwire, Turbo Drive, Frames, Streams và Morph giúp bạn xây dựng ứng dụng Ruby on Rails động, phản hồi nhanh mà không cần JavaScript phức tạp. Khám phá khi nào nên sử dụng từng công cụ với các ví dụ thực tế.
Khám phá RubyLLM 1.0 - thư viện AI 'xịn xò' giúp lập trình viên Ruby làm việc với AI một cách tự nhiên, thanh lịch và cực kỳ hiệu quả, với các tính năng như streaming, gọi hàm, và tích hợp Rails. Đã được kiểm chứng trong thực tế.
Tìm hiểu khi nào nên sử dụng Turbo Drive, Turbo Frames, Turbo Streams và Turbo Morph trong Hotwire để xây dựng ứng dụng Rails hiện đại, mượt mà mà không cần quá nhiều JavaScript.
Học cách xây dựng hệ thống tự động tạo tài liệu (Word, PowerPoint, PDF, Markdown) bằng AI và Ruby on Rails. Nâng cao hiệu quả quy trình làm việc và giảm lỗi thủ công.
RubyLLM 1.3.0 ra mắt với nhiều tính năng đột phá: đính kèm tệp siêu đơn giản, ngữ cảnh cấu hình độc lập cho multi-tenancy, hỗ trợ model cục bộ với Ollama, tích hợp hàng trăm model qua OpenRouter, theo dõi model tự động với Parsera, và tích hợp Rails liền mạch. Khám phá ngay để nâng tầm phát triển AI trong Ruby!
Giải mã lỗi 'target failed to become healthy' khi deploy ứng dụng với Kamal. Tìm hiểu nguyên nhân từ SSL, port, host đến tài nguyên và cách khắc phục chi tiết để deploy mượt mà hơn.
Khám phá cách thức hoạt động của cơ chế cache HTTP, từ các loại cache đến cách kiểm soát, làm mới và xác minh. Bài viết giúp bạn hiểu sâu và tối ưu hiệu suất website.
Tìm hiểu cách sử dụng EXPLAIN ANALYZE của PostgreSQL để tối ưu hóa hiệu suất truy vấn trong ứng dụng Rails. Khám phá cách đọc hiểu kết quả và phát hiện các vấn đề như thiếu Index hoặc N+1.
Chào bạn! Bạn có bao giờ cảm thấy 'đau đầu' mỗi khi cần rút trích dữ liệu từ database để tạo báo cáo động chưa? Phải ngồi gõ từng dòng SQL dài ngoằng, phức tạp, rồi lại loay hoay tối ưu cho môi trường thực tế – nghe thôi đã thấy nản rồi đúng không? Đừng lo, hôm nay tôi sẽ bật mí một giải pháp 'thần kỳ' mà tôi đã phát triển ra: biến ngôn ngữ tự nhiên thành các báo cáo SQL xịn sò bằng cách tận dụng sức mạnh của các Mô hình Ngôn ngữ Lớn (LLM)! Tuy tôi đang dùng Ruby on Rails 💎 quen thuộc, nhưng bạn hoàn toàn có thể áp dụng ý tưởng này vào Python 🐍, Java ☕, hay JavaScript 📜 mà không gặp chút khó khăn nào đâu nhé! <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/user_llm_db_interaction.png' alt='Mô hình tương tác giữa người dùng, LLM và cơ sở dữ liệu'> Vậy, 'công thức' bí mật của chúng ta hoạt động như thế nào? Cứ hình dung thế này nhé: 1. Bạn hỏi (User Query): 'Cho tôi biết số lượng người dùng không hoạt động?' 2. Hệ thống 'nghĩ' (Table Identification): 'À, câu này chắc liên quan đến bảng users rồi!' 3. Hệ thống 'viết' (SQL Generation): 'Để tôi viết câu SQL phù hợp: SELECT COUNT(*) FROM users WHERE status = \'inactive\';' 4. Hệ thống 'thực thi' (SQL Execution): 'Chạy câu SQL này trên database nào!' 5. Hệ thống 'trả lời' (Result Delivery): 'Đây rồi, 5 người dùng không hoạt động nhé!' Chúng ta sẽ chia nhỏ 'hành trình' này thành hai bước chính siêu rõ ràng: Bước 1: Tìm đúng 'tủ' (Table Identification) – Xác định bảng dữ liệu phù hợp với câu hỏi của bạn. Bước 2: 'Viết thư' và 'gửi thư' (SQL Generation and Execution) – Tạo câu lệnh SQL và chạy nó để lấy kết quả. <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/llm_workflow_simple.png' alt='Luồng hoạt động của hệ thống LLM tạo báo cáo'> Thử nghĩ xem, vấn đề 'nhức nhối' ở đây là gì? Đó là làm sao để 'tay mơ' nhất cũng có thể lấy được dữ liệu 'ngon lành' từ database mà không cần biết một chữ SQL nào cả! Một hệ thống 'trong mơ' sẽ phải: 'Đọc vị' ý định của bạn 🧐: Phải biết bạn muốn hỏi gì, và dữ liệu đó nằm ở cái bảng nào trong database. Tự động 'tạo' SQL 📝: Từ câu hỏi tiếng Việt/tiếng Anh của bạn, phải biến hóa ra một câu lệnh SQL chuẩn chỉnh, chạy được ngay. Trả kết quả 'dễ tiêu' 📈: Đưa ra dữ liệu dưới dạng bảng biểu, đồ thị hay bất cứ thứ gì dễ đọc, dễ hiểu nhất. Với cách làm này, ngay cả những người không chuyên về kỹ thuật cũng có thể 'lướt phím' lấy báo cáo vèo vèo, mà vẫn khai thác triệt để 'trí tuệ siêu phàm' của LLM để tạo ra các báo cáo linh hoạt và tùy biến theo ý muốn. <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/user_sql_problem_solution.png' alt='Từ vấn đề SQL phức tạp đến giải pháp dễ dàng'> Và đây là 'siêu phẩm' tôi đã tạo ra! Một hệ thống 'lắp ráp' siêu linh hoạt, gồm ba 'mảnh ghép' chính: <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/modular_system_lego.png' alt='Hệ thống mô-đun với các thành phần như khối Lego'> 1. LLM::CHAT - 'ANH PHIÊN DỊCH' CỦA OPENAI API: Cứ hình dung Llm::Chat như một 'phiên dịch viên' chuyên nghiệp, giúp hệ thống của chúng ta trò chuyện trôi chảy với các 'bộ não' siêu việt của OpenAI. Mỗi khi chúng ta muốn hỏi LLM một điều gì đó, thay vì phải loay hoay với đủ các giao thức phức tạp, anh Llm::Chat này sẽ lo từ A đến Z, từ việc đóng gói câu hỏi, gửi đi, cho đến việc 'giải mã' câu trả lời nhận về. Đơn giản là 'chuyền bóng' và 'nhận bóng' một cách mượt mà nhất! <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/api_wrapper_concept.png' alt='Khái niệm bộ bao API'> 'BÍ KÍP' PROMPTS - DẪN ĐƯỜNG CHO LLM: Để LLM không bị 'lạc lối' và luôn hiểu đúng ý chúng ta, chúng ta dùng những 'prompt' (lời nhắc) được định sẵn. Cứ như việc bạn dặn dò một đứa trẻ vậy: 'Con ơi, nhớ chỉ trả lời đúng trọng tâm thôi nhé, đừng lan man!' Prompt cho việc tìm bảng (TABLE_IDENTIFICATION): 'Này LLM, tôi cho bạn một câu hỏi. Bạn hãy nhìn vào các bảng users, departments, tickets và cho tôi biết bảng nào (hoặc những bảng nào) liên quan nhất nhé. Nhớ là chỉ trả lời tên bảng thôi, không thêm bớt chữ gì đâu nha!' Prompt cho việc tạo SQL (SQL_GENERATION): 'Giờ thì LLM nghe đây! Bạn đã biết cấu trúc bảng rồi đó: %{table_structure}. Hãy tạo một câu lệnh MySQL dựa trên câu hỏi của người dùng. Nếu họ hỏi về nhiều bảng, bạn cũng phải xử lý được luôn nhé. Quan trọng nhất là: CHỈ TRẢ LỜI CÂU SQL, không giải thích, không markdown, không hoa văn gì cả!' Nhờ những lời nhắc rõ ràng này, LLM sẽ luôn đi đúng hướng và trả về kết quả mà chúng ta mong muốn! <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/llm_prompts_concept.png' alt='Prompt là kim chỉ nam cho LLM'> 2. 'THÁM TỬ' TABLEIDENTIFIER - TÌM ĐÚNG 'TỦ HỒ SƠ': Bước đầu tiên và cũng là cực kỳ quan trọng, là xác định xem câu hỏi của người dùng đang muốn 'khai thác' dữ liệu từ cái 'bảng' nào trong database. Đây chính là nhiệm vụ của anh bạn TableIdentifier! Anh ấy sẽ lấy câu hỏi của bạn, gửi cho LLM cùng với 'bí kíp' TABLE_IDENTIFICATION prompt, và chờ đợi câu trả lời. Ví dụ, nếu bạn hỏi 'Cho tôi biết danh sách người dùng đang hoạt động', anh ấy sẽ 'thì thầm' với LLM và nhận được câu trả lời 'users'. Ngay lập tức, chúng ta biết phải đến đúng 'tủ hồ sơ' users để tìm kiếm! <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/table_identification_concept.png' alt='Xác định bảng dữ liệu phù hợp'> À tiện thể, để bạn dễ hình dung, đây là 'bộ sưu tập' các bảng dữ liệu mà hệ thống của chúng ta đang 'nắm trong lòng bàn tay' nhé: Bảng users 👥: Lưu thông tin về các 'cư dân' trong hệ thống của chúng ta. Gồm các cột: id: Mã số định danh duy nhất của mỗi người; name: Tên của 'cư dân'; email: Thư điện tử; status: Trạng thái (ví dụ: 'active' - hoạt động, 'inactive' - không hoạt động); department_id: ID phòng ban (liên kết với bảng departments); created_at, updated_at: Thời gian tạo và cập nhật. <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/users_table_icon.png' alt='Biểu tượng bảng người dùng'> Bảng departments 🏢: Chứa thông tin về các phòng ban. Gồm các cột: id: ID phòng ban; name: Tên phòng ban; manager_id: ID của quản lý (nếu có); created_at, updated_at: Thời gian tạo và cập nhật. <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/departments_table_icon.png' alt='Biểu tượng bảng phòng ban'> Bảng tickets 🎫: Ghi lại các yêu cầu hoặc sự cố. Gồm các cột: id: ID của ticket; user_id: ID người dùng tạo ticket (liên kết với bảng users); subject: Tiêu đề; status: Trạng thái của ticket (ví dụ: 'open', 'closed'); created_at, updated_at: Thời gian tạo và cập nhật. <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/tickets_table_icon.png' alt='Biểu tượng bảng tickets'> 3. 'THỢ THỦ CÔNG' REPORTGENERATOR - TẠO VÀ CHẠY SQL 'NHƯ BAY': Sau khi 'thám tử' TableIdentifier đã tìm ra đúng 'tủ hồ sơ' (bảng dữ liệu), thì 'thợ thủ công' ReportGenerator sẽ ra tay! Nhiệm vụ của anh này là: 1. Lấy cấu trúc bảng: Anh ấy sẽ hỏi database xem cấu trúc của cái bảng vừa tìm được trông như thế nào. Ví dụ, bảng users có cột id, name, status, v.v. 2. Nhờ LLM 'chế' SQL: Với câu hỏi của bạn và cấu trúc bảng vừa lấy được, anh ấy lại gửi cho LLM một 'đơn đặt hàng' kèm theo 'bí kíp' SQL_GENERATION prompt. LLM sẽ dựa vào đó để 'phù phép' ra một câu SQL hoàn chỉnh. 3. 'Chạy' SQL: Ngay khi có câu SQL, ReportGenerator sẽ không chần chừ gì mà 'thẳng tay' thực thi nó trên database. 4. Trả kết quả 'ngon lành': Cuối cùng, dữ liệu thu được sẽ được 'đóng gói' cẩn thận và trả về cho bạn dưới dạng một báo cáo dễ nhìn. Nghe có vẻ phức tạp, nhưng tất cả quá trình này diễn ra chỉ trong 'tích tắc' và hoàn toàn tự động! <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/sql_generation_execution.png' alt='Tạo và thực thi SQL tự động'> 'PHÉP THUẬT' TRONG THỰC TẾ 🛠️: Vậy là xong! Giờ đây, việc tạo báo cáo 'siêu khó nhằn' bỗng trở nên đơn giản như 'nhấn một nút' vậy đó: Bạn muốn biết 'có bao nhiêu người dùng không hoạt động?' ReportGenerator.new(query: \"count of inactive users\").call Hệ thống sẽ 'tự động' tạo ra câu SQL này: SELECT COUNT(*) FROM users WHERE status = \'inactive\'; Hay bạn cần 'liệt kê danh sách người dùng đang hoạt động?' ReportGenerator.new(query: \"list of active users\").call Tương tự, hệ thống sẽ 'chế biến' ra câu SQL tương ứng. Thậm chí, bạn hỏi những câu 'khó' hơn như 'số lượng người dùng mỗi phòng ban?' ReportGenerator.new(query: \"number of users per department\").call Hệ thống 'thông minh' của chúng ta vẫn 'nháy mắt' và tạo ra câu SQL phức tạp hơn một chút, có cả JOIN và GROUP BY luôn: SELECT d.name, COUNT(u.id) FROM users u JOIN departments d ON u.department_id = d.id GROUP BY d.name; Thật tuyệt vời phải không nào? Cứ như có một 'trợ lý' SQL siêu đẳng luôn túc trực vậy! <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/natural_language_to_sql.png' alt='Chuyển đổi ngôn ngữ tự nhiên sang SQL'> LỜI 'NHẮC NHỞ' NHỎ XÍU ⚠️: À mà bạn ơi, có một điều nho nhỏ cần lưu ý nhé! Mấy cái 'bí kíp' (prompt) mà LLM dùng để tạo ra SQL đôi khi cần phải được 'căn chỉnh' một chút xíu. Cứ như việc bạn nêm nếm gia vị cho món ăn vậy, đôi khi phải thử đi thử lại vài lần mới ra được 'mùi vị' ưng ý nhất. Bạn có thể cần 'tinh chỉnh' chúng cho phù hợp với cấu trúc database và nhu cầu báo cáo 'độc nhất vô nhị' của mình đó! Nhưng đừng lo, đó cũng là một phần thú vị của hành trình khám phá mà, đúng không? <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/fine_tune_llm_prompt.png' alt='Tinh chỉnh prompt của LLM'> NHỮNG 'SIÊU NĂNG LỰC' CỦA CÁCH TIẾP CẬN NÀY 🚀: Tại sao chúng ta lại 'phát cuồng' với giải pháp này đến vậy? Bởi vì nó mang lại cả tấn lợi ích 'khủng' lận đó: Không cần viết SQL thủ công ✅: 'Tạm biệt' những dòng SQL khô khan! Giờ đây, chỉ cần nói ra điều bạn muốn bằng ngôn ngữ tự nhiên là có báo cáo ngay tắp lự. Siêu linh hoạt 🔄: Mô hình này có thể dễ dàng 'học hỏi' và thích nghi với các bảng dữ liệu mới hoặc những câu hỏi báo cáo 'khó nhằn' hơn. Cứ như một chú tắc kè hoa vậy đó! Bảo mật 'tuyệt đối' 🔒: Hệ thống chỉ cho phép truy vấn các bảng liên quan và đảm bảo các câu lệnh SQL được tạo ra là an toàn. Không lo 'thủng' database nhé! Khả năng 'mở rộng' không giới hạn 📈: Dù bạn có bao nhiêu bộ dữ liệu đi chăng nữa, hệ thống vẫn 'cân' được hết mà không cần phải phát triển 'đo ni đóng giày' cho từng yêu cầu. Cứ gọi là 'khủng long'! <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/llm_benefits_icons.png' alt='Các lợi ích của hệ thống LLM'> LỜI KẾT 'CHẤT LỪ' 🎯: Tóm lại, bằng cách 'bắt tay' với các LLM, chúng ta đã biến công việc 'dịch thuật' từ ý định người dùng sang những câu SQL 'chuẩn không cần chỉnh' thành một quá trình hoàn toàn tự động, mượt mà và hiệu quả 'kinh ngạc'. Giải pháp này không chỉ 'giải phóng' chúng ta khỏi việc viết SQL bằng tay mà còn đảm bảo độ chính xác và khả năng thích ứng cao. Thế nào, bạn có nghĩ đến việc áp dụng một giải pháp 'đỉnh cao' như vậy vào ứng dụng của mình không? Chia sẻ suy nghĩ của bạn cho tôi biết với nhé! <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/llm_ai_handshake.png' alt='Hợp tác giữa con người và AI'>
Hướng dẫn xây dựng ứng dụng web siêu tốc với Rails 8, HTMX và TailwindCSS mà không cần Node/NPM. Tận hưởng lập trình mượt mà, tương tác cao với SQLite3 và bộ ba Solid Cable, Cache, Queue.
Chào bạn! Đã bao giờ bạn rơi vào tình huống ứng dụng của mình bỗng dưng 'đứng hình' đúng lúc quan trọng nhất chưa? Đúng vậy! Khi tôi bắt tay vào dự án này, một thử thách khổng lồ hiện ra: phải làm sao để ứng dụng Ruby on Rails có thể 'chịu nhiệt' nổi một đợt thử nghiệm Proof of Concept (POC) mà trọng tâm là khả năng mở rộng (scalability) siêu khủng. Ứng dụng của chúng ta xử lý các cuộc tư vấn y tế, biến chúng thành những bản 'khai thác bệnh sử' (anamnesis) cực kỳ có cấu trúc nhờ vào 'phép thuật' của OpenAI.Nhưng vấn đề ở đây là gì? Bài kiểm tra tải đầu tiên đã cho thấy 'cô bé' ứng dụng tội nghiệp của chúng ta chỉ có thể xử lý tối đa 20.000 yêu cầu đồng thời mà thôi. Thảm họa đúng không nào?Chính lúc này, 'sóng gió' nổi lên! Tôi phải lao đầu vào, tìm ra những điểm nghẽn và tối ưu hóa từng ngóc ngách của ứng dụng để 'vắt kiệt' từng giọt hiệu suất từ hạ tầng có sẵn – vốn đang chạy trên Sidekiq, Puma và Redis.Kết quả thì sao ư? Chuẩn bị tinh thần nhé: chúng ta đã từ mức chỉ vỏn vẹn 20.000 lên đến con số đáng kinh ngạc 500.000 yêu cầu đồng thời, tận dụng 100% tài nguyên mà không cần tốn thêm một xu nào cho hạ tầng! Bạn có muốn biết chúng tôi đã làm phép thuật này như thế nào không? Cùng tôi khám phá từng chi tiết nhé!<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhxbmveqababs37xa3id1.jpg' alt='Lập trình viên đang căng thẳng với code'>Tìm Bệnh: Nơi Nào Có Đá Ngáng Đường?Ai đã từng trải qua POC thì chắc chắn biết cảm giác như ngồi tàu lượn siêu tốc vậy đó. Tính năng mới ra liên tục, thay đổi xoành xoạch! Nhưng rào cản lớn nhất của chúng ta lúc này lại là khả năng mở rộng (scalability). Ứng dụng của chúng ta, vốn chỉ là một MVP (Sản phẩm Khả dụng Tối thiểu) cho đến thời điểm đó, đang gặp vô vàn khó khăn ở khoản này. Và tệ hơn nữa, chúng ta sẽ bị thử thách cực kỳ nặng nề chính ở điểm này.Chính trong thời khắc 'ngàn cân treo sợi tóc' này, những khái niệm về Hệ điều hành, tưởng chừng chỉ là lý thuyết khô khan ở trường đại học, đã cứu cánh và giúp ứng dụng của chúng ta 'nhảy vọt' lên 500.000 yêu cầu mà hạ tầng cũ chẳng hề 'đổ mồ hôi hột'.Để tìm ra nguyên nhân, chúng tôi đã sử dụng công cụ kiểm tra tải Locust. Mục tiêu của chúng ta là mô phỏng luồng hoạt động thật của người dùng và xem hệ thống có thể 'chịu đựng' được áp lực của bao nhiêu yêu cầu đồng thời.Mẹo vàng cho bạn: Nếu bạn chưa từng nghe về Locust cho các bài kiểm tra tải, thì hãy tìm hiểu ngay nhé! Công cụ này cực kỳ thân thiện và dễ học lắm đó. Bạn có thể xem thêm ở đây: <a href="https://locust.io/">https://locust.io/</a>Locust đã tiết lộ điều gì?Ban đầu, với vài yêu cầu ít ỏi, mọi thứ mượt mà như nhung.Nhưng khi tải trọng tăng lên, thời gian phản hồi bắt đầu 'phi mã' như tên lửa!Và khi chạm ngưỡng 20.000 yêu cầu đồng thời, server của chúng ta đơn giản là… 'từ chối làm việc'! Kết quả: timeouts, lỗi kết nối bị ngắt (connection reset) và cuối cùng là ứng dụng 'ngủm củ tỏi'.Hình ảnh dưới đây đã phơi bày rõ ràng viễn cảnh kinh hoàng đó: rất nhiều timeouts, các kết nối bị hủy và lỗi SSL, chứng minh rằng ứng dụng không thể duy trì kết nối hoặc phản hồi kịp thời. Đó là một hiệu ứng domino làm hệ thống càng thêm quá tải, dẫn đến việc không thể truy cập được. Chẩn đoán đã rõ ràng: kiến trúc của chúng ta chưa sẵn sàng cho 'cuộc chiến' truy cập đồng thời. Chúng ta cần một cuộc 'đại tu' toàn diện về quản lý kết nối, hàng đợi và xử lý song song.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Froabshxqjlk80hkis85z.png' alt='Kết quả kiểm tra tải với Locust, hiển thị lỗi và timeouts'>Lý Thuyết Cứu Nguy!Đối mặt với tình trạng hỗn loạn này, chúng tôi nhận ra rằng việc chỉ tối ưu hóa mã nguồn hoặc 'ném' thêm server vào lửa thôi là chưa đủ. Chúng tôi phải suy nghĩ lại cách ứng dụng của mình xử lý nhiều yêu cầu cùng lúc. Đó là lúc các khái niệm về concurrency (tính đồng thời), parallelism (tính song song), processes (tiến trình) và threads (luồng) trở thành những 'vũ khí bí mật' để mở rộng hệ thống mà không 'đốt' thêm tiền vào hạ tầng.Concurrency: Trò Chơi Giữ Thăng Bằng Nhiều Quả Bóng Cùng LúcConcurrency là một trong những trụ cột của Hệ điều hành. Nó cho phép nhiều phần của một chương trình 'chia sẻ' tài nguyên, luân phiên nhau thực thi để quản lý tải trọng một cách thông minh hơn.Để dễ hình dung, hãy tưởng tượng một nhà hàng nhỏ chỉ có một người phục vụ. Anh ta không thể làm mọi thứ cùng lúc, đúng không? Vậy là anh ta xoay sở thế này:Ghi một yêu cầu, tạm dừng và đi mang một món ăn ra bàn.Quay lại, tiếp tục ghi yêu cầu của một khách hàng khác.Đó chính là concurrency! Các tác vụ không diễn ra cùng một lúc, mà thay phiên nhau, tận dụng từng giây phút người phục vụ rảnh rỗi để làm việc mới, không phải đứng chờ tác vụ trước hoàn thành.Bây giờ, nếu người phục vụ này bắt đầu 'ngập' trong các yêu cầu, điều gì sẽ xảy ra?Việc phục vụ sẽ cực kỳ chậmmm chạppppp.Các yêu cầu sẽ ùn ứ trong bếp.Và một số khách hàng, chán nản, sẽ bỏ đi mà không được phục vụ (đây chính là timeout trong API đó!).Chính xác là điều này đã xảy ra với ứng dụng của chúng ta! API của chúng ta đang xử lý các yêu cầu theo thứ tự, mà không tận dụng concurrency đúng nghĩa. Kết quả? Một hiệu ứng domino của các vụ 'đứng hình' và lỗi kết nối.Concurrency so với Parallelism: Bước Ngoặt Quan Trọng!Hãy quay lại nhà hàng. Và nếu nhà hàng phát triển và chủ thuê thêm nhiều người phục vụ thì sao?Nếu tất cả người phục vụ vẫn dùng chung một cái khay, họ vẫn phải chờ đợi lẫn nhau. Cái này vẫn là concurrency.Nhưng nếu mỗi người phục vụ có khay riêng của mình và có thể phục vụ khách hàng cùng một lúc, thì đó mới chính là parallelism thực sự!Trong ứng dụng của chúng ta, sai lầm lớn là đã coi các tiến trình gây tắc nghẽn (blocking) như thể chúng chỉ là các tiến trình đồng thời, trong khi trên thực tế, chúng hoàn toàn có thể chạy song song! Điều này có ý nghĩa gì trong thực tế?Một số tác vụ đang tranh giành cùng một tài nguyên, trong khi chúng hoàn toàn có thể được phân tán một cách độc lập, mỗi tác vụ trong 'không gian' riêng của nó.API của chúng ta hoạt động như một người phục vụ đơn độc, nhận nhiều yêu cầu, nhưng chỉ xử lý từng yêu cầu một, luân phiên giữa chúng.Giải pháp ư? Biến các tiến trình 'xếp hàng một' thành các tiến trình 'xếp hàng song song', loại bỏ những điểm tắc nghẽn không cần thiết!Processes và Threads: Ai Là Người Trực Tiếp Xử Lý Công Việc?Bây giờ chúng ta đã hiểu rằng cần concurrency và parallelism, làm thế nào để biến điều đó thành hiện thực? Để làm được điều này, hai khái niệm cực kỳ quan trọng là: processes (tiến trình) và threads (luồng).Processes là gì?Một process (tiến trình) giống như một 'bản sao' độc lập của một chương trình đang chạy. Nó có không gian bộ nhớ và tài nguyên riêng của mình, tất cả chỉ dành cho nó. Trong một máy chủ web, mỗi process có thể xử lý một hoặc nhiều yêu cầu. Nhưng nhớ nhé, mỗi process tiêu tốn một lượng bộ nhớ đáng kể, vì vậy việc mở rộng chỉ bằng cách tăng số process có thể rất tốn kém và kém hiệu quả.Threads là gì?Threads (luồng) là các đơn vị thực thi nhỏ hơn, tồn tại bên trong một process. Điểm mấu chốt là, không giống như process, các thread chia sẻ cùng một không gian bộ nhớ. Điều này làm cho chúng nhẹ hơn rất nhiều để tạo và quản lý! Do đó, một ứng dụng có thể xử lý nhiều tác vụ cùng lúc mà không cần phải khởi tạo một process mới mỗi giờ.Hãy tưởng tượng các process như những 'nhà bếp' riêng biệt trong một nhà hàng. Còn threads? Chúng là những 'đầu bếp' bên trong mỗi nhà bếp đó. Nếu mỗi nhà bếp làm việc độc lập, việc giao tiếp sẽ chậm hơn. Nhưng nếu trong mỗi nhà bếp có nhiều đầu bếp chia sẻ nguyên liệu và không gian, các món ăn sẽ ra lò nhanh hơn và hiệu quả hơn rất nhiều!<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/process_thread_analogy.png' alt='Sơ đồ minh họa Processes và Threads như nhà bếp và đầu bếp'>Tối Ưu Concurrency Theo Số Lõi CPU Của ServerĐể đảm bảo hệ thống sử dụng 100% công suất mà không bị quá tải, chúng tôi đã thực hiện một phép tính thông minh dựa trên số lượng lõi CPU. Mỗi lõi CPU có thể xử lý một số lượng tác vụ đồng thời giới hạn. Để quyết định số process và thread cần sử dụng, chúng tôi đã áp dụng một công thức chuẩn:Công Thức Thần Kỳ (hoặc không đến mức thần kỳ lắm, haha):Số lượng process = Số lõi CPU vật lý của máy chủSố lượng thread trên mỗi process = 2 đến 4 thread trên mỗi lõiNói cách khác, nếu máy chủ của bạn có 8 lõi vật lý, bạn có thể cấu hình:8 process Puma (1 process cho mỗi lõi)Từ 16 đến 32 thread tổng cộng (2 đến 4 thread cho mỗi lõi)Tại sao công thức này hiệu quả?Nếu chúng ta sử dụng ít process hơn số lõi, nó giống như việc có một đống động cơ nằm im trong xe: lãng phí CPU.Nếu sử dụng quá nhiều thread, chúng ta có nguy cơ làm tăng độ trễ và tạo ra nhiều điểm tắc nghẽn do tranh giành tài nguyên.Cấu hình như thế nào trong Puma?Trong file config/puma.rb, chúng ta đã để cấu hình như thế này để chạy động:workers ENV.fetch("WEB_CONCURRENCY") { 8 } # Số lượng process dựa trên số lõithreads_count = ENV.fetch("RAILS_MAX_THREADS") { 16 } # Threads trên mỗi processthreads threads_count, threads_countpreload_app!Điểm cực kỳ quan trọng!Hãy kiểm tra, kiểm tra và kiểm tra! Hãy thử nghiệm các cấu hình khác nhau để tìm ra 'điểm vàng', vì hiệu suất có thể thay đổi tùy thuộc vào ứng dụng của bạn làm gì và cơ sở dữ liệu ra sao.Sử dụng các công cụ như htop hoặc top trên Linux để theo dõi việc sử dụng CPU và điều chỉnh khi cần thiết. Luôn luôn cảnh giác nhé!<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/htop_linux.png' alt='Ảnh chụp màn hình htop trên Linux, hiển thị mức sử dụng CPU'>Điều Chỉnh NGINX Để Xử Lý 'Trận Chiến' Yêu CầuNgay cả khi Puma và Sidekiq đã 'hót' lên như chim, vẫn còn một vấn đề nhỏ: NGINX chưa sẵn sàng để xử lý nhiều kết nối đồng thời đến vậy. Vì NGINX hoạt động như một 'người gác cổng' (proxy ngược), chúng ta cần 'dạy' nó cách chấp nhận nhiều người hơn để tránh những lỗi 502 Bad Gateway và timeouts đáng sợ.Những điều chỉnh chúng tôi đã thực hiện trong NGINX:1. Tăng giới hạn kết nối đồng thời:Trong file /etc/nginx/nginx.conf, chúng tôi đã thay đổi các giá trị này để cho phép nhiều kết nối cùng lúc:worker_processes auto; # Nginx giờ đây tự động định nghĩa số process theo số lõiworker_connections 8192; # Mỗi process worker có thể xử lý tới 8192 kết nốimulti_accept on; # Cho phép chấp nhận nhiều kết nối cùng lúcĐiều này có tác dụng gì?worker_processes auto -> Nginx tạo một process cho mỗi lõi CPU, tận dụng tối đa tài nguyên.worker_connections 8192 -> Mỗi process của Nginx giờ đây là một 'quái vật', có thể xử lý 8192 kết nối đồng thời!multi_accept on -> Nginx chấp nhận nhiều kết nối cùng lúc, giảm độ trễ.2. Tăng thời gian chờ (timeout) cho các yêu cầu:Với một loạt yêu cầu đi qua NGINX, một số yêu cầu, đặc biệt là những yêu cầu giao tiếp với OpenAI để chuyển đổi cuộc tư vấn, có thể mất nhiều thời gian hơn. Chúng tôi đã điều chỉnh các timeouts để không làm gián đoạn cuộc trò chuyện giữa chừng:proxy_connect_timeout 60s;proxy_send_timeout 60s;proxy_read_timeout 60s;send_timeout 60s;Điều này có tác dụng gì?Tránh việc các kết nối bị đóng sớm hơn dự kiến trong các yêu cầu tốn thời gian.3. Điều chỉnh bộ đệm phản hồi:Vì một số phản hồi từ API có thể rất lớn, chúng tôi đã tăng bộ đệm để không làm 'cắt xén' thông tin hoặc gây ra lỗi payload. Nó giống như một 'khay' lớn hơn để mang toàn bộ phản hồi:proxy_buffer_size 128k;proxy_buffers 4 256k;proxy_busy_buffers_size 256k;Điều này có tác dụng gì?Ngăn chặn các phản hồi lớn bị cắt và làm cho giao tiếp giữa NGINX và Puma mượt mà hơn.<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/nginx_diagram.png' alt='Sơ đồ minh họa Nginx như một proxy ngược'>Chúng Ta Đã Áp Dụng Tất Cả Những Điều Này Để Giải Quyết Vấn Đề Lớn Của Mình Như Thế Nào?Máy chủ ban đầu của chúng ta là một 'điểm nghẽn', xử lý mỗi yêu cầu như một tiến trình 'blocking' – tức là, một yêu cầu sẽ chiếm trọn tài nguyên cho đến khi hoàn thành, không cho phép các yêu cầu khác đi qua. Hành vi này chính là 'công thức' dẫn đến timeouts và lỗi, làm sập hệ thống khi tải cao. Để mở rộng đẹp mắt và đạt được 500.000 yêu cầu đồng thời, chúng ta đã sử dụng bốn chiến lược chính:1. Điều Chỉnh Concurrency Dựa Trên Số Lõi Server:Trước hết, chúng tôi nhận ra rằng chìa khóa là sử dụng tài nguyên server một cách hiệu quả hơn, không lãng phí CPU và bộ nhớ. Bằng cách nào?Tính toán số lượng process và thread tối ưu dựa trên số lõi vật lý của server.Xác định 1 process Puma cho mỗi lõi và 2 đến 4 thread cho mỗi lõi, đảm bảo hệ thống tận dụng tối đa mà không bị kiệt sức.Theo dõi hiệu suất bằng các công cụ như htop để điều chỉnh các giá trị phù hợp với 'tính cách' của ứng dụng.2. Sử Dụng Server Puma Với Nhiều Threads:Puma là một server web 'đỉnh của chóp' về xử lý đồng thời dựa trên threads. Khác với các server cũ tạo một process mới cho mỗi yêu cầu, Puma duy trì một số lượng process cố định và tạo ra nhiều threads bên trong mỗi process để phục vụ các yêu cầu cùng lúc. Điều này giúp ứng dụng Rails của chúng ta xử lý nhiều yêu cầu hơn rất nhiều mà không biến thành 'kẻ hút máu' bộ nhớ!3. Triển Khai Hàng Đợi Nền Với Sidekiq và Redis:Một số tác vụ, như việc chuyển đổi các cuộc tư vấn y tế bằng OpenAI, mất khá nhiều thời gian và không cần thiết phải thực hiện ngay lập tức. Chúng tôi đã dùng Sidekiq để 'đẩy' những tác vụ này vào 'hàng đợi nền', nơi các 'công nhân' (workers) sẽ thực thi chúng mà không làm tắc nghẽn các yêu cầu chính. Nó giống như việc có một đội ngũ hậu cần chuyên xử lý các công việc 'quan trọng nhưng không khẩn cấp' mà không làm phiền việc phục vụ khách hàng trực tiếp!<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/sidekiq_redis_diagram.png' alt='Sơ đồ minh họa Sidekiq và Redis cho xử lý tác vụ nền'>4. Điều Chỉnh Concurrency Của Cơ Sở Dữ Liệu:Cơ sở dữ liệu là một điểm cực kỳ quan trọng khác. Việc tối ưu hóa các kết nối và đảm bảo các truy vấn siêu hiệu quả là cực kỳ cần thiết để không bị tắc nghẽn do quá nhiều kết nối đồng thời. Rốt cuộc, hàng đợi có nhanh đến mấy mà 'nhà bếp' database không xoay sở kịp thì cũng vô ích thôi!Kết Quả: Từ 'Đứng Hình' Đến 'Máy Bay Phản Lực'!Sau tất cả những thay đổi này, chúng tôi đã chạy lại các bài kiểm tra tải với Locust. Tác động ư? Đáng kinh ngạc!Chúng tôi đã từ 20.000 lên 500.000 yêu cầu đồng thời, sử dụng hạ tầng có sẵn hiệu quả hơn rất nhiều.Độ trễ trung bình của các yêu cầu giảm mạnh, vì server giờ đây có thể xử lý nhiều thứ cùng lúc mà không bị quá tải.Ứng dụng trở nên ổn định như đá, ngay cả dưới tải trọng lớn.Cuối cùng, chúng tôi đã đạt được khả năng mở rộng cần thiết mà không phải tốn một xu nào cho server mới! Chỉ bằng cách áp dụng các khái niệm cơ bản về concurrency, process và thread một cách thông minh. Và đó là cách chúng tôi đã thoát khỏi cảnh 'chật vật' với 20.000 yêu cầu để vươn tới con số hơn 500.000 yêu cầu, với một hạ tầng có chi phí-hiệu quả đáng ghen tị!Một lời cảm ơn 'khổng lồ' đến toàn bộ đội ngũ đã cùng tôi trải qua hành trình điên rồ này! Cảm ơn các bạn rất nhiều!<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/celebration_success.png' alt='Mọi người đang ăn mừng thành công và khả năng mở rộng của dự án'>