Go语言及快速入门
最近没啥时间,只能找点简单的新东西看看,这次简单总结一下这两年火热的新语言Go。
其实早在18年,就开始对Go了有关注,却没太上心,只是简单的学了下语法,写简单的Demo,因为那时的Go真谈不上有什么好的。连基本的包管理都没做好。
而现在嘛,Go粉,Go吹是真的不少。主要原因无非Java人才太多了,导致Go的优点能吹,Go的缺点修饰一下也能吹。
而我对Go的关注,无非就是Docker、K8s,以及非常多的基础服务,比如数据存储、网络工具都Go写。不说其他项目,单Docker项目,就足够优秀、亮眼,你就会好奇写出这么优秀的项目的语言是不是也如此优秀。
放一张云生态全景图:
然而,我不是Go吹,只会根据我仅有的知识体系,谈谈我的主观感受。
相对于Java,Go有非常多的反人类设计:
异常、
泛型(改进,新版本支持)、
包管理(现在加强了很多)、
不支持重载(不知道作者怎么想的,这个让人很头疼);
优点也很明显:
直接打包成可执行文件(作用并没那么大)
云原生(并非非Go不行)、
cgo调用底层c语言库(网上说要踩很多坑,不如python集成好)
tag标签(类似Java注解)、
函数作为一等公民、
支持多返回值、
Goroutine的轻量级;
谈不上好坏的一些争议的点:
部分语法习惯比如命名和类型非要倒置(哗众取宠)、
比如方法这种显示写接收者、
比如有人喜欢结构体有人喜欢类。
知识需要凝结成块,零散的知识需要系统的总结,才能慢慢变成有用。
01
Go语言快速入门
1.基础
优秀入门书:https://books.studygolang.com/gopl-zh/
基础语法笔记:http://www.topgoer.com
(详细讲解-略)
Go工具集
build 、doc 、env、fmt、get、install、list 、run、test 、version、vet
2.内置库学习
库文档:https://studygolang.com/pkgdoc
1.fmt
fmt包实现了类似C语言printf和scanf的格式化I/O。主要分为向外输出内容和获取输入内容两大部分。
常用输入函数:Scan、Scanf、Scanln,可以在程序运行过程中从标准输入获取用户的输入。
常用输出函数
Print、Printf、Println:直接输出内容
Sprint、Sprintf、Sprintln:生成内容并返回字符串
Fprint:将内容输出到一个io.Writer接口类型的变量,经常用于写入文件
Errorf:根据format参数生成格式化字符串并返回一个包含该字符串的错误
占位符:
// 通用verbs
%v 值的默认格式
%+v 类似%v,但输出结构体时会添加字段名
%
%T Go语法表示类型
%% 百分号表示
// 浮点数
%t true或false
// 整数
%b 表示二进制
%c 该值对应的unicode吗值
%d 表示十进制
%o 表示八进制
%q 该值对应的单引号括起来的go语法字符字面值,必要时会采用安全的转义表示
%x 表示为十六进制,使用a-f
%X 表示为十六进制,使用A-F
%U 表示为Unicode格式:U+1234,等价于”U+%04X”
// 浮点数与复数
%b 无小数部分、二进制指数的科学计数法,如-123456p-78;参见strconv.FormatFloat
%e 科学计数法,例如 -1234.456e+78
%E 科学计数法,例如 -1234.456E+78
%f 有小数点而无指数,例如 123.456
%F 等价于%f
%g 根据实际情况采用%e或%f格式(以获得更简洁、准确的输出)
%G 根据实际情况采用%E或%F格式(以获得更简洁、准确的输出
// string与[]byte
%s 输出字符串表示(string类型或[]byte)
%q 双引号围绕的字符串,由Go语法安全地转义
%x 十六进制,小写字母,每字节两个字符 (使用a-f)
%X 十六进制,大写字母,每字节两个字符 (使用A-F)
// Slice
%p 切片第一个元素的指针
// point
%p 十六进制内存地址,前缀ox
2.Time
时间和日期
now := time.Now() //获取当前时间
year := now.Year() //年
month := now.Month() //月
day := now.Day() //日
hour := now.Hour() //小时
minute := now.Minute() //分钟
second := now.Second() //秒
timestamp1 := now.Unix() //时间戳
timestamp2 := now.UnixNano() //纳秒时间戳
ticker := time.Tick(time.Second) //定义一个1秒间隔的定时器
3.Flag
Go语言内置的flag包实现了命令行参数的解析,flag包使得开发命令行工具更为简单。
4.Log
Go语言内置的log包实现了简单的日志服务。
5.IO操作
os包提供了Create、NewFile、Open、OpenFile、Remove方法
返回的文件对象,提供了读写方法,比如Write、WriteAt、WriteString、Read、ReadAt方法
bufio包实现了带缓冲区的读写,是对文件读写的封装
6.Strconv
strconv包实现了基本数据类型与其字符串表示的转换,主要有以下常用函数:Atoi()、Itia()、parse系列、format系列、append系列。
7.Template
html/template包实现了数据驱动的模板,用于生成可对抗代码注入的安全HTML输出。它提供了和text/template包相同的接口,Go语言中输出HTML的场景都应使用text/template包。
8.Http(重点)
Go语言内置的net/http包十分的优秀,提供了HTTP客户端和服务端的实现。
9.Context
Go http包的Server中,每一个请求在都有一个对应的 goroutine 去处理。请求处理函数通常会启动额外的 goroutine 用来访问后端服务,比如数据库和RPC服务。用来处理一个请求的 goroutine 通常需要访问一些与请求特定的数据,比如终端用户的身份认证信息、验证相关的token、请求的截止时间。当一个请求被取消或超时时,所有用来处理该请求的 goroutine 都应该迅速退出,然后系统才能释放这些 goroutine 占用的资源。
10.jaon
json可以和map、struct相互转换
11.reflect
反射可以获取interface类型信息、获取值信息、修改值信息
反射可以查看结构体字段、类型、方法,修改结构体的值,调用方法
空接口 可以表示任何参数,利用反射判断参数类型
获取类型信息:reflect.TypeOf,是静态的
获取值信息:reflect.ValueOf,是动态的
3.项目目录结构
一个好的目录规范才能让代码组织清晰,明白通用目录规范也才能看得懂别人的代码
目录
/cmd
该项目的主要应用。
每个应用程序的目录名称应与您想要的可执行文件的名称相匹配(例如/cmd/myapp)。
如果您认为代码可以导入并在其他项目中使用,那么它应该存在于/pkg目录中。如果代码不可重用或者您不希望其他人重用它,请将该代码放在/internal目录中。
/internal
私有应用程序和库代码。这是您不希望其他人在其应用程序或库中导入的代码。
将您的实际应用程序代码放在/internal/app目录(例如/internal/app/myapp)和/internal/pkg目录中这些应用程序共享的代码(例如/internal/pkg/myprivlib)。
/pkg
可以由外部应用程序使用的库代码(例如/pkg/mypubliclib)。其他项目将导入这些库,期望它们可以工作,所以在你把东西放在这里之前要三思而后行:-)
/vendor
应用程序依赖项(手动管理或由您喜欢的依赖管理工具管理dep)。
tip:如果要构建库,请不要提交应用程序依赖项。
服务应用程序目录
/api
OpenAPI / Swagger规范,JSON模式文件,协议定义文件。请参阅/api目录以获取示例。
Web应用程序目录
/web
特定于Web应用程序的组件:静态Web资产,服务器端模板和SPA。
常见应用程序目录
/configs
配置文件模板或默认配置。
将您的confd或consul-template模板文件放在这里。
/init
系统初始化(systemd,upstart,sysv)和进程管理器/主管(runit,supervisord)配置。
/scripts
脚本执行各种构建,安装,分析等操作。
这些脚本使根级Makefile保持简洁(例如https://github.com/hashicorp/terraform/blob/master/Makefile)。请参阅/scripts目录以获取示例。
/build
包装和持续集成。
将您的云(AMI),容器(Docker),OS(deb,rpm,pkg)包配置和脚本放在/build/package目录中。
将CI(travis,circle,drone)配置和脚本放在/build/ci目录中。请注意,某些CI工具(例如,Travis CI)对其配置文件的位置非常挑剔。尝试将配置文件放在/build/ci将它们链接到CI工具所期望的位置的目录中(如果可能)。
/deployments
IaaS,PaaS,系统和容器编排部署配置和模板(docker-compose,kubernetes / helm,mesos,terraform,bosh)。
/test
其他外部测试应用和测试数据。您可以随意构建/test目录。对于更大的项目,有一个数据子目录是有意义的。例如,您可以拥有/test/data或者/test/testdata如果需要Go来忽略该目录中的内容。请注意,Go也会忽略以“。”开头的目录或文件。或“_”,因此您在命名测试数据目录方面具有更大的灵活性。
请参阅/test目录以获取示例。
其他目录
/docs
设计和用户文档(除了你的godoc生成的文档)。
请参阅/docs目录以获取示例。
/tools
该项目的支持工具。请注意,这些工具可以从/pkg和/internal目录中导入代码。
请参阅/tools目录以获取示例。
/examples
应用程序和/或公共库的示例。请参阅/examples目录以获取示例。
/third_party
外部帮助工具,分叉代码和其他第三方实用程序(例如,Swagger UI)。
/githooks
Git钩子。
/assets
与您的存储库一起使用的其他资产(图像,徽标等)。
/website
如果您不使用Github页面,这是放置项目的网站数据的地方。请参阅/website目录以获取示例。
tip:你不应该有的目录/src
4.包管理工具
常见的包管理有,dep、go vendor、glide、go modules 等。
建议初步只看go mod,常用命令
go mod 命令
go mod tidy
拉取缺少的模块,移除不用的模块。
我常用这个命令。
go mod vendor
将依赖复制到vendor下。
我常用这个命令。
go mod download
下载依赖包。
go mod verify
检验依赖。
go mod graph
打印模块依赖图。
其他命令,可以执行 go mod ,查看即可。
5.生态及常用框架
1.urfave/cli go语言的命令行库 =》同类型:cobra
github| cli
https://github.com/urfave/cli
github| cobra
https://github.com/spf13/cobra
2. web框架 gin =》同类型 Iris、 Echo 、 Beego
文档 | Gin Web Framework
https://gin-gonic.com/zh-cn/docs/
3.加载配置文件 viper
https://github.com/spf13/viper#working-with-flags
4.casbin 轻量级访问控制框架
博客 | Casbin
https://www.kancloud.cn/oldlei/casbin/1289455
5.日志Zap、Lumberjack(Zap本身不支持切割归档日志文件,使用Lumberjack进行日志切割归档)
文档 | zap
https://pkg.go.dev/go.uber.org/zap
6.数据库 Gorm 类似Hibernate
文档| GORM
https://gorm.io/zh_CN/
7.定时调度robfig
8.colly 爬虫
6.刷些练习-例如算法
题解:https://github.com/halfrost/LeetCode-Go
参考:https://books.halfrost.com/leetcode/
模板:https://github.com/greyireland/algorithm-pattern
7.web项目 go-admin
比较规范的web管理系统模板,并集成了时下流行组件:
https://github.com/go-admin-team/go-admin.git
✨ 特性
遵循 RESTful API 设计规范
基于 GIN WEB API 框架,提供了丰富的中间件支持(用户认证、跨域、访问日志、追踪ID等)
基于Casbin的 RBAC 访问控制模型
JWT 认证
支持 Swagger 文档(基于swaggo)
基于 GORM 的数据库存储,可扩展多种类型数据库
配置文件简单的模型映射,快速能够得到想要的配置
代码生成工具
表单构建工具
多命令模式
TODO: 单元测试
8.编译、打包、部署
(略)
9.优秀参考项目
web项目:go-admin、相册管理photoprism
明星项目:Docker(容器技术)、Kubernetes(容器编排)Prometheus(监控系统)、Etcd(分布式存储)、InfluxDB(时序数据库)
10.设计模式
参考别人如何组织设计模式
https://github.com/senghoo/golang-design-pattern
02
Java和Go语言比较
这里比较并非全面,仅仅挑自己写代码感兴趣的几点作为比较。Java在web服务开发方面的底蕴,Go是很难追赶的。而Go在云生态方面就比Java先走了很远。作为搬砖工,我是不屑关注太底层或者太宏观的泛泛概念,手里的干活家伙才是我需要日常吐槽讨论的东西。
1.并发编程
线程(Thread):有时被称作轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。
协程(coroutine):又称微线程与子例程(或者称为函数)一样,协程(coroutine)也是一种程序组件。和线程类似,共享堆,不共享栈,协程的切换一般由程序员在代码中显式控制。它避免了上下文切换的额外耗费,兼顾了多线程的优点,简化了高并发程序的复杂。
线程与协程,表面上也很难就应用场景分好坏。
不过一句话听起来会让人对Go的并发感兴趣:一个Golang的程序中可以支持10w级别的Goroutine。
并发模型:Go实现了两种并发形式:
第一种是大家普遍认知的:多线程共享内存。其实就是Java或者C++等语言中的多线程开发。
非常典型的方式就是,在访问共享数据(例如数组、Map、或者某个结构体或对象)的时候,通过锁来访问,因此,在很多时候,衍生出一种方便操作的数据结构,叫做“线程安全的数据结构”。例如Java提供的包”java.util.concurrent”中的数据结构。Go中也实现了传统的线程并发模型。
另外一种是Go语言特有的,也是Go语言推荐的:CSP(communicating sequential processes)并发模型。goroutine 奉行通过通信来共享内存,而不是共享内存来通信。
Go的CSP并发模型,是通过goroutine
和channel
来实现的。在通信过程中,传数据channel <- data
和取数据<-channel
必然会成对出现,因为这边传,那边取,两个goroutine
之间才会实现通信。而且不管传还是取,必阻塞,直到另外的goroutine
传或者取为止。(本质上类似Java的同步阻塞队列)
2.反射
反射本质上,是能让程序猿动态的拿到写死的程序逻辑,进而可选择性作进一步处理,或者进行配置化执行逻辑。
Go反射比Java反射使用上简单。
Java提供了setAccessible方法来获取操作私有字段和私有方法的权限,而Golang未提供类似机制,因此Golang无法通过反射来操作私有字段和私有方法。想实现一个接口,直接实现接口包含的方法,不用关心方法属于哪个接口。
3.语言特性
1.在语言层面,Go不支持继承、不支持重载,导致诸多不便。
2.Java侵入式接口:需要显式地创建一个类去实现一个接口,Go非侵入式接口:不需要显式地创建一个类去实现一个接口。(不需要再语法上显式地声明,貌似没什么实际差别,用得着用侵入式这个词?)
go不支持继承,在 Go 中, 使用组合和内嵌实现代码的复用, 使用接口来实现多态和动态绑定。但是不支持重载,目前没看到好的办法,莫非,仅仅因为参数类型不同,就要实现一个新的名称的函数,或者在函数内部根据参数进行不同逻辑处理(这样属于硬编码!)?
总体而言,Go近些年,进步还是神速的,将来未必不能成为Java强劲的竞争对手。