1、GoLang概述

GoLang

img

gopher是一种生活在加拿大的小动物,go的吉祥物就是这个小动物, 它的中文名叫做囊地鼠,他们最大的特点就是挖洞速度特别快,当然可能不止是挖洞啦。

Go 编程语言是Google公司的一个开源项目,它使程序员更具生产力。Go 语言具有很强的表达能力,它简洁、清晰而高效。得益于其并发机制, 用它编写的程序能够非常有效地利用多核与联网的计算机,其新颖的类型系统则使程序结构变得灵活而模块化。 Go 代码编译成机器码不仅非常迅速,还具有方便的垃圾收集机制和强大的运行时反射机制。 它是一个快速的、静态类型的编译型语言,感觉却像动态类型的解释型语言。(摘取自官网)

Go语言(或 Golang)起源于 2007 年,并在 2009 年正式对外发布。Go 是非常年轻的一门语言,它的主要目标是兼具Python 等动态语言的开发速度和 C/C++ 等编译型语言的性能与安全性。

Go语言是编程语言设计的又一次尝试,是对类C语言的重大改进,它不但能让你访问底层操作系统,还提供了强大的网络编程和并发编程支持。Go语言的用途众多,可以进行网络编程、系统编程、并发编程、分布式编程。

此外,很多重要的开源项目都是使用Go语言开发的,其中包括 Docker、Go-Ethereum、Thrraform 和 Kubernetes。

创始人

思想

Less can be more

核心特性

1、并发编程

Go语言在并发编程方面比绝大多数语言要简洁不少,这一点是其最大亮点之一,也是其在未来进入高并发高性能场景的重要筹码。

不同于传统的多进程或多线程,golang的并发执行单元是一种称为goroutine的协程。

由于在共享数据场景中会用到锁,再加上GC,其并发性能有时不如异步复用IO模型,因此相对于大多数语言来说,golang的并发编程简单比并发性能更具卖点。

在当今这个多核时代,并发编程的意义不言而喻。当然,很多语言都支持多线程、多进程编程,但遗憾的是,实现和控制起来并不是那么令人感觉轻松和愉悦。Golang不同的是,语言级别支持协程goroutine并发(协程又称微线程,比线程更轻量、开销更小,性能更高),操作起来非常简单,语言级别提供关键字go用于启动协程,并且在同一台机器上可以启动成千上万个协程。协程经常被理解为轻量级线程,一个线程可以包含多个协程,共享堆不共享栈。协程间一般由应用程序显式实现调度,上下文切换无需下到内核层,高效不少。协程间一般不做同步通讯,而golang中实现协程间通讯有两种:1、共享内存型,即使用全局变量加mutex锁来实现数据共享;2、消息传递型,即使用一种独有的channel机制进行异步通讯。

对比Java的多线程和GO的协程实现,明显更直接、简单。这就是GO的魅力所在,以简单、高效的方式解决问题,关键字go,或许就是GO语言最重要的标志。

2、内存回收(GC)

从C到C++,从程序性能的角度来考虑,这两种语言允许程序员自己管理内存,包括内存的申请和释放等。因为没有垃圾回收机制所以C/C++运行起来速度很快,但是随着而来的是程序员对内存使用上的很谨小慎微的考虑。因为哪怕一点不小心就可能会导致“内存泄露”使得资源浪费或者“野指针”使得程序崩溃等,尽管C++11后来使用了智能指针的概念,但是程序员仍然需要很小心的使用。后来为了提高程序开发的速度以及程序的健壮性,Java和C#等高级语言引入了GC机制,即程序员不需要再考虑内存的回收等,而是由语言特性提供垃圾回收器来回收内存。但是随之而来的可能是程序运行效率的降低。

GC过程是:先stop the world,扫描所有对象判活,把可回收对象在一段bitmap区中标记下来,接着立即start the world,恢复服务,同时起一个专门gorountine回收内存到空闲list中以备复用,不物理释放。物理释放由专门线程定期来执行。

GC瓶颈在于每次都要扫描所有对象来判活,待收集的对象数目越多,速度越慢。一个经验值是扫描10w个对象需要花费1ms,所以尽量使用对象少的方案,比如我们同时考虑链表、map、slice、数组来进行存储,链表和map每个元素都是一个对象,而slice或数组是一个对象,因此slice或数组有利于GC。

GC性能可能随着版本不断更新会不断优化,这块没仔细调研,团队中有HotSpot开发者,应该会借鉴jvm gc的设计思想,比如分代回收、safepoint等。

3、内存分配

初始化阶段直接分配一块大内存区域,大内存被切分成各个大小等级的块,放入不同的空闲list中,对象分配空间时从空闲list中取出大小合适的内存块。内存回收时,会把不用的内存重放回空闲list。空闲内存会按照一定策略合并,以减少碎片。

4、编译

编译涉及到两个问题:编译速度和依赖管理

目前Golang具有两种编译器,一种是建立在GCC基础上的Gccgo,另外一种是分别针对64位x64和32位x86计算机的一套编译器(6g和8g)。

