面试题答案
一键面试设计思路
- 超时处理:
- 使用Go语言的
context.Context
来设置每个子服务接口调用的超时时间。context.Context
可以在整个调用链中传递,方便在不同层级进行超时控制。 - 在启动每个子服务接口调用的goroutine时,通过
context.WithTimeout
创建带超时的上下文,一旦超时,该上下文会被取消,所有依赖此上下文的操作(如http.Request
等)都会被中断。
- 使用Go语言的
- 异常分类处理:
- 网络中断:这类异常通常由底层网络库抛出,如
net/http
包在请求过程中出现网络问题时会返回相应的错误。可以通过检测错误类型中是否包含与网络相关的关键词(如"net/http: request canceled (Client.Timeout exceeded while awaiting headers)"
等)来识别。对于这类异常,可以选择进行有限次数的重试,因为网络问题有时是短暂的。 - 服务内部错误:这类错误一般由子服务自身返回,可能是业务逻辑错误、数据库错误等。可以通过子服务返回的HTTP状态码(如500系列表示服务器内部错误)或自定义的错误码来识别。对于这类错误,通常不建议盲目重试,而是记录详细错误信息并反馈给上层调用者,由上层决定如何处理(如向用户显示友好的错误提示)。
- 网络中断:这类异常通常由底层网络库抛出,如
- 动态调整超时时间:
- 可以根据子服务的历史调用数据来动态调整超时时间。例如,记录每个子服务每次调用的实际耗时,计算一段时间内的平均耗时,并在此基础上增加一定的安全余量作为新的超时时间。
- 也可以根据系统的负载情况进行调整。当系统负载较高时,适当增加超时时间,避免因为短暂的资源紧张导致不必要的超时。
关键代码片段
package main
import (
"context"
"fmt"
"net/http"
"time"
)
// 模拟子服务调用
func callSubService(ctx context.Context, url string) (string, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return "", err
}
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
// 处理网络相关异常
if err.Error() == "net/http: request canceled (Client.Timeout exceeded while awaiting headers)" {
// 重试逻辑
for i := 0; i < 3; i++ {
time.Sleep(time.Second)
resp, err = client.Do(req)
if err == nil {
break
}
}
}
return "", err
}
defer resp.Body.Close()
if resp.StatusCode >= 500 {
// 处理服务内部错误
// 记录详细错误信息
return "", fmt.Errorf("sub - service internal error, status code: %d", resp.StatusCode)
}
// 处理正常响应
// 这里省略读取响应体的具体逻辑
return "success", nil
}
func main() {
// 设置全局超时时间
globalTimeout := 10 * time.Second
ctx, cancel := context.WithTimeout(context.Background(), globalTimeout)
defer cancel()
subServiceURL := "http://example.com/sub - service"
result, err := callSubService(ctx, subServiceURL)
if err != nil {
fmt.Printf("Call sub - service failed: %v\n", err)
return
}
fmt.Printf("Call sub - service result: %s\n", result)
}
在上述代码中:
callSubService
函数模拟了对子服务的调用,通过context.Context
来控制超时,并对网络中断和服务内部错误进行了分类处理。- 在
main
函数中,创建了一个带超时的上下文,并调用callSubService
函数。实际应用中,可能会并发调用多个callSubService
来模拟future模式调用多个子服务接口。