Go最全大红大紫的 Golang 真的是后端开发中的万能药吗?,大牛手把手带你

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

func longTask(signal chan int) {

// 不带参数的 for

// 相当于 while 循环

for {

// 接收 signal 通道传值

v := <- signal

// 如果接收值为 1,停止循环

if v == 1 {

break

}

time.Sleep(1 * Second)

}

}

func main() {

// 声明通道

sig := make(chan int)

// 异步调用 longTask

go longTask(sig)

// 等待 1 秒钟

time.Sleep(1 * time.Second)

// 向通道 sig 传值

sig <- 1

// 然后 longTask 会接收 sig 传值,终止循环

}

复制代码

面向接口编程

Go 语言不是严格的面向对象编程(OOP),它采用的是面向接口编程(IOP),是相对于 OOP 更先进的编程模式。作为 OOP 体系的一部分,IOP 更加强调规则和约束,以及接口类型方法的约定,从而让开发人员尽可能的关注更抽象的程序逻辑,而不是在更细节的实现方式上浪费时间。很多大型项目采用的都是 IOP 的编程模式。如果想了解更多面向接口编程,请查看 “码之道” 个人技术博客的往期文章《为什么说 TypeScript 是开发大型前端项目的必备语言》,其中有关于面向接口编程的详细讲解。

Go 语言跟 TypeScript 一样,也是采用鸭子类型的方式来校验接口继承。下面这个例子可以描述 Go 语言的鸭子类型特性。

// 定义 Animal 接口

interface Animal {

Eat() // 声明 Eat 方法

Move() // 声明 Move 方法

}

// ==== 定义 Dog Start ====

// 定义 Dog 类

type Dog struct {

}

// 实现 Eat 方法

func (d *Dog) Eat() {

fmt.Printf(“Eating bones”)

}

// 实现 Move 方法

func (d *Dog) Move() {

fmt.Printf(“Moving with four legs”)

}

// ==== 定义 Dog End ====

// ==== 定义 Human Start ====

// 定义 Human 类

type Human struct {

}

// 实现 Eat 方法

func (h *Human) Eat() {

fmt.Printf(“Eating rice”)

}

// 实现 Move 方法

func (h *Human) Move() {

fmt.Printf(“Moving with two legs”)

}

// ==== 定义 Human End ====

复制代码

可以看到,虽然 Go 语言可以定义接口,但跟 Java 不同的是,Go 语言中没有显示声明接口实现(Implementation)的关键词修饰语法。在 Go 语言中,如果要继承一个接口,你只需要在结构体中实现该接口声明的所有方法。这样,对于 Go 编译器来说你定义的类就相当于继承了该接口。在这个例子中,我们规定,只要既能吃(Eat)又能活动(Move)的东西就是动物(Animal)。而狗(Dog)和人(Human)恰巧都可以吃和动,因此它们都被算作动物。这种依靠实现方法匹配度的继承方式,就是鸭子类型:如果一个动物看起来像鸭子,叫起来也像鸭子,那它一定是鸭子。这种鸭子类型相对于传统 OOP 编程语言显得更灵活。但是,后面我们会讨论到,这种编程方式会带来一些麻烦。

错误处理

Go 语言的错误处理是臭名昭著的啰嗦。这里先给一个简单例子。

package main

import “fmt”

func isValid(text string) (valid bool, err error){

if text == “” {

return false, error(“text cannot be empty”)

}

return text == “valid text”, nil

}

func validateForm(form map[string]string) (res bool, err error) {

for _, text := range form {

valid, err := isValid(text)

if err != nil {

return false, err

}

if !valid {

return false, nil

}

}

return true, nil

}

func submitForm(form map[string]string) (err error) {

if res, err := validateForm(form); err != nil || !res {

return error(“submit error”)

}

fmt.Printf(“submitted”)

return nil

}

func main() {

form := map[string]string{

“field1”: “”,

“field2”: “invalid text”,

“field2”: “valid text”,

}

if err := submitForm(form); err != nil {

panic(err)

}

}

复制代码

虽然上面整个代码是虚构的,但可以从中看出,Go 代码中充斥着 if err := ...; err != nil { ... } 之类的错误判断语句。这是因为 Go 语言要求开发者自己管理错误,也就是在函数中的错误需要显式抛出来,否则 Go 程序不会做任何错误处理。因为 Go 没有传统编程语言的 try/catch 针对错误处理的语法,所以在错误管理上缺少灵活度,导致了 “err 满天飞” 的局面。