依赖管理方面,由于golang绝大多数第三方开源库都在github上,在代码的import中加上对应的github路径就可以使用了,库会默认下载到工程的pkg目录下。

另外,编译时会默认检查代码中所有实体的使用情况,凡是没使用到的package或变量,都会编译不通过。这是golang挺严谨的一面。

5、网络编程

由于golang诞生在互联网时代,因此它天生具备了去中心化、分布式等特性,具体表现之一就是提供了丰富便捷的网络编程接口,比如socket用net.Dial(基于tcp/udp,封装了传统的connectlistenaccept等接口)、http用http.Get/Post()、rpc用client.Call(‘class_name.method_name’, args, &reply),等等。

6、函数多返回值

在C,C++中,包括其他的一些高级语言是不支持多个函数返回值的。但是这项功能又确实是需要的,所以在C语言中一般通过将返回值定义成一个结构体,或者通过函数的参数引用的形式进行返回。而在Go语言中,作为一种新型的语言,目标定位为强大的语言当然不能放弃对这一需求的满足,所以支持函数多返回值是必须的。

函数定义时可以在入参后面再加(a,b,c),表示将有3个返回值a、b、c。这个特性在很多语言都有,比如python。

这个语法糖特性是有现实意义的,比如我们经常会要求接口返回一个三元组(errno,errmsg,data),在大多数只允许一个返回值的语言中,我们只能将三元组放入一个map或数组中返回,接收方还要写代码来检查返回值中包含了三元组,如果允许多返回值,则直接在函数定义层面上就做了强制,使代码更简洁安全。

7、语言交互性

在Go语言中直接重用了大部份的C模块,这里称为Cgo。Cgo允许开发者混合编写C语言代码,然后Cgo工具可以将这些混合的C代码提取并生成对于C功能的调用包装代码。开发者基本上可以完全忽略这个Go语言和C语言的边界是如何跨越的。

golang可以和C程序交互,但不能和C++交互。可以有两种替代方案:1、先将c++编译成动态库,再由go调用一段c代码,c代码通过dlfcn库动态调用动态库(记得export LD_LIBRARY_PATH);2、使用swig。

8、异常处理

golang不支持try…catch这样的结构化的异常解决方式,因为觉得会增加代码量,且会被滥用,不管多小的异常都抛出。golang提倡的异常处理方式是:

9、其他特性

适合用来做什么

安装sdk

下载

Windows

官网:https://golang.google.cn/

image-20220321110616643

msi可以直接安装到windows系统,全程下一步完事

MacOS

brew install go

目录结构

目录名 说明
api 每个版本的 api 变更差异
bin go 源码包编译出的编译器(go)、文档工具(godoc)、格式化工具(gofmt)
doc 英文版的 Go 文档
lib 引用的一些库文件
misc 杂项用途的文件,例如Android平台的编译、git 的提交钩子等
pkg Windows 平台编译好的中间文件
src 标准库的源码
test 测试用例

验证安装

执行命令:go version

环境变量

GOROOT

GOROOT也就是golang的安装目录根路径,配置到环境变量就好了

如果要使用go命令,需要将%GOROOT%/bin配置到系统环境变量中

GOPATH

GOPATH 是一个路径,用来存放开发中需要用到的代码包(依赖),注意,这个是一个独立的环境变量,不是配置到PATH里

注意:如果需要在命令行中直接调用GOPATH中install的工具,就需要将$GOPATH/bin配置到环境变量PATH中

GOPROXY

proxy 顾名思义就是代理服务器的意思。Go语言在 1.13 版本之后 GOPROXY 默认值为https://proxy.golang.org,由于国内的网络有防火墙的存在,这导致有些Go语言的第三方包我们无法直接通过go get 命令获取。GOPROXY 是Go语言官方提供的一种通过中间代理商来为用户提供包下载服务的方式。要使用 GOPROXY 只需要设置环境变量 GOPROXY 即可。

目前公开的代理服务器的地址有:

Windows 下设置 GOPROXY 的命令为:

go env -w GOPROXY=https://goproxy.cn

MacOS 或 Linux 下设置 GOPROXY 的命令为:

export GOPROXY=https://goproxy.cn

测试配置

执行命令:go env

image-20240108142657026

Go工程结构

GOPATH模式(弃用)

注意:此项目管理方式为最早的go1.0时所使用,现在不推荐使用,原因是:GOPATH模式下没有版本控制的概念,在执行 go get 的时候,获取的永远是最新的依赖包,下载到GOPATH/src,如果你有两个工程依赖一个包的v1和v2版本,则会发生冲突,因为 GOPATH 模式下两个工程内依赖的导入路径都是一样的,因此两个工程获取的都是v2版本。

前面搭建Go语言开发环境时添加了环境变量 GOPATH,项目的构建主要是靠它来实现的。就是说,如果想要构建一个项目,就需要将这个项目的目录添加到 GOPATH 中,多个项目之间可以使用;分隔。

如果不配置 GOPATH,即使处于同一目录,代码之间也无法通过绝对路径相互调用。

