Go进阶训练营第三课问题收集
2021年11月4日 更新
开启更多功能,提升办公效能
Go 进阶训练营第二课问题收集

课上推荐文章书籍记录

助教老师回答:

  1. DAO 层与 Service 层异常应该如何处理?DAO 层仅仅使用 pkg/errors 进行 wrap,Service 进行自定义错误码的定义?在 go-kratos 里 ecode 包在哪一层使用比较合适?DAO 层、Service 层?<基础层透传错误,仅在需要处理错误的层记录日志,如:dao 层透传错误,service 层转换出基础错误码对外暴露,并记录错误日志>
  1. 对于每个函数里面都会出现多个 error 返回,就会出现层层嵌套的 error,在整个工程项目设计,如何合理全局处理这些函数里的 error 以及进行日志处理?譬如调用第三方的接口,服务,int 类型转换等等都会出现 error <遇到 err 就会处理 err,所以不会同时存在两个 err,需要将当前的 err 处理了再继续,所以一般仅仅只会有一个要处理的 err,至于这个 err 是在当前处理,还是透传到上一级,需要根据业务判断,透传时候通常使用 wrap 包裹 err,携带上当前栈信息>
  1. 在应用分层中, 对于 error 返回应该在哪一层, 捕获 error 错误报警在哪层 <基础组件一般抛出错误,在处理 err 时候记录日志(通常是最上层)即记录的日志是错误码不变但是携带了调用栈信息的错误,报警分两种一种是监控日志数量的,另外一种是 trace 打点直接上报的,尽量不要在框架中做报警的事情,即将报警异步化>
  1. Struct 可以认为是值类型么?如果有 slice/map/嵌套 struct 字段, 还可以认为是值类型么? <struct 是值类型,struct 是无法直接内嵌 slice 和 map 的(不可匿名)>
  1. panic 或者 error 对于事务一致性有没有好的实践?(非分布式事务)<开启事务手动提交,没正确的时候别 commit 就行,正确的时候记得 commit,其他想起来的地方 rollback 下,没想起来的漏掉问题也不大>
  1. 在 http 或者 rpc 服务中,处理异步的时候通过 channel 的方式去消费, 当流量大的时候, 消费能力不足的话, 会阻塞请求吧, 请问这种怎么处理,直接引入消费队列中间件?<chan 类似个队列,在缓存数量之外会阻塞,之内会不阻塞,通常是多开 go routine 数量解决并发,消费队列中间件只是在请求处理上做到最快的处理,即写入队列即完成,这是两个维度考虑的问题,用着消息队列也就预示着不能及时反馈任务处理结果,仅能返回加入任务成功>
  1. golang 里面有没有类似其他语言的 box 或 unbox 的操作?例如一个基本类型 int 转为 interface{} <不同于 C#/java,go 中的类型不区分基础类型和包装类型,因此没有 box 和 unbox 这类操作, int 类型可以直接转为 interface{};>
  1. 数据查询不到就返回 error 么。 查询列表返回空 slice 感觉也可以啊<看具体业务,但是要有能区分是数据为空还因为网络等其他因素没查到>
  1. error 处理单元测试覆盖率问题,if else 太多,导致覆盖率不够,怎么办?难道需要每个 error case 都要实现吗?<if else 本身就是逻辑上的分支,要想覆盖到分支,就需要实现,做覆盖测试的时候没准就发现永远走不到的逻辑呢>
  1. 查询为空时,用 err 返回做处理。如果是服务之间的调用,也是用 err 来处理空查询吗。如果是,那么 err 处理格式和方法有什么特别需要注意的地方?服务之间调用用错误码比较合适
  1. 怎么能查看到 error 的异常栈,好像只有 panic 的时候可以看到 <err 的栈需要自己包裹,直接使用 github.com/pkg/errors 类 处理即可>
  1. 有时候我们依赖各个不同区域的客户端,一般会使用一个 map 进行存储,然后在具体使用的时候再读取,但是这么做的话,会在每个具体的函数调用时都需要判断一波客户端是否存在,这种场景有比较好的办法消除错误么,虽然多些一个 if err 也不是什么大问题,但是每个都写就想处理一下

下面的代码我觉得并没有实质解决问题,也就是包了一层判断,无非就是封装了一下,是否可以考虑不用 err,client 方法直接返回存在的 iclient 或者不存在则返回 nil

type repository struct {
clients map[string]xxx.IClient
}

func (r *repository) client(region, env string) (xxx.IClient, error) {
client, ok := r.clients[region+"-"+env]
if !ok {
return nil, errors.Errorf("client not found, region: %s, env: %s", region, env)
}
return client, nil
}

func (r *repository) GetTasks(region, env string, uuids []string) ([]domain.Task, error) {
client, err := r.client(region, env)
if err != nil {
return nil, err
}
// 其他业务代码
}
  1. error 是个 interface, 但没有大写, 也就不是可导出的类型。但在代码中还是可以处处使用。 对于未导出类型,只能在同包级别访问,如果超出了同一个包,那应该是在这个包的外面定义的。 我们自己写的代码包,是被哪个包给包裹了呢?如果不是这样,不可能访问到小写的 error 类型呀?
  • "The error identifiers are implicitly declared in the universe block"


  1. 什么是强依赖 ,什么是弱依赖

强依赖:没有你我活不了;弱依赖:不是没你不行,只是有你更好


毛剑老师回答:

  1. [Assert errors for behaviour, not type] 这个地方钉钉断了,能再讲解一下吗?调用 IsTemporary 函数是看作一种行为是吗?
  1. 毛老师,上周六讲的 gRPC 的标准健康检测协议具体是怎么做的呢,grpc.io 网站上没有找到,gprc 的 GitHub 仓找到一篇,是说定义健康检测 IDL,健康状态还是 server 端自己实现报告啊,不太明白老师课件里说的标准化协议是怎么回事,只是指 IDL 的强约束吗?<https://github.com/grpc/grpc/blob/master/doc/health-checking.md>

func Go(f func()) {
go func() {
defer func() {
if err := recover(); err != nil {
// 打印日志
fmt.Println("panic",err)
}
}()
// 调用
f()
}()
}

毛老师 底层的基础兜底函数是这样写的吗? 如果要开协程的形参不一样怎么办呀?每个方法都包一下吗?