golang1.16也在今天正式发布了。
原定计划是2月1号年前发布的,不过迟到也是golang的老传统了,正好也趁着最后的假期快速预览一下golang1.16的新特性吧。
语言內建的资源嵌入支持
embed
我之前以及写了embed的使用教程,可以看这里。
这儿还有一篇官方推荐的教程。
支持arm64
m1芯片可以说是近日的焦点,golang自然也不会落下。
darwin/arm64
现在可以在新版mac上尝试golang了。
不过plugin模式的支持仍在进行中,想要完整支持arm64还需要一段时间。
go modules的新特性
本次更新依旧带过来了许多modules的新特性。
GO111MODULE现在默认为on
1.16开始默认启用modules,这在1.15的时刻已经预告过了。现在GO111MODULE的默认值为on。
不过golang还是提供了一个版本的适应期,假如你还不习惯modules,可以把GO111MODULE设置回auto。在1.17中这个环境变量将会被删除。
都1202年了,也该学学go modules如何用了。
go build不再更改mod相关文件
以前的教程里我提到过go build会自动下载依赖,这会更新mod文件。
现在这一行为被禁止了。想要安装、更新依赖只能使用go get命令,go build和go test将不会再做这类工作。
go install的变化
go install在1.16中也有了不小的变化。
首先是通过go install my.module/tool@1.0.0 这样在module末尾加上版本号,可以在不影响与作用与影响当前mod的依赖的情况下安装golang流程。
-d
总之go的命令各司其职,不再长臂管辖了。
新的GOVCS环境变量
新的GOVCS环境变量指定了golang用什么版本控制工具下载源代码。
GOVCS=:,[:, ...]
其中module prefix为github.com等,而tool name就是版本控制工具的名字,打比方说git,svn。
GOVCS=github.com:git,evil.com:off,*:git|hg
*
tool name还不错设置为all和off,all代表允许使用任何可用的工具,而off则预示不允许使用任何版本控制工具。
不过现在设置为off的模块的代码仍然可能会被下载。
go help vcs
相对路径导入不在被允许
.
import ( "./tools/factory" "../models/user" "some.pkg.com/杀马特/音乐工厂" )
对非ASCII字符一如既往的不友好,不过也只能按规矩办事了。
标准库的变化
golang1.16除了对标准库进行通常的功能更新和修复,还引入了一些重大变化。
testing
os.Exit(0)
打比方说这个:
package main import ( "os" "testing" ) func TestXXX(t *testing.T) { t.Log("exit") os.Exit(0) }
现在会是这样的输出:
$ go test -v a_test.go === RUN TestXXX a_test.go:9: exit --- FAIL: TestXXX (0.00s) panic: unexpected call to os.Exit(0) during test [recovered] panic: unexpected call to os.Exit(0) during test goroutine 18 [running]: testing.tRunner.func1.2(0x51b920, 0x56cc28) /usr/local/go/src/testing/testing.go:1144 +0x332 testing.tRunner.func1(0xc000082600) /usr/local/go/src/testing/testing.go:1147 +0x4b6 panic(0x51b920, 0x56cc28) /usr/local/go/src/runtime/panic.go:965 +0x1b9 os.Exit(0x0) /usr/local/go/src/os/proc.go:68 +0x6d command-line-arguments.TestXXX(0xc000082600) /tmp/a_test.go:10 +0x76 testing.tRunner(0xc000082600, 0x54df18) /usr/local/go/src/testing/testing.go:1194 +0xef created by testing.(*T).Run /usr/local/go/src/testing/testing.go:1239 +0x2b3 FAIL command-line-arguments 0.004s FAIL
ioutils包已经废弃
io/ioutil
ioutil旧函数 | 新函数 |
---|---|
Discard | io.Discard |
NopCloser | io.NopCloser |
ReadAll | io.ReadAll |
ReadDir | os.ReadDir |
ReadFile | os.ReadFile |
WriteFile | os.WriteFile |
TempDir | os.MkdirTemp |
TempFile | os.CreateTemp |
现在开始可以做移植了。
tcp半连接队列扩容
在Linux kernel 4.1以前,golang设置tcp的listen队列的长度是从/proc/sys/net/core/somaxconn获取的,通常为4096。
2^32 - 1
更大的半连接队列象征着可以同时处理更加的多的新加入请求,而且不用再读取配置文件性能也会略微提升。
重大更新io/fs
1.16除了支持嵌入静态资源外,最大的变化就是引入了io/fs包。
golang认为文件的io操作是依赖于文件系统(filesystem,fs)的,所以决定模仿Linux的vfs做一套基于fs的io接口。
这样做的目的有三个:
所以io/fs诞生了。
fs包中主要包含了下面几种数据类型(都是接口类型):
名称 | 影响与作用 |
---|---|
FS | 文件系统的抽象,有一个Open方法用来从FS打开获取文件数据 |
DirEntry | 描述目录项目(包含目录自身)的数据结构 |
File | 描述文件数据的结构,包含Stat,Read,Close方法 |
ReadDirFile | 在File的基础上支持ReadDir,可以代表目录自身 |
FileMode | 描述文件类型,打比方说是通常文件还是套接字或者是管道 |
FileInfo | 文件的元数据,例如创建时间等 |
其中有一些接口和os包中的同名,事实上是os包引入fs包后起的别名。
对于FS,还有以下的扩展,以便增量描述文件系统允许的操作:
名称 | 影响与作用 |
---|---|
GlobFS | 增添Glob方法,可以用通配符查找文件 |
ReadDirFS | 增添ReadDir方法,可以遍历目录 |
ReadFileFS | 增添ReadFile方法,可以用文件名读取文件所有内容 |
StatFS | 增添Stat方法,可以获得文件/目录的元信息 |
SubFS | 增添Sub方法,Sub方法接受一个文件/目录的名字,从这个名字作为根目录返回一个新的文件系统对象 |
fs包还提供了诸如Glob,WalkDir等传统的文件操作接口。
embed
由于只是速览的原因,无法详尽介绍io/fs包,你可以参考golang的文档或这篇文章做进一步了解。
其他改进
其他的改进包括Unicode更新到了13.0、新增添了runtime/metrics包已提供更好更规范的运行时信息等。
同时1.16优化了链接器,现在它在linux/amd64上比1.15快了20-25%,内存占用减少了5-15%。
在Windows上已经全面支持了地址空间布阵与布局随机化(ASLR),此前不支持将golang编译为dll时启用ASLR。
本次更新中语言本身没有什么变化。
更加的多信息可以查看golang1.16 release notes
在运维开发的过程中,经常遇见这种情况:启动某个应用前,需要先检测一下端口是否被其他的运用占用了,若占用了,能否得到占用进程的PID。后续可以依据这个PID,查找是哪个应用占据这端口,然后KILL掉。非常简单容易的需求,思路是:
利用 netstat 命令获取当前的相关端口号的PID,然后正则表达式过滤出相关的PID即可
netstat -ano | findstr 8099
输出如下:
以windows 系统下为例,linux系统下,修改相关参数即可。代码如下:
// 传入查询的端口号 // 返回端口号对应的进程PID,若没有找到相关进程,返回-1 func portInUse(portNumber int) int { res := -1 var outBytes bytes.Buffer cmdStr := fmt.Sprintf("netstat -ano -p tcp | findstr %d", portNumber) cmd := exec.Command("cmd", "/c", cmdStr) cmd.Stdout = &outBytes cmd.Run() resStr := outBytes.String() r := regexp.MustCompile(`\s\d+\s`).FindAllString(resStr, -1) if len(r) > 0 { pid, err := strconv.Atoi(strings.TrimSpace(r[0])) if err != nil { res = -1 } else { res = pid } } return res }
补:golang 实现端口扫描
对端口进行扫描基于连接,在go中我们可以使用net.Dial进行判断,假如返回的结果没有错误,那么这个端口就应该是打开的,假如返回了错误,那么可以说明这个端口是关闭的,代码如下
func ScanPort(protocol string, hostname string, port int) bool { fmt.Printf("scanning port %d \n", port) p := strconv.Itoa(port) addr := net.JoinHostPort(hostname, p) conn, err := net.DialTimeout(protocol, addr, 3*time.Second) if err != nil { return false } defer conn.Close() return true }