2024年Go最全Go分布式爬虫笔记(九)_golang分布式爬虫,2024年最新2024最新阿里Golang面经

img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

		} 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 等。
	
	 
	```
	
	func (c Client) done() error {
	 // ...
	}
	func (cl Client) call() error {
	 // ...
	}
	
	```
+ 注释


	- [强制] 无用注释直接删除,无用的代码不应该注释而应该直接删除。即使日后需要,我们也可以通过 Git 快速找到。
	- [强制] 紧跟在代码之后的注释,使用 //。
	- [强制] 统一使用中文注释,**中英文字符之间严格使用空格分隔**。
	- [强制] 注释不需要额外的格式,例如星号横幅。
	- [强制] **包、函数、方法和类型的注释说明都是一个完整的句子,以被描述的对象为主语开头**。Go 源码中都是这样的。
	
	 
	```
	// queueForIdleConn queues w to receive the next idle connection for w.cm.
	// As an optimization hint to the caller, queueForIdleConn reports whether
	// it successfully delivered an already-idle connection.
	func (t \*Transport) queueForIdleConn(w \*wantConn) (delivered bool) 
	
	```
	- [强制] Go 语言提供了文档注释工具 [go doc](https://bbs.csdn.net/topics/618658159),可以生成注释和导出函数的文档。文档注释的写法可以参考文稿中的链接。
	- [强制 godot] **注释最后应该以句号结尾**。
	- [建议] 当某个部分等待完成时,可用 **TODO:** 开头的注释来提醒维护人员。
	- [建议] 大部分情况下使用**行注释**。**块注释主要用在包的注释上**,不过块注释在表达式中或禁用大量代码时很有用。
	- [建议] 当某个部分存在已知问题需要修复或改进时,**可用 FIXME: 开头的注释来提醒维护人员**。
	- [建议] 需要特别说明某个问题时,可用 **NOTE:** 开头的注释。
+ 结构体


	- [强制] 不要将 Context 成员添加到 Struct 类型中。
  • 一致

  • 高效

    • [强制] Map 在初始化时需要指定长度make(map[T1]T2, hint)。
    • [强制] Slice 在初始化时需要指定长度和容量make([]T, length, capacity)。扩容性能损失
    • [强制] time.After() 在某些情况下会发生泄露,替换为使用 Timer。
    • [强制] 数字与字符串转换时,使用 strconv,而不是 fmt
    • [强制] 读写磁盘时,使用读写 buffer
    • [建议] 谨慎使用 Slice 的截断操作和 append 操作,除非你知道下面的代码输出什么:
    x := []int{1, 2, 3, 4}
    y := x[:2]
    fmt.Println(cap(x), cap(y))
    y = append(y, 30)
    fmt.Println("x:", x)
    fmt.Println("y:", y)
    
    
    • [建议] 任何书写的协程,都需要明确协程什么时候退出。
    • [建议] 热点代码中,内存分配复用内存可以使用 sync.Pool 提速
    • [建议] 将频繁的字符串拼接操作(+=),替换为 StringBuffer 或 StringBuilder。
    • [建议] 使用正则表达式重复匹配时,利用 Compile 提前编译提速
    • [建议] 当程序严重依赖 Map 时,Map 的 Key 使用 int 而不是 string 将提速
    • [建议] 多读少写的场景,使用读写锁而不是写锁将提速。
  • 健壮

    • [强制] 除非出现不可恢复的程序错误,否则不要使用 panic 来处理常规错误,使用 error 和多返回值。
    • [强制 revive] 错误信息不应该首字母大写(除专有名词和缩写词外),也不应该以标点符号结束。因为错误信息通常在其他上下文中被打印。
    • [强制 errcheck] 不要使用 _ 变量来丢弃 error。如果函数返回 error,应该强制检查。
    • [建议] 在处理错误时,如果我们逐层返回相同的错误,那么在最后日志打印时,我们并不知道代码中间的执行路径。例如找不到文件时打印的No such file or directory,这会减慢我们排查问题的速度。因此,在中间处理 err 时,需要使用 fmt.Errorf 或第三方包给错误添加额外的上下文信息。像下面这个例子,在 fmt.Errorf 中,除了实际报错的信息,还加上了授权错误信息authenticate failed :
    func AuthenticateRequest(r \*Request) error {
            err := authenticate(r.User)
            if err != nil {
                    return fmt.Errorf("authenticate failed: %v", err)
            }
            return nil
    }
    
    

    有多个错误需要处理时,可以考虑将 fmt.Errorf 放入 defer 中:

    func DoSomeThings(val1 int, val2 string) (\_ string, err error) {
        defer func() {
            if err != nil {
                err = fmt.Errorf("in DoSomeThings: %w", err)
            }
        }()
        val3, err := doThing1(val1)
        if err != nil {
            return "", err
        }
        val4, err := doThing2(val2)
        if err != nil {
            return "", err
        }
        return doThing3(val3, val4)
    }
    
    
    • [强制] 利用 recover 捕获 panic 时,需要由 defer 函数直接调用。
      例如,下面例子中的 panic 是可以被捕获的:
    package main
    
    import "fmt"
    
    func printRecover() {
        r := recover()
        fmt.Println("Recovered:", r)
    }
    
    func main() {
        defer printRecover()
    
        panic("OMG!")
    }
    
    

    但是下面这个例子中的 panic 却不能被捕获:

    package main
    
    import "fmt"
    
    func printRecover() {
      r := recover()
      fmt.Println("Recovered:", r)
    }
    
    func main() {
      defer func() {
        printRecover()
      }()
    
      panic("OMG!")
    }
    
    
    • [强制] 不用重复使用 recover,只需要在每一个协程的最上层函数拦截即可。recover 只能够捕获当前协程,而不能跨协程捕获 panic,下例中的 panic 就是无法被捕获的.
    • [强制] 有些特殊的错误是 recover 不住的,例如 Map 的并发读写冲突。这种错误可以通过 race 工具来检查。
  • 可扩展

    • [建议] 利用接口实现扩展性。接口特别适用于访问外部组件的情况,例如访问数据库、访问下游服务。另外,接口可以方便我们进行功能测试。关于接口的最佳实践,需要单独论述。
    • [建议] 使用功能选项模式对一些公共 API 的构造函数进行扩展,大量第三方库例如 gomicro、zap 等都使用了这种策略。
    db.Open(addr, db.DefaultCache, zap.NewNop())
    可以替换为=>
    db.Open(
    addr,
    db.WithCache(false),
    db.WithLogger(log),
    )
    
    

img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

ogger(log),
)

```

[外链图片转存中…(img-0TYxAT0F-1715702725807)]
[外链图片转存中…(img-Kobzz7fQ-1715702725808)]
[外链图片转存中…(img-qobRPDNe-1715702725808)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

  • 14
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值