go 没有异常 怎么判断逻辑以外的错误 全局的处理

6 天前
 dzdh

比如数据库服务吧。

程序启动,先连 db 。连成功。启动 web 服务。然后 setup 路由啥的一堆。

好,服务启动成功了。

现在接受 http 请求,此时数据库崩了。

gorm 返回了 err 。比如代码如下

// userRepository.go
func GetUser(uid int64) (*User, error) {
    user:=new(User)
    if err :=db.model(user).Find(user).err; err != nil {
        return nil,err
    } else { return user, nil }
}

按照 java/php 这种的逻辑。我可以抛出个异常。然后有个地方是处理这个特殊的异常。返回 500,db no connection 。

go 里边咋做呢?现在数据库崩了以后,被业务中间件拦截到了 返回 401 unauthorized 。

repository 由 http 服务调用。我要直接 panic 吗 0.0 http 的中间件 recover 住判断 err 是哪种错误? 这么粗暴的吗?

3535 次点击
所在节点    Go 编程语言
40 条回复
lqs
6 天前
ctx 要一路带进去(适当的地方加上 WithTimeout / WithCancel 等)
err 要一路带出来(适当的地方包裹一下加上错误提示和相关参数上下文)
iyaozhen
6 天前
学 go 有个段子:和你们这些搞 java 的讲不清楚
jnyanmeng
6 天前
@iyaozhen java 以外我都可以这样讲:和你们这些搞 java 的讲不清楚😄
Ghostisbored
6 天前
每次看到一些回复都看得好笑 用个开发语言还用出优越感来了。php 流行的时候: 有新人问问题 你不适合 php 还是回去吧 ; java 流行的时候:你不适合 java 还是回去吧 ; go 近几年因为云原生 加上国内字节用的多 不少项目开始用了 又开始了:你不适合 go 还是去搞 java 吧 ; rust 现在被大家喜欢很多软件开始锈化 是不是过一段时间 要出现 你不适合搞 rust 快回去搞 go 吧🙄
rower
6 天前
1.有处理错误的中间件,有处理 panic 的中间件,这两个是不一样的

2.这里是错误类型,我们走处理错误的中间件

3.你想返回的 http 状态码是 500 ,同时错误信息是 db no ....

却被 401 处理了

对于这种情况是创建自定义错误类型,参考

https://github.com/ardanlabs/service6-video/tree/main/app/api/errs

```
// Error represents an error in the system.
type Error struct {
Code ErrCode `json:"code"`
Message string `json:"message"`
}
```

这里的 message 就是我们的错误信息`db no ...`

这里的 code 是我们内部错误的编码,比如说授权错误,code = 1 ,数据库错误,code = 2

参考 code 设计

https://github.com/ardanlabs/service6-video/blob/main/app/api/errs/codes.go

4.如何将 不同的错误转换为对应的 http 状态码,需要建立 code 和 http 状态码的 map 关系

参考

https://github.com/ardanlabs/service6-video/blob/main/api/http/api/mid/errors.go

5.错误中间件处理错误

这里处理错误时,如果是我们自定义的错误,就将 code 转换为对应的 http 状态码,错误信息不变。

如果不是自定义错误,表明是未知错误,返回 {500,unknow err}

参考 https://github.com/ardanlabs/service6-video/blob/main/app/api/mid/errors.go

