MST

星途 面试题库

面试题:Go defer在复杂错误处理场景的优化策略

在一个具有多层嵌套函数调用且存在多种错误类型的复杂Go程序中,如何利用defer语句实现高效、优雅的错误处理,同时避免资源泄露和错误传播混乱。请设计一个具体的场景,并给出详细的代码实现和解释。
46.0万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

设计场景

假设我们正在编写一个处理文件读取和数据库操作的Go程序。程序需要从文件中读取数据,解析数据后插入到数据库中。这个过程涉及多层嵌套函数调用,并且可能会出现文件读取错误、数据解析错误以及数据库插入错误。

代码实现

package main

import (
    "database/sql"
    "fmt"
    "os"

    _ "github.com/lib/pq" // 这里假设使用PostgreSQL,实际根据需要更改
)

// 模拟数据库连接
func connectDB() (*sql.DB, error) {
    // 实际连接字符串根据数据库实际情况填写
    db, err := sql.Open("postgres", "user=postgres dbname=mydb sslmode=disable")
    if err != nil {
        return nil, err
    }
    // 尝试ping数据库,确保连接可用
    err = db.Ping()
    if err != nil {
        return nil, err
    }
    return db, nil
}

// 从文件读取数据
func readFile(filePath string) ([]byte, error) {
    data, err := os.ReadFile(filePath)
    if err != nil {
        return nil, err
    }
    return data, nil
}

// 解析数据
func parseData(data []byte) ([]string, error) {
    // 简单示例,实际可能更复杂
    if len(data) == 0 {
        return nil, fmt.Errorf("data is empty")
    }
    return []string{string(data)}, nil
}

// 插入数据到数据库
func insertData(db *sql.DB, data []string) error {
    tx, err := db.Begin()
    if err != nil {
        return err
    }
    stmt, err := tx.Prepare("INSERT INTO my_table (column1) VALUES ($1)")
    if err != nil {
        tx.Rollback()
        return err
    }
    defer stmt.Close()

    for _, value := range data {
        _, err = stmt.Exec(value)
        if err != nil {
            tx.Rollback()
            return err
        }
    }

    err = tx.Commit()
    if err != nil {
        return err
    }
    return nil
}

func main() {
    filePath := "test.txt"
    db, err := connectDB()
    if err != nil {
        fmt.Println("Failed to connect to database:", err)
        return
    }
    defer db.Close()

    data, err := readFile(filePath)
    if err != nil {
        fmt.Println("Failed to read file:", err)
        return
    }

    parsedData, err := parseData(data)
    if err != nil {
        fmt.Println("Failed to parse data:", err)
        return
    }

    err = insertData(db, parsedData)
    if err != nil {
        fmt.Println("Failed to insert data into database:", err)
        return
    }

    fmt.Println("Data inserted successfully")
}

解释

  1. defer用于资源清理
    • connectDB函数中,虽然没有显式使用defer关闭数据库连接,但在main函数中获取数据库连接后,使用defer db.Close()确保无论后续操作是否成功,数据库连接都会被关闭,避免资源泄露。
    • insertData函数中,使用defer stmt.Close()确保在函数结束时关闭数据库语句对象,防止资源未释放。
  2. 错误处理
    • 每个函数在执行可能出错的操作后,立即检查错误并返回。如果错误发生,后续的函数调用将不会执行,避免了错误传播混乱。
    • main函数中,依次调用各个功能函数,并在每次调用后检查错误。如果出现错误,打印错误信息并返回,从而有效地处理了不同类型的错误。
  3. 数据库事务处理
    • insertData函数中,使用数据库事务来确保数据插入的原子性。如果在插入过程中出现任何错误,通过tx.Rollback()回滚事务,避免部分数据插入成功而部分失败的情况。

通过这种方式,利用defer语句结合合理的错误处理流程,实现了高效、优雅的错误处理,同时避免了资源泄露和错误传播混乱。