Golang
2022-04-10
2007 Robert Griesemer、Rob Pike 和 Ken Thompson 三位巨头都在谷歌任职,开始设计一门全新的语言。
| Release |
Status |
Release date |
Maintenance end |
| go1 |
End-of-Life |
2012-03-28 |
2013-12-01 |
| go1.1 |
End-of-Life |
2013-05-13 |
2014-06-18 |
| go1.2 |
End-of-Life |
2013-12-01 |
2014-12-10 |
| go1.3 |
End-of-Life |
2014-06-18 |
2015-08-19 |
| go1.4 |
End-of-Life |
2014-12-10 |
2016-02-17 |
| go1.5 |
End-of-Life |
2015-08-19 |
2016-08-15 |
| go1.6 |
End-of-Life |
2016-02-17 |
2017-02-16 |
| go1.7 |
End-of-Life |
2016-08-15 |
2017-08-24 |
| go1.8 |
End-of-Life |
2017-02-16 |
2018-02-16 |
| go1.9 |
End-of-Life |
2017-08-24 |
2018-08-24 |
| go1.10 |
End-of-Life |
2018-02-16 |
2019-02-25 |
| go1.11 |
End-of-Life |
2018-08-24 |
2019-09-03 |
| go1.12 |
End-of-Life |
2019-02-25 |
2020-02-25 |
| go1.13 |
End-of-Life |
2019-09-03 |
2020-08-11 |
| go1.14 |
End-of-Life |
2020-02-25 |
2021-02-16 |
| go1.15 |
End-of-Life |
2020-08-11 |
2021-08-16 |
| go1.16 |
End-of-Life |
2021-02-16 |
2022-03-15 |
| go1.17 |
End-of-Life |
2021-08-16 |
2022-08-02 |
| go1.18 |
End-of-Life |
2022-03-15 |
2023-02-01 |
| go1.19 |
Maintenance |
2022-08-02 |
Q3 2023 |
| go1.20 |
Current |
2023-02-01 |
Q1 2024 |
| go1.21 |
Planned |
Q3 2023 |
Q3 2024 |
1.16 及以前的版本
我开始学习 Golang 的时候,已经是 1.5 和 1.6 版本了,更早的版本只需要对重大特性引入时间做一个简单了解。
现在主要的开发版本已经升级到 1.18 和 1.20。
- 1.0
- 第一个正式版本,核心特性都已经包含在里面,承诺以后语法不会发生不兼容变化
- 至今为止,承诺都实现了。后面的版本几乎很少有激动人心的功能,绝大多数时候都是各种改进
- 1.1
- 1.2
- 支持
slice[low:high:max] 语法
- 1.4
for range 语法引入
- 1.5
- 实现自举
- 1.9
type alias 语法引入
- 1.11
- Go Modules 引入
- 1.12
- 开始支持 TLS 1.3
- 需要通过
GODEBUG=tls13=1 开启(不能通过 tls Config MaxVersion 限制版本)
- 0-RTT 不被支持(到 2023 年,依然没有提供支持)
- 1.13
- TLS 1.3 默认开启
- 1.14
- TLS 1.3 成为默认选项,并且无法通过 GODEBUG 关闭
- 异步可抢占 goroutine
- 1.16
- Go Modules 成为默认(
GO111MODULE = on)
- 支持将静态文件打包进可执行文件(
//go:embed)
- 弃用 io/ioutil
参考:
1.17
- Module graph pruning(依赖图修剪)
- 切片转数组指针
参考:
1.18
- 泛型(Golang 发布以来最大的语法变更)
- fuzzing(模糊测试)
- TLS 客户端默认版本改为 1.2
参考:
1.19
没有特别值得关注的的改动。
参考:
1.20
参考:
1.21
min, max 和 clear
- 循环变量捕获 (loop variable capture)
参见:2023/07/05,Go 1.21 for 语义变更
- 标准库:
log/slog, slices, maps, cmp
参考:
WebDev Golang Gin Vue ElementUI
2022-04-03
:) 本文正在编辑中,暂时不提供浏览...
Golang
2022-04-02
- 语法简单
- 确实没有很多语言特性,容易上手,但说什么大道至简,Less is more 等价值观就有点扯了。
- 静态类型、强类型、编译型语言
- 性能
- 并发:自带的协程实现(
goroutine + channel)在开发效率和性能之间达成了一个不错的平衡,非常优秀
- 跨平台
- 接口,反射,GC
- C 嵌入
- 便于工程化:工具链齐全,代码规范严格
- 没有历史负担:但是向前兼容的承诺也是
- Go1 兼容性承诺(最重要特性之一):但是也令人担忧,时间一长,可能就有太重的包袱
- 基于消息传递的通信机制
- 核心开发团队名气非常大
- robert griesemer
- rob pike
- ken thompson
- russ cox
- 生态:有大公司站台,有杀手级项目(Docker, K8S 等)
存在的问题
- 异常处理的设计
- 标准库相对太薄弱
- 生态:缺乏主流框架
- 包管理机制上存在的问题
- 没有泛型(1.18 以前)
场景
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语言资深工程师的路上任重道远呀~。
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
)
参考资料与拓展阅读
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, 简单一看,还看不出来有什么特别的地方。
Golang
2022-01-18
InfoQ 的公众号文章《解读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 的中国用户已经被过滤过一道,应该是以开发者为主。所以这个热度数据的对比是不能说明什么问题的。而且由于一些上网方式的问题,国内的这部分统计数据应该是非常不可靠的。
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 状态。
Golang LDAP
2021-11-26
使用 go-ldap/ldap 实现 LDAP 的基本操作,包括查询、添加、修改、删除等。
Golang go-micro 微服务 云原生
2021-11-21
Micro 上次作妖的时候,我专门了解过,但是时间有点久有点忘了,这里把我记得的整理一下做个记录。