```
func Errors(ctx context.Context, log *logger.Logger, handler Handler) error {
err := handler(ctx)
if err == nil {
return nil
}

log.Error(ctx, "message", "ERROR", err.Error())

// 这里判断是不是自定义错误
if errs.IsError(err) {
return errs.GetError(err)
}

return errs.Newf(errs.Unknown, errs.Unknown.String())
}
```
z1829909
6 天前
每一层调用处理能处理的 error, 不能处理的加一些额外信息返回上一层
异常其实也差不多, 只不过你自己不需要手动处理, 默认是往上抛的
代码里尽量不要 panic, 程序启动的时候, 做一些必要的依赖检测, 如果一些关键依赖缺失或者启动异常可以 panic
povsister
6 天前
和你们这些搞 java 的说不清楚.jpg
(狗头保命
bli22ard
6 天前
java 是 try catch ,只需要处理 exception 。go 里面有两种一种是 panic ,一种是 error ,panic 通过 recover 捕获,error 就是带在函数返回值里面返回。可能是最佳实战的做法是,你调用了标准里,或者第三方库,这些库返回 error 之后,你应该先用一些第三方 error 库 wrap 一下,主要目的是记录一下 error 发生的调用栈,这样上层什么位置拿到 error ,都能打印出来这个 error 是哪个位置发生的。还有一种,目的类似的做法,不记录调用栈,而改为附加一个错误码进去,这样上层的任何调用者也可以知道 error 哪里发生的,不过维护错误码这种方式维护时间越长,越容易搞混乱,导致排查问题困难。
楼上有说,将 error 转换 panic 的做法,这样做看起来比较爽,不用多余定义 error 返回值,不用 if err!=nil 判断,只需调用入口 recover 住,但是这种做法,不是主流做法,至于为什么不主流,知道的可以解释下🤭。
haierspi
6 天前
直接 Recover 返回 API 啊
soul11201
6 天前
换个思路看下这个问题:调用栈比较深的时候,是否强制程序员显示处理流程异常终止情况。

1. PHP 是不强制的,在调用处看不出来处理程序可能出错。如果不 Catch 一个 Exception 的基类,可能导致异常漏处理;;但是写的很多,代码就很啰嗦、过度防御 。两个方面不管从哪个角度来说,都是工程质量堪忧。

2. Go err 最佳实践,其实就是强制显示处理错误,调用处就能看出来程序可能处理错误,整体看上去就是做 err 体操,有些呆板。

3. Java Exception 的 Checked Exception 和 Runtime Exception 设计思路,分别是 Go err 体操路子和 PHP 的埋雷路子。Checked Exception 相比 err 体操只不过是标记在了方法签名处,看上去代码少写了两行,但 Checked Excption 是从哪里丢出来的就不太明确了。从 Checked Exception 的实际使用情况来看,偏 Runtime Exception 方向倾斜,就是逐层标记不处理的多,垃圾~

从设计和实际使用综合来看,还是 Go Err 相对来说更严谨一点
lysShub
6 天前
@chen11 我也遇到过一个忽略 err 导致的问题,搞了整整一天
henix
5 天前
我的话这种情况不会使用 panic / recover ,那个是给意料之外的严重异常用的
这种确实需要一路 return nil, err
如果需要中间处理,那在最开始创建 err 的时候选择一个特定类型,中间件用 errors.Is 判断
以上是如果你用网上的常用框架的话就这么做

我个人认为这些框架的错误处理设计得不好,我开发自己的 web 项目的时候不用任何框架,只用 go 标准库
我设计的 controller 会返回:(结果, err1, err2)
其中 err1 代表用户输入错误,比如参数检查错误,要给用户返回 4xx
err2 代表服务器内部错误,要给用户返回 5xx
用这种方法,不需要 errors.Is 判断类型,只需要判断 err2 != nil 即可
fovecifer
5 天前
我的建议是要慎重使用 panic ,切记
bv
4 天前
Don’t use panic for normal error handling. Use error and multiple return values.

https://go.dev/wiki/CodeReviewComments#dont-panic
ninjashixuan
4 天前
慎用 panic ,因为你不知道什么时候你主动 panic 的函数跑着一个新的没有 recovery 的 goroutine 里,这样就 gg 了。
wnanbei
4 天前
不是,楼上你们这些人真的敢在生产环境用 panic 啊?真不怕万一吗?
我们线上用 panic 的仅仅在启动服务的阶段,前置条件不满足服务无法启动才 panic 。
DefoliationM
4 天前
有 Java 经验的不建议转其他语言。“Java 那种迂腐笨重的思维,你是忘不掉的。”
northluo
3 天前
@chen11 遇到程序崩溃,大概率是 map 的同时读写导致的,因为大部分的异常都能 recover 住,只有 map 的多协程同时读写会导致这个问题,建议你梳理下你们项目中的 map 是不是有些被其他协程读写了
chen11
3 天前
@northluo 自己写的代码一路 recover 都没找到错误,结果发现是之前的老代码那里报了个空指针,没有返回 error ,所以就像楼主说的,“成熟的 go 项目。应该是各种一路 return nil, err..”
yyj08070631
3 天前
一路 return 出去也可以的,但有个小问题,这样拿不到初始位置的堆栈,所以还得加个类型把堆栈包进去抛出

(不过我们业务项目也是直接 panic 出去给 recover 打错误日志的,这种做法由于日志信息不是显式的,所以查起来稍微麻烦点,但耐不住方便,错误处理的代码可以减少很多,本来一行 log 、一行 resp 、两行 if err!= nil ,现在一个 panic 就完事了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1100894

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX