8年Java开发转Go

1,976 阅读5分钟

「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战

2021年由于项目对并发和异步编程的要求很高,我开始深入了解Go语言,并使用它作为第一语言进行开发。之前我已经有8年Java语言的开发经历,突然转换为Go语言,整体来说并没有很痛苦。除了一些高级的编程模型不一样,Go的基本语法和语言风格,对于有一定编程经验的人来说,还是非常好上手。

下面主要总结下这一年使用Go开发后,对Go语言的一些领悟。

GO语言介绍

Go语言是在2009年底开源的,这十几年随着云计算领域的发展,仿佛像坐上了火箭一样迅猛发展。近几年在语言排行榜的前20名,Golang就没有缺席过,各个大厂现在也基本把Golang作为对面试者的基本要求。

对于Go语言编程主要有如下几点体会。

  1. 语言简洁,上手容易。 Go的语法特性太简单了,很难玩出什么花招,容易上手。
  2. 就是为并行和异步编程而生的语言。 Goroutine和Channel就是并发和异步编程的巨大亮点。像Java搞起并发和异步编程就是各种线程池,锁,原子性来控制就比较复杂了,也容易出错,但Go语言用非常优雅和流畅的方式解决这个问题。image.png
  3. lib库很小,但是基本工具都有。 Java的工具库太多了,Go这点跟Java还是有些差距,Go目前基本上有绝大多数常用的库,后续随着技术发展,这些问题容易解决。
  4. 语言设计的巧妙。 Go保持语言的小巧,不屏蔽底层且对底层友好,关注语言的执行效率和性能,并且用尽量少的代码完成尽量多的事。image.png

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语言的优势:

  1. 容易上手
  2. 解决了并发编程和底层应用开发效率的痛点
  3. 有Google世界一流公司的支持
  4. 杀手级应用是Docker容器

关于Docker,这是云计算中PaaS的关键技术,没有Docker,几乎所有要玩公有PaaS的公司和产品都玩不起来。PaaS是一个被世界或被产业界严重低估的平台

PaaS层是承上启下的关键技术,是公司要成长为一个大型公司的必要条件,它能解决如下问题:

  1. 软件生产线问题。持续集成和持续发布,DevOps必须通过PaaS
  2. 分布式服务化问题。服务高可用,服务编排,服务调度,服务发现,服务路由等
  3. 提高服务的可用性SLA。服务可用性SLA需要的分布式,高可用技术架构和运维工具,都是PaaS提供的
  4. 软件能力的复用。

Go语言和Docker作为PaaS平台的关键技术,前途是无限的。C和C++解决更底层操作系统的问题,Go依托PaaS解决中间层的问题,Java解决业务层的问题。