「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战」
2021年由于项目对并发和异步编程的要求很高,我开始深入了解Go语言,并使用它作为第一语言进行开发。之前我已经有8年Java语言的开发经历,突然转换为Go语言,整体来说并没有很痛苦。除了一些高级的编程模型不一样,Go的基本语法和语言风格,对于有一定编程经验的人来说,还是非常好上手。
下面主要总结下这一年使用Go开发后,对Go语言的一些领悟。
GO语言介绍
Go语言是在2009年底开源的,这十几年随着云计算领域的发展,仿佛像坐上了火箭一样迅猛发展。近几年在语言排行榜的前20名,Golang就没有缺席过,各个大厂现在也基本把Golang作为对面试者的基本要求。
对于Go语言编程主要有如下几点体会。
- 语言简洁,上手容易。 Go的语法特性太简单了,很难玩出什么花招,容易上手。
- 就是为并行和异步编程而生的语言。 Goroutine和Channel就是并发和异步编程的巨大亮点。像Java搞起并发和异步编程就是各种线程池,锁,原子性来控制就比较复杂了,也容易出错,但Go语言用非常优雅和流畅的方式解决这个问题。
- lib库很小,但是基本工具都有。 Java的工具库太多了,Go这点跟Java还是有些差距,Go目前基本上有绝大多数常用的库,后续随着技术发展,这些问题容易解决。
- 语言设计的巧妙。 Go保持语言的小巧,不屏蔽底层且对底层友好,关注语言的执行效率和性能,并且用尽量少的代码完成尽量多的事。
Go语言编程模型
一、切片,接口,性能
切片
test := make([]int, 5)
test[2] = 11
test[3] = 12
test2 := test[1:3]
test2[1] = 9
对于切片 test和test2的内存共享,对数组内容的修改都会影响到对方。
test := make([]int, 5)
test2 := test[1:3]
test = append(test, 1)
test2[1] = 9
append操作会给test重新分配内存,test和test2的内存不再共享。
接口
type Test1 struct {
Name string
}
type Test2 struct {
Name string
}
type Stringable interface {
ToString() string
}
func (c Test1) ToString() string {
return "Test1 = " + c.Name
}
func (c Test2) ToString() string{
return "Test2 = " + c.Name
}
func PrintStr(p Stringable) {
fmt.Println(p.ToString())
}
d1 := Test1 {"Test1"}
d2 := Test2{"Test2"}
PrintStr(d1)
PrintStr(d2)
面向对象编程的黄金法则:Program to an interface not an implementation
性能
- strconv.Itoa()比fmt.Springtf()要快1倍
- 避免把String转成[]Byte
- 用StringBuffer或者StringBuild来拼接字符串
- 并发用goroutine,然后用syc.WaitGroup来同步分片
- 内存分配尽可能用sync.Pool来重用对象
- I/O操作用bufio.NewWrite()和bufio.NewReader()可以带来更高的性能
- 用regexp.Compile()编译正则表达式
- 更高性能的协议使用protobuf而不是JSON
二、错误处理
Go语言的函数支持多返回值,很多函数都会返回result, err两个值
- 返回接口把结果和错误分离,使得函数的接口语义更清晰
- Go语言中的错误参数如果要忽略,需要显示的忽略,用_这样的变量来忽略
- 返回的error是个接口,可以自己扩张自定义错误处理
if err != nil {
switch err.(type) {
case *NullPointerError:
...
case *json.SyntaxError:
...
case *ZeroDivisionError:
...
default:
...
}
}
Go语言的错误处理方式,本质上是返回值检查,但是它兼顾了异常的一些好处,能够对错误进行扩展。
三、函数式编程
type Option func(*Server)
func Protocol(p string) Option {
return func(s *Server) {
s.Protocol = p
}
}
func Timeout(timeout time.Duration) Option {
return func(s *Server) {
s.Timeout = timeout
}
}
func MaxConns(maxconns int) Option {
return func(s *Server) {
s.MaxConns = maxconns
}
}
func TLS(tls *tls.Config) Option {
return func(s *Server) {
s.TLS = tls
}
}
func NewServer(addr string, port int, options ...func(*Server)) (*Server, error) {
srv := Server{
Addr: addr,
Port: port,
Protocol: "tcp",
Timeout: 30 * time.Second,
MaxConns: 1000,
TLS: nil,
}
for _, option := range options {
option(&srv)
}
//...
return &srv, nil
}
s1, _ := NewServer("localhost", 1024)
s2, _ := NewServer("localhost", 2048, Protocol("udp"))
s3, _ := NewServer("0.0.0.0", 8080, Timeout(300*time.Second), MaxConns(1000))
函数式编程带来的好处:直觉式编程,可配置化,容易维护和扩展
四、Pipeline
Go语言最具特色的Go Routine和Channel这两个神器
func echo(nums []int) <-chan int {
out := make(chan int)
go func() {
for _, n := range nums {
out <- n
}
close(out)
}()
return out
}
func sq(in <-chan int) <-chan int {
out := make(chan int)
go func() {
for n := range in {
out <- n * n
}
close(out)
}()
return out
}
func odd(in <-chan int) <-chan int {
out := make(chan int)
go func() {
for n := range in {
if n%2 != 0 {
out <- n
}
}
close(out)
}()
return out
}
type EchoFunc func ([]int) (<- chan int)
type PipeFunc func (<- chan int) (<- chan int)
func pipeline(nums []int, echo EchoFunc, pipeFns ... PipeFunc) <- chan int {
ch := echo(nums)
for i := range pipeFns {
ch = pipeFns[i](ch)
}
return ch
}
var nums = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
for n := range pipeline(nums, echo, odd, sq) {
fmt.Println(n)
}
Go编程中的Pipeline模式,可以像Unix/Linux命令行那样,它是一种把命令拼接起来完成一个更强功能的技术方法。流式处理,函数是编程,应用网关对微服务进行简单的API编排,都是受到Pipeline这种技术方式的影响。
Go语言未来趋势
Go语言的优势:
- 容易上手
- 解决了并发编程和底层应用开发效率的痛点
- 有Google世界一流公司的支持
- 杀手级应用是Docker容器
关于Docker,这是云计算中PaaS的关键技术,没有Docker,几乎所有要玩公有PaaS的公司和产品都玩不起来。PaaS是一个被世界或被产业界严重低估的平台
PaaS层是承上启下的关键技术,是公司要成长为一个大型公司的必要条件,它能解决如下问题:
- 软件生产线问题。持续集成和持续发布,DevOps必须通过PaaS
- 分布式服务化问题。服务高可用,服务编排,服务调度,服务发现,服务路由等
- 提高服务的可用性SLA。服务可用性SLA需要的分布式,高可用技术架构和运维工具,都是PaaS提供的
- 软件能力的复用。
Go语言和Docker作为PaaS平台的关键技术,前途是无限的。C和C++解决更底层操作系统的问题,Go依托PaaS解决中间层的问题,Java解决业务层的问题。