--GOPATH
  --src               //所有源代码都存放到这个文件
      --project1      //目录一般为包名称
      	--xx.go       //源码文件
      	--main.go     //源码文件
      --project2
        --xx.go       //源码文件
  --bin  
  --pkg    			//自动生成,不需要创建

一个Go语言项目的目录一般包含以下三个子目录:

社区推荐结构

由于现在的go项目不需要再放在GOPATH下了,目前社区最推荐的项目结构为:https://github.com/golang-standards/project-layout,虽然它并不是官方和社区的规范,但因为组织方式比较合理,被很多 Go 开发人员接受。

image-20221029164741707

应用开发目录

应用测试目录

应用部署目录

项目管理目录

应用文档目录

建议

不要使用src目录:在默认情况下,Go 语言的项目都会被放置到 $GOPATH/src 目录下(GOPATH模式的时候)。这个目录中存放着所有代码,如果我们在自己的项目中使用 /src 目录,这个包的导入路径中就会出现两个 src

对于小型项目,可以考虑先包含 cmdpkginternal 3 个目录,其他目录后面按需创建。

IDE

Goland

Goland

VsCode

使用需要安装插件

image-20220321134257602

安装后重启vscode,使用ctrl+shift+p,搜索go:install/update tools,全选进行安装

image-20220321134607386

安装完成后,可以看到bin目录下出现这些可执行工具

image-20220321134648623

HelloWorld

创建工程

mkdir helloworld
cd helloworld
go mod init top.ygang/helloworld

main.go

//这是一个main包
//这是main包的包注释
package main    // 声明 main 包

import (
    "fmt"       // 导入 fmt 包,打印字符串是需要用到
)

func main() {   // 声明 main 主函数
    fmt.Println("Hello World!") // 打印 Hello World!
}

package

Go语言以“包”作为管理单位,每个 Go 源文件必须先声明它所属的包,格式如下:

package name

package 是声明包名的关键字,name 为包的名字

import

在包声明之后,是 import 语句,用于导入程序中所依赖的包,导入的包名使用双引号""包围,如果一个import导入多个包,需要用()包围,并且每个包名占一行,格式如下:

import "name"

import(
    "name1"
    "name2"
)

注意,导入的包中不能含有代码中没有使用到的包,否则Go编译器会报编译错误,例如 imported and not used: "xxx"

main 函数

main 函数,它是Go语言程序的入口函数,也即程序启动后运行的第一个函数。main 函数只能声明在 main 包中,不能声明在其他包中,并且,一个 main 包中也必须有且仅有一个 main 函数

func 函数名 (参数列表) (返回值列表){
    函数体
}

注意:Go语言函数的左大括号{必须和函数名称在同一行,否则会报错。

运行和编译

Go语言是编译型的静态语言(和C语言一样),所以在运行Go语言程序之前,先要将其编译成二进制的可执行文件。

可以通过Go语言提供的go build或者go run命令对Go语言程序进行编译:

语法

行分隔符

在 Go 程序中,一行代表一个语句结束。每个语句不需要像 C 家族中的其它语言一样以分号;结尾,因为这些工作都将由 Go 编译器自动完成。

如果你打算将多个语句写在同一行,它们则必须使用;人为区分,但在实际开发中并不鼓励这种做法。

fmt.Println("Hello, World!")
fmt.Println("你好")

注释

注释不会被编译,每一个包应该有相关注释。

单行注释是最常见的注释形式,可以在任何地方使用以//开头的单行注释;多行注释也叫块注释,均已以/*开头,并以 */ 结尾。

// 单行注释

/*
	多行
	注释
*/

一般情况下,对于方法、变量、包的注释直接加在对应元素上方就可以。

// this is my package
package mypackage

// this is my int
var myint int

// this is my func
func myfunc(){}

对于包中有很多源代码文件,后续可能会忘记包注释写在哪一个源代码文件中所以一般在对应包的根目录中创建一个文件doc.go,这个文件只声明package name,并且添加包注释,而其他源代码文件不再添加包注释。

go doc与godoc

go doc是Go自带的命令,可以通过go doc packageName查看包的说明

godoc需要额外下载并安装

go get -u golang.org/x/tools/cmd/godoc
go install golang.org/x/tools/cmd/godoc@latest

可以使用godoc -http=:6060查看GO API文档,如果在项目目录中使用,还包含了项目的API文档

标识符

标识符用来命名变量、类型等程序实体。一个标识符实际上就是一个或是多个字母(A-Z和a-z)数字(0-9)、下划线_组成的序列,但是首个字符不能是数字,标识符不可以使用关键字或保留字

Go 代码中会使用到的 25 个关键字或保留字

break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var

除了以上关键字,Go 语言还有 36 个预定义标识符

append bool byte cap close complex complex64 complex128 uint16
copy false float32 float64 imag int int8 int16 uint32
int32 int64 iota len make new nil panic uint64
print println real recover string true uint uint8 uintptr

API文档地址

中文文档:https://studygolang.com/pkgdoc