Go最新Go分布式爬虫笔记(九)_golang分布式爬虫,2024年最新2024-2024历年字节跳动Golang面试真题解析

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

```
+ Go 语言定义了实现 Linter 的 API,它还提供了 golint 工具,用于集成了几种常见的 Linter。在[源码](https://bbs.csdn.net/topics/618658159)中,我们可以查看怎么在标准库中实现典型的 Linter。


	- Linter 的实现原理是静态扫描代码的 AST(抽象语法树),Linter 的标准化意味着我们可以灵活实现自己的 Linters。不过 golangci-lint 里面其实已经集成了包括 golint 在内的总多 Linter,并且有灵活的配置能力。所以在自己写 Linter 之前,建议先了解 golangci-lint 现有的能力。
+ 在大型项目中刚开始使用 golang-lint 会出现大量的错误,这种情况下我们只希望扫描增量的代码。如下所示,可以通过在[golangci-lint 配置](https://bbs.csdn.net/topics/618658159)文件中调整 new-from-rev 参数,配置以当前基准分支为基础实现增量扫描

 
```
linters:
 enable-all: true
issues:
 new-from-rev: master

```
  • Pre-Commit

    • 在代码通过 Git Commit 提交到代码仓库之前,git 提供了一种 pre-commit 的 hook 能力,用于执行一些前置脚本。在脚本中加入检查的代码,就可以在本地拦截住一些不符合规范的代码,避免频繁触发 CI 或者浪费时间。pre-commit 的配置和使用方法,可以参考TiDB
  • race

    • Go 1.1 提供了强大的检查工具 race 来排查数据争用问题。race 可以用在多个 Go 指令中,一旦检测器在程序中找到数据争用,就会打印报告。这份报告包含发生 race 冲突的协程栈,以及此时正在运行的协程栈。可以在编译时和运行时执行 race,方法如下:
    $ go test -race mypkg
    $ go run -race mysrc.go
    $ go build -race mycmd
    $ go install -race mypkg
    
    

    在下面这个例子中, 运行中加入 race 检查后直接报错。从报错后输出的栈帧信息中,我们能看出具体发生并发冲突的位置。

    » go run -race 2_race.go
    ==================
    WARNING: DATA RACE
    Read at 0x00000115c1f8 by goroutine 7:
      main.add()
        bookcode/concurrence_control/2_race.go:5 +0x3a
    Previous write at 0x00000115c1f8 by goroutine 6:
      main.add()
        bookcode/concurrence_control/2_race.go:5 +0x56
    
    

    第四行 Read at 表明读取发生在 2_race.go 文件的第 5 行,而第七行 Previous write 表明前一个写入也发生在 2_race.go 文件的第 5 行。这样我们就可以非常快速地定位数据争用问题了。
    竞争检测的成本因程序而异。对于典型的程序,内存使用量可能增加 5~10 倍,执行时间会增加 2~20 倍。同时,竞争检测器会为当前每个 defer 和 recover 语句额外分配 8 字节。在 Goroutine 退出前,这些额外分配的字节不会被回收。这意味着,如果有一个长期运行的 Goroutine,而且定期有 defer 和 recover 调用,那么程序内存的使用量可能无限增长。(这些内存分配不会显示到 runtime.ReadMemStats 或 runtime / pprof 的输出。)

  • 覆盖率

    • 一般我们会使用代码覆盖率来判断代码书写的质量,识别无效代码。go tool cover 是 go 语言提供的识别代码覆盖率的工具,

我们为什么需要编程规范?

好处:

  • 促进团队合作
  • 规避错误
  • 提升性能
  • 便于维护

Any fool can write code that a computer can understand. Good programmers write code that humans can understand.

任何傻瓜都可以编写电脑能理解的代码。好的程序员编写的是人类可以理解的代码。

  • 整洁

    • 格式化

      • 代码长度

        • F(行 120)

        • F(函数 40)

          • swich?
        • F(文件 2000)

      • 代码布局

        • 包注释: 对整个模块和功能的完整描述,写在文件头部。
        • Package:包名称。
        • Imports: 引入的包。
        • [F goimports] 当 import 多个包时,应该对包进行分组。同一组的包之间不需要有空行,不同组之间的包需要一个空行。标准库的包应该放在第一组。
        import (
            "fmt"
            "hash/adler32"
            "os"
         
            "appengine/foo"
            "appengine/user"
            "github.com/foo/bar"
            "rsc.io/goversion/version"
        )
        
        
        • Constants:常量定义。
        • Typedefs:类型定义。
        • Globals:全局变量定义。
        • Functions:函数实现。
        • 每个部分之间用一个空行分割。每个部分有多个类型定义或者有多个函数时,也用一个空行分割。
        • 示例如下:
        /*
        注释
        */
        package http
          
        import (
         "fmt"
         "time"
        )
          
        const (
         VERSION = "1.0.0"
        )
        
        type Request struct{
        }
          
        var msg = "HTTP success"
          
        func foo() {
         //...
        }
        
        
      • 空格与缩进

        • [F gofmt] 注释和声明应该对齐
        • [F gofmt] 小括号 ()、中括号[]、大括号{} 内侧都不加空格。
        • [F gofmt] 逗号、冒号(slice 中冒号除外)前都不加空格,后面加 1 个空格。
        • [F gofmt] 所有二元运算符前后各加一个空格,作为函数参数时除外。例如b := 1 + 2。
        • [F gofmt] 使用 Tab 而不是空格进行缩进。
        • [F nlreturn] return 前方需要加一个空行,让代码逻辑更清晰。
        • [F gofmt] 判断语句、for 语句需要缩进 1 个 Tab,并且右大括号}与对应的 if 关键字垂直对齐。例如:
        if xxx {
          
        } else {
        
        }
        
        
        • [推荐] 避免 else 语句中处理错误返回,避免正常的逻辑位于缩进中。如下代码实例,else 中进行错误处理,代码逻辑阅读起来比较费劲。
        if something.OK() {
                something.Lock()
                defer something.Unlock()
                err := something.Do()
                if err == nil {
                        stop := StartTimer()
                        defer stop()
                        log.Println("working...")
                        doWork(something)
                        <-something.Done() // wait for it
                        log.Println("finished")
                        return nil
                } else {
                        return err
                }
        } else {
                return errors.New("something not ok")
        }
        
        

        如果把上面的代码修改成下面这样会更加清晰:

        if !something.OK() {  
            return errors.New("something not ok")
        }
        something.Lock()
        defer something.Unlock()
        err := something.Do()
        if err != nil {   
            return err
        }
        stop := StartTimer()
        defer stop()
        log.Println("working...")
        doWork(something)
        <-something.Done() // wait for it
        log.Println("finished")
        return nil
        
        
        • [推荐] 函数内不同的业务逻辑处理建议用单个空行加以分割。
        • [推荐] 注释之前的空行通常有助于提高可读性——新注释的引入表明新思想的开始。
    • 命名

      • 短,容易拼写;

      • 保持一致性;

      • 意思准确,容易理解,没有虚假和无意义的信息。

      • [F revive] Go 中的命名统一使用驼峰式、不要加下划线

      • [F revive] 缩写的专有名词应该大写,例如: ServeHTTP、IDProcessor。

      • [强制] 区分变量名应该用有意义的名字,而不是使用阿拉伯数字:a1, a2, … aN。

      • [强制] 不要在变量名称中包含你的类型名称。

      • [建议]变量的作用域越大,名字应该越长

      • 包名

        • 简短而清晰
        • [强制] 使用简短的小写字母,不需要下划线或混合大写字母
        • [建议] 合理使用缩写,例如:
        strconv(字符串转换)
        syscall(系统调用)
        fmt(格式化的 I/O)
        
        
        • [强制] 避免无意义的包名,例如 util,common,base 等。
          感觉经常有些通用的
      • 接口命名

        • 单方法接口由方法名称加上 -er 后缀或类似修饰来命名。例如:Reader, Writer, Formatter, CloseNotifier
        • 当一个接口包含多个方法时,请选择一个能够准确描述其用途的名称(例如:net.Conn、http.ResponseWriter、io.ReadWriter)
      • 变量命名

        • [建议]尽可能地短。在这里,i 指代 index,r 指代 reader,b 指代 buffer。
      • 函数命名

        • [建议]如果函数参数的类型已经能够看出参数的含义,那么函数参数的命名应该尽量简短:
        func AfterFunc(d Duration, f func()) *Timer
        func Escape(w io.Writer, s []byte)
        
        
        • [建议]如果函数参数的类型不能表达参数的含义,那么函数参数的命名应该尽量准确:
        func Unix(sec, nsec int64) Time
        func HasPrefix(s, prefix []byte) bool
        
        
        • [建议] 对于公开的函数,返回值具有文档意义,应该准确表达含义,如下所示:
        func Copy(dst Writer, src Reader) (written int64, err error)
        
        func ScanBytes(data []byte, atEOF bool) (advance int, token []byte, err error)
        
        
      • 可导出的变量名: 由于使用可导出的变量时会带上它所在的包名,因此,不需要对变量重复命名。例如 bytes 包中的 ByteBuffer 替换为 Buffer,这样在使用时就是 bytes.Buffer,显得更简洁。类似的还有把 strings.StringReader 修改为 strings.Reader,把 errors.NewError 修改为 errors.New。

      • Error 值命名

        • [建议] 错误类型应该以 Error 结尾。
        • [建议] Error 变量名应该以 Err 开头。
        type ExitError struct {
            ...
        }
        var ErrFormat = errors.New("image: unknown format")
        
        
    • 函数

      • [强制 cyclop] 圈复杂度(Cyclomatic complexity)<10。
      • [强制 gochecknoinits] 避免使用 init 函数
      • [强制 revive] Context 应该作为函数的第一个参数
      • [强制] 正常情况下禁用 unsafe。
      • [强制]​ 禁止 return 裸返回,如下例中第一个 return:
      func (f \*Filter) Open(name string) (file File, err error) {
        for \_, c := range f.chain {
          file, err = c.Open(name)
          if err ! =  nil {
            return
          }
        }
        return f.source.Open(name)
      }
      
      
      • [强制]​ 不要在循环里面使用 defer,除非你真的确定 defer 的工作流程。
      • [强制] 对于通过:= 进行变量赋值的场景,禁止出现仅部分变量初始化的情况。例如在下面这个例子中,f 函数返回的 res 是初始化的变量,但是函数返回的 err 其实复用了之前的 err:
      var err error
      res,err := f()
      
      
      • [建议] 函数返回值大于 3 个时,建议通过 struct 进行包装
      • [建议] 函数参数不建议超过 3 个,大于 3 个时建议通过 struct 进行包装。
    • 控制结构

      • [强制] 禁止使用 goto。
      • [强制 gosimple] 当一个表达式为 bool 类型时,应该使用 expr 或 !expr 判断,禁止使用 == 或 != 与 true / false 比较。
      • [强制 nestif] if 嵌套深度不大于 5
    • 方法

      • [强制 revive] receiver 的命名要保持一致,如果你在一个方法中将接收器命名为 “c”,那么在其他方法中不要把它命名为 “cl”。
      • [强制] receiver 的名字要尽量简短并有意义,禁止使用 this、self 等。

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

尽量简短并有意义,禁止使用 this、self 等。

[外链图片转存中…(img-UQq5Qyl6-1715516304723)]
[外链图片转存中…(img-wYLNqG3X-1715516304724)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值