面试题答案
一键面试错误生成
- 自定义错误类型:在Go语言中,为每个微服务定义特定的错误类型,便于区分不同服务产生的错误。
type Service1Error struct {
ErrMsg string
}
func (e *Service1Error) Error() string {
return e.ErrMsg
}
- 错误包含上下文信息:在错误中添加相关的上下文信息,例如请求ID等,方便追踪错误来源。
type Service1Error struct {
ErrMsg string
RequestID string
}
func (e *Service1Error) Error() string {
return fmt.Sprintf("RequestID: %s, Error: %s", e.RequestID, e.ErrMsg)
}
错误传递
- RPC通信:如果使用gRPC,在定义proto文件时,将错误信息作为响应的一部分。
syntax = "proto3";
package yourpackage;
service YourService {
rpc YourMethod(YourRequest) returns (YourResponse) {}
}
message YourRequest {
string request_id = 1;
// other fields
}
message YourResponse {
string error_msg = 1;
// other fields
}
在Go代码中,处理错误并返回:
func (s *YourServiceImpl) YourMethod(ctx context.Context, req *pb.YourRequest) (*pb.YourResponse, error) {
if someErrorCondition {
return &pb.YourResponse{
ErrorMsg: "Error in Service1",
}, nil
}
return &pb.YourResponse{
// normal response fields
}, nil
}
- HTTP通信:对于HTTP,通过HTTP状态码和响应体传递错误信息。
func yourHandler(w http.ResponseWriter, r *http.Request) {
if someErrorCondition {
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(map[string]string{
"error": "Error in Service1",
})
return
}
// normal response
}
错误聚合
- 中间件方式:可以使用中间件来收集和聚合错误。例如,在HTTP服务中,可以定义一个错误处理中间件。
func errorHandlerMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var errors []string
defer func() {
if len(errors) > 0 {
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(map[string][]string{
"errors": errors,
})
}
}()
next.ServeHTTP(w, r)
})
}
- 上下文传递:在多个微服务调用过程中,利用
context
传递错误相关信息。
func service1(ctx context.Context) error {
if someErrorCondition {
err := &Service1Error{
ErrMsg: "Error in Service1",
RequestID: ctx.Value("request_id").(string),
}
return err
}
return nil
}
func service2(ctx context.Context) error {
if someErrorCondition {
err := &Service2Error{
ErrMsg: "Error in Service2",
RequestID: ctx.Value("request_id").(string),
}
return err
}
return nil
}
func main() {
ctx := context.WithValue(context.Background(), "request_id", "12345")
var errors []error
err1 := service1(ctx)
if err1 != nil {
errors = append(errors, err1)
}
err2 := service2(ctx)
if err2 != nil {
errors = append(errors, err2)
}
if len(errors) > 0 {
// 处理聚合的错误
}
}
最终呈现给用户
- 友好的错误信息:对于外部用户,将聚合的错误转换为友好的提示信息。例如,将内部错误信息映射为用户能理解的错误提示。
func formatErrorForUser(err error) string {
if _, ok := err.(*Service1Error); ok {
return "There was an issue with the service. Please try again later."
}
return "An unexpected error occurred. Please contact support."
}
- 日志记录:同时,记录详细的错误信息到日志文件,便于开发人员排查问题。
func logError(err error) {
logger.Println(err.Error())
}
利用context辅助实现
- 传递请求ID:
context
可以方便地在不同微服务间传递请求ID等上下文信息,用于错误追踪。
func client() {
ctx := context.WithValue(context.Background(), "request_id", "unique_id_123")
// 调用微服务
response, err := yourServiceClient.YourMethod(ctx, &pb.YourRequest{
RequestId: "unique_id_123",
// other fields
})
if err != nil {
logError(err)
}
}
- 控制错误传播范围:通过
context
的取消机制,可以在必要时取消整个请求链,避免无效的错误传递和处理。
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
err := service1(ctx)
if err != nil {
// 处理错误
}
通过以上设计思路和代码示例,可以实现一个统一且高效的分布式错误处理策略。