Sử dụng `recover`
Thuộc phần Logic và luồng điều khiển trong hành trình GO của Coddy. Bài học 43 trên 68.
Trong khi panic dừng chương trình ngay lập tức, Go cung cấp cách để bạn lấy lại quyền kiểm soát thông qua hàm (function) recover. Hàm recover cho phép bạn bắt panic và xử lý một cách an toàn, tránh để toàn bộ chương trình bị sập.
Tuy nhiên, recover có một yêu cầu rất cụ thể: chỉ có thể dùng nó bên trong một hàm được trì hoãn. Khi xảy ra panic, Go thực thi tất cả các hàm được trì hoãn trước khi chương trình kết thúc, nhờ đó recover có cơ hội chặn panic:
func riskyFunction() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered from panic:", r)
}
}()
panic("something went wrong!")
}Hàm recover trả về giá trị đã được truyền vào panic, hoặc nil nếu không xảy ra panic. Nhờ đó, bạn có thể kiểm tra xem panic có xảy ra hay không và thực hiện hành động phù hợp, chẳng hạn như ghi nhật ký lỗi hoặc dọn dẹp.
Giống như panic, bạn chỉ nên dùng recover khi thật cần thiết trong Go. Từ này thường xuất hiện trong những tình huống như máy chủ web, khi bạn muốn ngăn lỗi panic của một yêu cầu làm sập toàn bộ máy chủ để máy chủ tiếp tục xử lý các yêu cầu khác.
Thử thách
DễXây dựng trình xử lý yêu cầu máy chủ web dùng recover để xử lý các panic một cách an toàn và ngăn toàn bộ máy chủ gặp sự cố. Thử thách này minh họa cách dùng recover bên trong một hàm trì hoãn để bắt panic và duy trì sự ổn định của máy chủ.
Bạn sẽ nhận hai đầu vào:
- Một chuỗi chứa thông tin chi tiết về yêu cầu theo định dạng
"endpoint,method,action"(ví dụ:"/api/users,GET,process") - Một chuỗi chứa cấu hình máy chủ theo định dạng
"server_name,max_requests"(ví dụ:"WebServer,100")
Nhiệm vụ của bạn là:
- Phân tích đầu vào thứ nhất bằng cách tách theo dấu phẩy để lấy endpoint, method và action
- Phân tích đầu vào thứ hai bằng cách tách theo dấu phẩy để lấy tên máy chủ và số yêu cầu tối đa
- Chuyển chuỗi số yêu cầu tối đa thành số nguyên
- Tạo hàm có tên
handleRequestnhận endpoint, method, action và tên máy chủ làm tham số - Bên trong
handleRequest, thêm một hàm trì hoãn dùngrecoverđể bắt mọi panic:- Nếu khôi phục được panic (recover trả về giá trị khác nil), in ra:
"Server [server_name] recovered from panic: [panic_value]" - Sau khi xử lý panic, in ra:
"Request handling completed safely"
- Nếu khôi phục được panic (recover trả về giá trị khác nil), in ra:
- Sau hàm trì hoãn, mô phỏng việc xử lý yêu cầu dựa trên action:
- Nếu action là
"process": in ra"Processing [method] request to [endpoint]" - Nếu action là
"panic_nil": in ra"Attempting dangerous operation"rồi gọipanic("nil pointer access") - Nếu action là
"panic_overflow": in ra"Attempting resource allocation"rồi gọipanic("memory overflow") - Nếu action là
"panic_timeout": in ra"Attempting database connection"rồi gọipanic("connection timeout") - Với mọi action khác: in ra
"Unknown action: [action]"rồi gọipanic("invalid action")
- Nếu action là
- Trong hàm main, in thông báo khởi động máy chủ:
"Starting [server_name] with max [max_requests] requests" - Gọi hàm
handleRequestvới các tham số đã phân tích - Sau khi gọi hàm, in trạng thái máy chủ:
"Server [server_name] is still running" - Cuối cùng, in phần tóm tắt:
"Server Summary:""Name: [server_name]""Max Requests: [max_requests]""Last Request: [method] [endpoint]""Action Performed: [action]""Status: Operational"
Dùng package strings để tách các chuỗi đầu vào theo dấu phẩy và package strconv để chuyển chuỗi số yêu cầu tối đa thành số nguyên. Thử thách này minh họa cách recover giúp máy chủ xử lý các panic bất ngờ một cách an toàn, ngăn lỗi của từng yêu cầu riêng lẻ làm sập toàn bộ hệ thống.
Tự làm thử
package main
import (
"fmt"
"strconv"
"strings"
)
func main() {
// Đọc đầu vào
var requestDetails string
var serverConfig string
fmt.Scanln(&requestDetails)
fmt.Scanln(&serverConfig)
// Phân tích thông tin yêu cầu
requestParts := strings.Split(requestDetails, ",")
endpoint := requestParts[0]
method := requestParts[1]
action := requestParts[2]
// Phân tích cấu hình máy chủ
configParts := strings.Split(serverConfig, ",")
serverName := configParts[0]
maxRequests, _ := strconv.Atoi(configParts[1])
// TODO: Viết code bên dưới
// Tạo hàm handleRequest có cơ chế recover
// In ra thông báo khởi động máy chủ
// Gọi hàm handleRequest
// In ra trạng thái và bản tóm tắt của máy chủ
}Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Logic và luồng điều khiển
1Luồng điều khiển nâng cao
Switch với `fallthrough`Thoát khỏi vòng lặp lồng nhauTiếp tục một vòng lặp cụ thểCâu lệnh `goto`Ôn tập: điều khiển vòng lặp nâng cao4Dự án: Danh sách việc cần làm đơn giản
Thiết lập dự ánThêm một việc cần làm7Xử lý lỗi chuyên sâu
Kiểu lỗi tùy chỉnhBọc lỗi bằng `%w`Gỡ bọc lỗi bằng `errors.Is`Gỡ bọc lỗi bằng `errors.As`Tìm hiểu `panic`Sử dụng `recover`Ôn tập: phép chia an toàn2Struct và phương thức
Định nghĩa phương thức cho structReceiver giá trịReceiver con trỏChọn receiverPhương thức và hàmÔn tập: hành vi của struct5Tìm hiểu chuyên sâu về map
Map chứa structCon trỏ làm giá trị của mapKiểm tra map nilSo sánh các mapÔn tập: bộ đếm tần suất từ8Dự án: Quản lý hàng tồn kho đơn giản
Tổng quan dự ánKiểm tra hàng tồn kho3Interface (cơ bản)
Interface là gì?Định nghĩa interfaceTriển khai interfaceDùng kiểu interfaceInterface rỗngKhẳng định kiểuType switchÔn tập: hình dạng và hành viTự luyện tập: Trình biên dịch Go online