#76 Go 语言有什么优势
Golang 2022-04-02- 语法简单
- 确实没有很多语言特性,容易上手,但说什么大道至简,Less is more 等价值观就有点扯了。
- 静态类型、强类型、编译型语言
- 性能
- 并发:自带的协程实现(
goroutine+channel)在开发效率和性能之间达成了一个不错的平衡,非常优秀 - 跨平台
- 接口,反射,GC
- C 嵌入
- 便于工程化:工具链齐全,代码规范严格
- 没有历史负担:但是向前兼容的承诺也是
- Go1 兼容性承诺(最重要特性之一):但是也令人担忧,时间一长,可能就有太重的包袱
- 基于消息传递的通信机制
- 核心开发团队名气非常大
- robert griesemer
- rob pike
- ken thompson
- russ cox
- 生态:有大公司站台,有杀手级项目(Docker, K8S 等)
存在的问题
- 异常处理的设计
- 标准库相对太薄弱
- 生态:缺乏主流框架
- 包管理机制上存在的问题
- 没有泛型(1.18 以前)
场景
- 云原生
- 基础设施项目
- Web 开发
- 网络编程
#75 转载:一道正确率只有15%的命名返回值和闭包的问题
Golang 2022-03-01哈喽,大家好,我是asong。今天新注册了twitter,在里面没事瞎逛的时候,发现了一道有意思的题,他是由Redhat的首席工程师、Prometheus开源项目维护者 Bartłomiej Płotka 发出的,经调查显示,这道题的正确率只有15.2%,惨目忍睹,接下来我们就一起来看一下这道题~
原文地址:https://twitter.com/bwplotka/status/1495002204163678211
题目:下面这段代码输出结果是多少?
func aaa() (done func(), err error) {
return func() { print("aaa: done") }, nil
}
func bbb() (done func(), _ error) {
done, err := aaa()
return func() { print("bbb: surprise!"); done() }, err
}
func main() {
done, _ := bbb()
done()
}
- A.
aaa: done - B.
bbb: surprise!aaa: done - C.
永远不会结束 - D.
编译错误
解析
答案:C 永远不会结束
这道题考查的点就是命名返回值 + 闭包,把上面的代码换成等效的匿名返回值代码你就明白了:
func aaa() (func(), error) {
var done func()
done = func() {
print("aaa: done")
}
return done, nil
}
func bbb() (func(), error) {
var done func()
done, err := aaa()
done = func() {
print("bbb: surprise!");
done()
}
return done, err
}
func main() {
done, _ := bbb()
done()
}
这其实是 Go 语言设计上一个 feature,当 Go 语言的返回值赋给我们特殊的"返回参数"时,如果它们被命名了,在 return 之后,我们可以在函数主体完成后的任何执行过程中引用那些带有这些名称的值,在 defer 或闭包中一样。
我们在说回这道题,在 bbb() 函数内我们使用了命名返回值 done func(), _ error,使用短变量声明 done, err := aaa() 接收 aaa() 的返回值,这里变量 done 并不是一个新变量,这就要说到Go语言的短变量声明的语法糖了,在多变量声明中,如果其中一个变量是新的,可以使用 := 声明,编译器会进行类型推断和赋值,已经声明的变量不会重新声明,直接在原变量上赋值;之后我们return的是一个闭包函数,闭包里的done值并不会被提前解析,在bbb()函数结束后,实际对应的代码就成了这样,变成了递归。
done = func() {
print("bbb: surprise!");
done()
}
如果我们把代码在改成这样:
func bbb() (func(), error) {
var done func()
done, err := aaa()
return func() {
print("bbb: surprise!");
done()
}, err
}
答案就是【B】:bbb: surprise!aaa: done
总结
一道看似简单的题,其中蕴涵的知识点确有很多,这就说明了解设计原理是多么的重要,Go语言资深工程师的路上任重道远呀~。
#74 Go 枚举
Golang 2022-02-07编程语言中一般都有枚举类型。可以用来替换代码中的那些有一定范围的常量,减少幻数的使用,提升代码可读性。
有些语言的枚举支持遍历等操作,有一些语言的枚举还支持枚举值和枚举名字的映射。
Go 没有在语言层面实现枚举,只能通过定义一组变量。
比如(src/runtime/time.go):
const (
timerNoStatus = iota
timerWaiting
timerRunning
timerDeleted
timerRemoving
timerRemoved
timerModifying
timerModifiedEarlier
timerModifiedLater
timerMoving
)
官方代码(src/time/time.go)中有些地方会给这些变量加一个自定义类型:
// A Weekday specifies a day of the week (Sunday = 0, ...).
type Weekday int
const (
Sunday Weekday = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
)
参考资料与拓展阅读
#73 Go HTTP 客户端
HTTP Golang 2022-01-29原生
之前的文章:Golang HTTP 以及 HTML/XML 解析
package main
import (
"fmt"
"io/ioutil"
"net/http"
)
func main() {
resp, err := http.Get("https://www.baidu.com")
if err != nil {
fmt.Println(err)
return
}
defer resp.Body.Close()
fmt.Printf("%#v\n", resp.Status) // string, "200 OK"
fmt.Printf("%#v\n", resp.StatusCode) // int, 200
fmt.Printf("%#v\n", resp.Header) // http.Header, map[string][]string
fmt.Printf("%#v\n", resp.Request) // *http.Request
fmt.Printf("%#v\n", resp.ContentLength) // int64
fmt.Printf("%#v\n", resp.TransferEncoding) // []string(nil)
fmt.Printf("%#v\n", resp.Trailer) // http.Header(nil)
fmt.Printf("%#v\n", resp.Uncompressed) // bool
fmt.Printf("%#v\n", resp.TLS) // *tls.ConnectionState
fmt.Printf("%#v\n", resp.Body) // *http.bodyEOFSignal => io.ReadCloser => io.Reader
body, err := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
}
multipart
admin@victus:~$ cd /C/Program\ Files/Go/src/mime/multipart
nosch@victus:/C/Program Files/Go/src/mime/multipart$ grep -ER 'func.+\) [A-Z]\w+' .
./formdata.go:func (r *Reader) ReadForm(maxMemory int64) (*Form, error) {
./formdata.go:func (f *Form) RemoveAll() error {
./formdata.go:func (fh *FileHeader) Open() (File, error) {
./formdata.go:func (rc sectionReadCloser) Close() error {
./formdata_test.go:func testFile(t *testing.T, fh *FileHeader, efn, econtent string) File {
./formdata_test.go:func (r *failOnReadAfterErrorReader) Read(p []byte) (n int, err error) {
./multipart.go:func (p *Part) FormName() string {
./multipart.go:func (p *Part) FileName() string {
./multipart.go:func (r *stickyErrorReader) Read(p []byte) (n int, _ error) {
./multipart.go:func (p *Part) Read(d []byte) (n int, err error) {
./multipart.go:func (pr partReader) Read(d []byte) (int, error) {
./multipart.go:func (p *Part) Close() error {
./multipart.go:func (r *Reader) NextPart() (*Part, error) {
./multipart.go:func (r *Reader) NextRawPart() (*Part, error) {
./multipart_test.go:func (mr *maliciousReader) Read(b []byte) (n int, err error) {
./multipart_test.go:func (s *slowReader) Read(p []byte) (int, error) {
./multipart_test.go:func (s *sentinelReader) Read([]byte) (int, error) {
./writer.go:func (w *Writer) Boundary() string {
./writer.go:func (w *Writer) SetBoundary(boundary string) error {
./writer.go:func (w *Writer) FormDataContentType() string {
./writer.go:func (w *Writer) CreatePart(header textproto.MIMEHeader) (io.Writer, error) {
./writer.go:func (w *Writer) CreateFormFile(fieldname, filename string) (io.Writer, error) {
./writer.go:func (w *Writer) CreateFormField(fieldname string) (io.Writer, error) {
./writer.go:func (w *Writer) WriteField(fieldname, value string) error {
./writer.go:func (w *Writer) Close() error {
./writer.go:func (p *part) Write(d []byte) (n int, err error) {
第三方库
GitHub: http client stars:>1000
- go-resty/resty
Simple HTTP and REST client library for Go - parnurzeal/gorequest
GoRequest -- Simplified HTTP client ( inspired by nodejs SuperAgent ) - gojek/heimdall
An enhanced HTTP client for Go - imroc/req
Simplified Golang HTTP client library with Black Magic, Less Code and More Efficiency - dghubble/sling
A Go HTTP client library for creating and sending API requests - hashicorp/go-retryablehttp
Retryable HTTP client in Go
简单的了解:
- resty 看起来确实不错,链式调用,清晰明了,而且有不错的调试信息。
- gorequest 是在原生库上做了一点简单的封装,优化调用体验。有篇中文文档可以参考:gorequest中文文档(非官方)
需要学习一下他的设计。官方文档说是借鉴 Node.js 的 SuperAgent。 - sling 也挺有特色的,使 API 变得结构化,调用变得像普通的 Go 函数一样。
- go-retryablehttp 在原生库上加了一个自动重试机制。
- heimdall, req, 简单一看,还看不出来有什么特别的地方。
#72 Golang 观察
Golang 2022-01-18InfoQ 的公众号文章《解读Go语言的2021:稳定为王》中引用 TIOBE Index 和 Google Trends 的数据来分析 Go 语言的发展趋势。
非常有意思,我找他的思路,直接去源头研究了一番。
Go 在 2009 年和 2016 年得了 TIOBE 年度语言。
PS: Go 是 2009 年 11 月发布。TIOBE 也是够捧场了,刚一出来就得了年度语言。

根据 TIOBE 的数据,Go 在 2016 年下半年大火了一波,随后热度消退,从 2018 年到现在一直比较稳定。

然后 Google Trends 的数据(2009/10/01 至今,全球)显示:
- Go 在 2009 年诞生之后就慢慢攀升
- 2012 年达到一个阶段
- 2013 年 10 月又上一个台阶
- 2014 年 5 月达到巅峰
- 然后一直持续下跌到 2020 年底
- 2021 年稍有复苏的迹象
最后再上这张图,看看 Go 在各个地区的火爆程度:

那篇文章,包括我之前在网上看到的其他文章,都在这个地方得出了一个结论:Go 在国内的热度远超国外,甚至有地方说 Go 语言的热度全靠国内开发者支撑。
我认为这是非常不严谨的。
其实 Google Trends 的数据是根据这个地方搜索相关词条的比例来计算的。而 Google 的中国用户已经被过滤过一道,应该是以开发者为主。所以这个热度数据的对比是不能说明什么问题的。而且由于一些上网方式的问题,国内的这部分统计数据应该是非常不可靠的。
#71
Go: rewrite interface{} to any
Golang GoGenerics
2021-12-12
Golang GitHub 仓库上的 Issue 49884 all: rewrite interface{} to any 值得吃瓜。
由于新的泛型机制中引入了 any 关键字, 有人提议将代码中的所有 interface{} 替换为 any。
相关连接:all: gofmt -w -r 'interface{} -> any' src
目前这个 Issue 还是 Open 状态。
#70 Go LDAP
Golang LDAP 2021-11-26使用 go-ldap/ldap 实现 LDAP 的基本操作,包括查询、添加、修改、删除等。
#69 Micro (go-micro)
Golang go-micro 微服务 云原生 2021-11-21Micro 上次作妖的时候,我专门了解过,但是时间有点久有点忘了,这里把我记得的整理一下做个记录。
#68 Go 常用库 (持续更新)
Golang 2021-11-21- HTTP
- HttpServer
- HttpClient
- EmailFormat RFC822
- SMTP
- SmtpServer
- SmtpClient
- jordan-wright/email
- Cache
- Memcache
- Redis
- DB
- MySQL
- MongoDB
- PostgreSQL
- SQLite
- ORM
- MQ
- streadway/amqp
- JSON
- json-iterator
- XML
- beevik/etree
- YAML
- go-yaml/yaml
- 配置
- viper
- configor
- Gzip
- 日志
- Logrus