不过,辩证法则告诉我们,这种做法也是有好处的。第一,它强制要求 Go 语言开发者从代码层面来规范错误的管理方式,这驱使开发者写出更健壮的代码;第二,这种显式返回错误的方式避免了 “try/catch 一把梭”,因为这种 “一时爽” 的做法很可能导致 Bug 无法准确定位,从而产生很多不可预测的问题;第三,由于没有 try/catch 的括号或额外的代码块,Go 程序代码整体看起来更清爽,可读性较强。

其他

Go 语言肯定还有很多其他特性,但笔者认为以上的特性是 Go 语言中比较有特色的,是区分度比较强的特性。Go 语言其他一些特性还包括但不限于如下内容。

  • 编译迅速

  • 跨平台

  • defer 延迟执行

  • select/case 通道选择

  • 直接编译成可执行程序

  • 非常规依赖管理(可以直接引用 Github 仓库作为依赖,例如 import "github.com/crawlab-team/go-trace"

  • 非常规日期格式(格式为 “2006-01-02 15:04:05”,你没看错,据说这就是 Golang 的创始时间!)

优缺点概述


前面介绍了 Go 的很多语言特性,想必读者已经对 Golang 有了一些基本的了解。其中的一些语言特性也暗示了它相对于其他编程语言的优缺点。Go 语言虽然现在很火,在称赞并拥抱 Golang 的同时,不得不了解它的一些缺点。

这里笔者不打算长篇大论的解析 Go 语言的优劣,而是将其中相关的一些事实列举出来,读者可以自行判断。以下是笔者总结的 Golang 语言特性的不完整优缺点对比列表。

| 特性 | 优点 | 缺点 |

| — | — | — |

| 语法简单 | 提升开发效率,节省时间 | 难以处理一些复杂的工程问题 |

| 天然支持并发 | 极大减少异步编程的难度,提高开发效率 | 不熟悉通道和协程的开发者会有一些学习成本 |

| 类型系统 |

  • Go 语言是静态类型,相对于动态类型语言更稳定和可预测

  • IOP 鸭子类型比严格的 OOP 语言更简洁

|

  • 没有继承、抽象、静态、动态等特性

  • 缺少泛型,导致灵活性降低

  • 难以快速构建复杂通用的框架或工具

|

| 错误处理 | 强制约束错误管理,避免 “try/catch 一把梭” | 啰嗦的错误处理代码,充斥着 if err := ... |

| 编译迅速 | 这绝对是一个优点 | 怎么可能是缺点? |

| 非常规依赖管理 |

  • 可以直接引用发布到 Github 上的仓库作为模块依赖引用,省去了依赖托管的官方网站

  • 可以随时在 Github 上发布 Go 语言编写的第三方模块

  • 自由的依赖发布意味着 Golang 的生态发展将不受官方依赖托管网站的限制

| 严重依赖 Github,在 Github 上搜索 Go 语言模块相对不精准 |

| 非常规日期格式 | 按照 6-1-2-3-4-5(2006-01-02 15:04:05),相对来说比较好记 | 对于已经习惯了 yyyy-MM-dd HH:mm:ss 格式的开发者来说非常不习惯 |

其实,每一个特性在某种情境下都有其相应的优势和劣势,不能一概而论。就像 Go 语言采用的静态类型和面向接口编程,既不缺少类型约束,也不像严格 OOP 那样冗长繁杂,是介于动态语言和传统静态类型 OOP 语言之间的现代编程语言。这个定位在提升 Golang 开发效率的同时,也阉割了不少必要 OOP 语法特性,从而缺乏快速构建通用工程框架的能力(这里不是说 Go 无法构建通用框架,而是它没有 Java、C# 这么容易)。另外,Go 语言 “奇葩” 的错误处理规范,让 Go 开发者们又爱又恨:可以开发出更健壮的应用,但同时也牺牲了一部分代码的简洁性。要知道,Go 语言的设计理念是为了 “大道至简”,因此才会在追求高性能的同时设计得尽可能简单。

无可否认的是,Go 语言内置的并发支持是非常近年来非常创新的特性,这也是它被分布式系统广泛采用的重要原因。同时,它相对于动辄编译十几分钟的 Java 来说是非常快的。此外,Go 语言没有因为语法简单而牺牲了稳定性;相反,它从简单的约束规范了整个 Go 项目代码风格。因此,**“快”(Fast)、“简”(Concise)、“稳”(Robust)**是 Go 语言的设计目的。我们在对学习 Golang 的过程中不能无脑的接纳它的一切,而是应该根据它自身的特性判断在实际项目应用中的情况。

适用场景


经过前文关于 Golang 各个维度的讨论,我们可以得出结论:Go 语言并不是后端开发的万能药。在实际开发工作中,开发者应该避免在任何情况下无脑使用 Golang 作为后端开发语言。相反,工程师在决定技术选型之前应该全面了解候选技术(语言、框架或架构)的方方面面,包括候选技术与业务需求的切合度,与开发团队的融合度,以及其学习、开发、时间成本等因素。笔者在学习了包括前后端的一些编程语言之后,发现它们各自有各自的优势,也有相应的劣势。如果一门编程语言能广为人知,那它绝对不会是一门糟糕语言。因此,笔者不会断言 “XXX 是世界上最好的语言“,而是给读者分享个人关于特定应用场景下技术选型的思路。当然,本文是针对 Go 语言的技术文,接下来笔者将分享一下个人认为 Golang 最适合的应用场景。

分布式应用

Golang 是非常适合在分布式应用场景下开发的。分布式应用的主要目的是尽可能多的利用计算资源和网络带宽,以求最大化系统的整体性能和效率,其中重要的需求功能就是并发(Concurrency)。而 Go 是支持高并发异步编程方面的佼佼者。前面已经提到,Go 语言内置了协程(Goroutine)通道(Channel)两大并发特性,这使后端开发者进行异步编程变得非常容易。Golang 中还内置了sync,包含 Mutex(互斥锁)、WaitGroup(等待组)、Pool(临时对象池)等接口,帮助开发者在并发编程中能更安全的掌控 Go 程序的并发行为。Golang 还有很多分布式应用开发工具,例如分布式储存系统(Etcd、SeaweedFS)、RPC 库(gRPC、Thrift)、主流数据库 SDK(mongo-driver、gnorm、redigo)等。这些都可以帮助开发者有效的构建分布式应用。

网络爬虫

稍微了解网络爬虫的开发者应该会听说过 Scrapy,再不济也是 Python。市面上关于 Python 网络爬虫的技术书籍数不胜数,例如崔庆才的 《Python 3 网络开发实战》 和韦世东的《Python 3 网络爬虫宝典 用 Python 编写的高性能爬虫框架 Scrapy》,自发布以来一直是爬虫工程师的首选。

不过,由于近期 Go 语言的迅速发展,越来越多的爬虫工程师注意到用 Golang 开发网路爬虫的巨大优势。其中,用 Go 语言编写的 Colly 爬虫框架,如今在 Github 上已经有 13k+ 标星。其简洁的 API 以及高效的采集速度,吸引了很多爬虫工程师,占据了爬虫界一哥 Scrapy 的部分份额。前面已经提到,Go 语言内置的并发特性让严重依赖网络带宽的爬虫程序更加高效,很大的提高了数据采集效率。另外,Go 语言作为静态语言,相对于动态语言 Python 来说有更好的约束下,因此健壮性和稳定性都更好。

后端 API

Golang 有很多优秀的后端框架,它们大部分都非常完备的支持了现代后端系统的各种功能需求:RESTful API、路由、中间件、配置、鉴权等模块。而且用 Golang 写的后端应用性能很高,通常有非常快的响应速度。笔者曾经在开源爬虫管理平台 Crawlab 中用 Golang 重构了 Python 的后端 API,响应速度从之前的几百毫秒优化到了几十毫秒甚至是几毫秒,用实践证明 Go 语言在后端性能方面全面碾压动态语言。Go 语言中比较知名的后端框架有 GinBeegoEchoIris

当然,这里并不是说用 Golang 写后端就完全是一个正确的选择。笔者在工作中会用到 Java 和 C#,用了各自的主流框架(SpringBoot 和 .Net Core)之后,发现这两门传统 OOP 语言虽然语法啰嗦,但它们的语法特性很丰富,特别是泛型,能够轻松应对一些逻辑复杂、重复性高的业务需求。因此,笔者认为在考虑用 Go 来编写后端 API 时候,可以提前调研一下 Java 或 C#,它们在写后端业务功能方面做得非常棒。

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

法特性很丰富,特别是泛型,能够轻松应对一些逻辑复杂、重复性高的业务需求。因此,笔者认为在考虑用 Go 来编写后端 API 时候,可以提前调研一下 Java 或 C#,它们在写后端业务功能方面做得非常棒。

[外链图片转存中…(img-fzT7asFr-1715509970959)]
[外链图片转存中…(img-2u6TUnZ7-1715509970959)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 7
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值