精华内容
参与话题
问答
  • go语言实战

    2017-10-08 18:44:20
    go语言实战go语言实战go语言实战go语言实战go语言实战go语言实战
  • GO语言实战

    2018-01-07 11:43:34
    Go语言结合了底层系统语言的能力以及现代语言的高级特性,旨在降低构建简单、可靠、高效软件的门槛。...Go语言实战同时关注语言的规范和实现,涉及的内容包括语法、类型系统、并发、管道、测试,以及其他一些主题。
  • Go语言实战

    2017-08-11 13:01:50
    Go语言结合了底层系统语言的能力以及现代语言的高级特性,旨在降低构建简单、可靠、高效软件的门槛。...Go语言实战同时关注语言的规范和实现,涉及的内容包括语法、类型系统、并发、管道、测试,以及其他一些主题。
  • Go 语言实战

    2018-04-12 10:41:40
    尽管这门语言借鉴了很多其他语言的思想,但是凭借自身统一和自然的表达,Go 程序在本质上完全不同于用其他语言编写的程序。Go 平衡了底层系统语言的能力,以及在现代语言中所见到的高级特性。你可以依靠 Go 语言来...

    内容介绍

    Go 是一门开源的编程语言,目的在于降低构建简单、可靠、高效软件的门槛。尽管这门语言借鉴了很多其他语言的思想,但是凭借自身统一和自然的表达,Go 程序在本质上完全不同于用其他语言编写的程序。Go 平衡了底层系统语言的能力,以及在现代语言中所见到的高级特性。你可以依靠 Go 语言来构建一个非常快捷、高性能且有足够控制力的编程环境。使用 Go 语言,可以写得更少,做得更多。

    本书是写给已经有一定其他语言编程经验,并且想学习 Go 语言的中级开发者的。我们写这本书的目的是,为读者提供一个专注、全面且符合语言习惯的视角。我们同时关注语言的规范和实现,涉及的内容包括语法、类型系统,并发、通道、测试以及其他一些主题。我们相信,对于刚开始学 Go 语言的人,以及想要深入了解这门语言内部实现的人来说,本书都是极佳的选择。

    译者简介

    李兆海,多年专注于后端分布式网络服务开发,曾使用过多个流行后端技术和相关架构实践,是 Go 语言和 Docker 的早期使用者和推广者,《第一本 Docker 书》的译者。作为项目技术负责人,成功开发了百万用户级直播系统。

    课程内容

    译者序

    Go 语言是由谷歌公司在2007年开始开发的一门语言,目的是能在多核心时代高效编写网络应用程序。Go 语言的创始人 Robert Griesemer、Rob Pike 和 Ken Thompson 都是在计算机发展过程中作出过重要贡献的人。自从2009年11月正式公开发布后,Go 语言迅速席卷了整个互联网后端开发领域,其社区里不断涌现出类似 vitess、Docker、etcd、Consul 等重量级的开源项目。

    在 Go 语言发布后,我就被其简洁、强大的特性所吸引,并于2010年开始在技术聚会上宣传 Go 语言,当时所讲的题目是《Go 语言:互联网时代的 C》。现在看来,Go 语言确实很好地解决了互联网时代开发的痛点,而且入门门槛不高,是一种上手容易、威力强大的工具。试想一下,不需要学习复杂的异步逻辑,使用习惯的顺序方法,就能实现高性能的网络服务,并充分利用系统的多个核心,这是多么美好的一件事情。

    本书是国外 Go 社区多年经验积累的成果。本书默认读者已经具有一定的编程基础,希望更好地使用 Go 语言。全书以示例为基础,详细介绍了 Go 语言中的一些比较深入的话题。对于有经验的程序员来说,很容易通过学习书中的例子来解决自己实际工作中遇到的问题。辅以文字介绍,读者会对相关问题有更系统的了解和认识。翻译过程中我尽量保持了原书的叙述方法,并加强了叙述逻辑,希望读者会觉得清晰易读。

    在翻译本书的过程中,感谢人民邮电出版社编辑杨海玲老师的指导和进度安排,让本书能按时与读者见面。感谢谢孟军对译稿的审校,你的润色使译文读起来流畅了很多。尤其要感谢我老婆对我的支持,感谢你能理解我出于热爱才会“匍匐”在计算机前码字。

    最后,感谢读者购买此书。希望读者在探索 Go 语言的道路上,能够享受到和我一样的乐趣。

    在计算机科学领域,提到不同寻常的人,总会有一些名字会闪现在你的脑海中。Rob Pike、Robert Griesmier 和 Ken Thompson 就是其中几个。他们3个人负责构建过 UNIX、Plan 9、B、Java的JVM HotSpot、V8、Strongtalk、Sawzall、Ed、Acme 和 UTF8,此外还有很多其他的创造。在2007年,这3个人凑在一起,尝试一个伟大的想法:综合他们多年的经验,借鉴已有的语言,来创建一门与众不同的、全新的系统语言。他们随后以开源的形式发布了自己的实验成果,并将这种语言命名为“Go”。如果按照现在的路线发展下去,这门语言将是这3个人最有影响的一项创造。

    当人们聚在一起,纯粹是为了让世界变得更好的时候,往往也是他们处于最佳状态的时候。在2013年,为了围绕 Go 语言构建一个更好的社区,Brian 和 Erik 联合成立了 Gopher Academy,没过多久,Bill 和其他一些有类似想法的人也加入进来。他们首先注意到,社区需要有一个地方可以在线聚集和分享素材,所以他们在 slack 创立了 Go 讨论版和 Gopher Academy 博客。随着时间的推移,社区越来越大,他们创建了世界上第一个全球 Go 语言大会—GopherCon。随着与社区更深入地交流,他们意识到还需要为广大想学习这门新语言的人提供一些资源,所以他们开始着手写一本书,就是现在你手里拿的这本书。

    为 Go 社区贡献了大量的时间和精力的3位作者,出于对 Go 语言社区的热爱写就了这本书。我曾在 Bill、Brian 和 Erik 身边,见证了他们在不同的环境和角色(作为 Gopher Academy 博客的编辑,作为大会组织者,甚至是在他们的日常工作中,作为父亲和丈夫)下,都会认真负责地撰写和修订本书。对他们来说,这不仅仅是一本书,也是对他们心爱的语言的献礼。他们并不满足于写就一本“好”书。他们编写、审校,再写、再修改,再三推敲每页文字、每个例子、每一章,直到认为本书的内容配得上他们珍视的这门语言。

    离开一门使用舒服、掌握熟练的语言,去学习一门不仅对自己来说,对整个世界来说都是全新的语言,是需要勇气的。这是一条人迹罕至,沿途充满 bug,只有少数先行者熟悉的路。这里充满了意外的错误,文档不明确或者缺失,而且缺少可以拿来即用的代码库。这是拓荒者、先锋才会选择的道路。如果你正在读这本书,那么你可能正在踏上这段旅途。

    本书自始至终是为你—本书的读者精心制作的一本探索、学习和使用 Go 语言的简洁而全面的指导手册。在全世界,你也不会找到比 Bill、Brian 和 Erik 更好的导师了。我非常高兴你能开始探索 Go 语言的优点,期望能在线上和线下大会上遇到你。

    Steve Francia
    Go 语言开发者,Hugo、Cobra、Viper 和 SPF13-VIM 的创建人


    一个高性能强类型的Smalltalk实现。——译者注

    前言

    那是2013年10月,我刚刚花几个月的时间写完 GoingGo.net 博客,就接到了 Brian Ketelsen 和 Erik St. Martin 的电话。他们正在写这本书,问我是否有兴趣参与进来。我立刻抓住机会,参与到写作中。当时,作为一个 Go 语言的新手,这是我进一步了解这门语言的好机会。毕竟,与 Brian 和 Erik 一起工作、一起分享获得的知识,比我从构建博客中学到的要多得多。

    完成前4章后,我们在 Manning 早期访问项目(MEAP)中发布了这本书。很快,我们收到了来自语言团队成员的邮件。这位成员对很多细节提供了评审意见,还附加了大量有用的知识、意见、鼓励和支持。根据这些评审意见,我们决定从头开始重写第2章,并对第4章进行了全面修订。据我们所知,对整章进行重写的情况并不少见。通过这段重写的经历,我们学会要依靠社区的帮助来完成写作,因为我们希望能立刻得到社区的支持。

    自那以后,这本书就成了社区努力的成果。我们投入了大量的时间研究每一章,开发样例代码,并和社区一起评审、讨论并编辑书中的材料和代码。我们尽了最大的努力来保证本书在技术上没有错误,让代码符合通用习惯,并且使用社区认为应该有的方式来教 Go 语言。同时,我们也融入了自己的思考、自己的实践和自己的指导方式。

    我们希望本书能帮你学习 Go 语言,不仅是当下,就是多年以后,你也能从本书中找到有用的东西。Brian、Erik 和我总会在线上帮助那些希望得到我们帮助的人。如果你购买了本书,谢谢你,来和我们打个招呼吧。

    William Kennedy

    第01章:关于 Go 语言的介绍

    本章主要内容

    • 用 Go 语言解决现代计算难题
    • 使用 Go 语言工具

    计算机一直在演化,但是编程语言并没有以同样的速度演化。现在的手机,内置的 CPU 核数可能都多于我们使用的第一台电脑。高性能服务器拥有64核、128核,甚至更多核。但是我们依旧在使用为单核设计的技术在编程。

    编程的技术同样在演化。大部分程序不再由单个开发者来完成,而是由处于不同时区、不同时间段工作的一组人来完成。大项目被分解为小项目,指派给不同的程序员,程序员开发完成后,再以可以在各个应用程序中交叉使用的库或者包的形式,提交给整个团队。

    如今的程序员和公司比以往更加信任开源软件的力量。Go 语言是一种让代码分享更容易的编程语言。Go 语言自带一些工具,让使用别人写的包更容易,并且 Go 语言也让分享自己写的包更容易。

    在本章中读者会看到 Go 语言区别于其他编程语言的地方。Go 语言对传统的面向对象开发进行了重新思考,并且提供了更高效的复用代码的手段。Go 语言还让用户能更高效地利用昂贵服务器上的所有核心,而且它编译大型项目的速度也很快。

    在阅读本章时,读者会对影响 Go 语言形态的很多决定有一些认识,从它的并发模型到快如闪电的编译器。我们在前言中提到过,这里再强调一次:这本书是写给已经有一定其他编程语言经验、想学习 Go 语言的中级开发者的。本书会提供一个专注、全面且符合习惯的视角。我们同时专注语言的规范和实现,涉及的内容包括语法、Go 语言的类型系统、并发、通道、测试以及其他一些非常广泛的主题。我们相信,对刚开始要学习 Go 语言和想要深入了解语言内部实现的人来说,本书都是最佳选择。

    本书示例中的源代码可以在这里下载。

    我们希望读者能认识到,Go 语言附带的工具可以让开发人员的生活变得更简单。最后,读者会意识到为什么那么多开发人员用 Go 语言来构建自己的新项目。

    1.1 用Go解决现代编程难题

    Go 语言开发团队花了很长时间来解决当今软件开发人员面对的问题。开发人员在为项目选择语言时,不得不在快速开发和性能之间做出选择。C 和 C++这类语言提供了很快的执行速度,而 Ruby 和 Python 这类语言则擅长快速开发。Go 语言在这两者间架起了桥梁,不仅提供了高性能的语言,同时也让开发更快速。

    在探索 Go 语言的过程中,读者会看到精心设计的特性以及简洁的语法。作为一门语言,Go 不仅定义了能做什么,还定义了不能做什么。Go 语言的语法简洁到只有几个关键字,便于记忆。Go 语言的编译器速度非常快,有时甚至会让人感觉不到在编译。所以,Go 开发者能显著减少等待项目构建的时间。因为 Go 语言内置并发机制,所以不用被迫使用特定的线程库,就能让软件扩展,使用更多的资源。Go 语言的类型系统简单且高效,不需要为面向对象开发付出额外的心智,让开发者能专注于代码复用。Go 语言还自带垃圾回收器,不需要用户自己管理内存。让我们快速浏览一下这些关键特性。

    1.1.1 开发速度

    编译一个大型的 C 或者 C++项目所花费的时间甚至比去喝杯咖啡的时间还长。图1-1是 XKCD 中的一幅漫画,描述了在办公室里开小差的经典借口。

    enter image description here

    Go 语言使用了更加智能的编译器,并简化了解决依赖的算法,最终提供了更快的编译速度。编译 Go 程序时,编译器只会关注那些直接被引用的库,而不是像 Java、C 和 C++那样,要遍历依赖链中所有依赖的库。因此,很多 Go 程序可以在1秒内编译完。在现代硬件上,编译整个 Go 语言的源码树只需要20秒。

    因为没有从编译代码到执行代码的中间过程,用动态语言编写应用程序可以快速看到输出。代价是,动态语言不提供静态语言提供的类型安全特性,不得不经常用大量的测试套件来避免在运行的时候出现类型错误这类 bug。

    想象一下,使用类似 JavaScript 这种动态语言开发一个大型应用程序,有一个函数期望接收一个叫作ID的字段。这个参数应该是整数,是字符串,还是一个 UUID?要想知道答案,只能去看源代码。可以尝试使用一个数字或者字符串来执行这个函数,看看会发生什么。在 Go 语言里,完全不用为这件事情操心,因为编译器就能帮用户捕获这种类型错误。

    1.1.2 并发

    作为程序员,要开发出能充分利用硬件资源的应用程序是一件很难的事情。现代计算机都拥有多个核,但是大部分编程语言都没有有效的工具让程序可以轻易利用这些资源。这些语言需要写大量的线程同步代码来利用多个核,很容易导致错误。

    Go 语言对并发的支持是这门语言最重要的特性之一。goroutine 很像线程,但是它占用的内存远少于线程,使用它需要的代码更少。通道(channel)是一种内置的数据结构,可以让用户在不同的 goroutine 之间同步发送具有类型的消息。这让编程模型更倾向于在 goroutine 之间发送消息,而不是让多个 goroutine 争夺同一个数据的使用权。让我们看看这些特性的细节。

    1.goroutine

    goroutine 是可以与其他 goroutine 并行执行的函数,同时也会与主程序(程序的入口)并行执行。在其他编程语言中,你需要用线程来完成同样的事情,而在 Go 语言中会使用同一个线程来执行多个 goroutine。例如,用户在写一个 Web 服务器,希望同时处理不同的 Web 请求,如果使用 C 或者 Java,不得不写大量的额外代码来使用线程。在 Go 语言中,net/http 库直接使用了内置的 goroutine。每个接收到的请求都自动在其自己的 goroutine 里处理。goroutine 使用的内存比线程更少,Go 语言运行时会自动在配置的一组逻辑处理器上调度执行 goroutine。每个逻辑处理器绑定到一个操作系统线程上(见图1-2)。这让用户的应用程序执行效率更高,而开发工作量显著减少。

    enter image description here

    如果想在执行一段代码的同时,并行去做另外一些事情,goroutine 是很好的选择。下面是一个简单的例子:

    func log(msg string) {    ...这里是一些记录日志的代码}// 代码里有些地方检测到了错误go log("发生了可怕的事情")

    关键字go是唯一需要去编写的代码,调度log函数作为独立的 goroutine 去运行,以便与其他 goroutine 并行执行。这意味着应用程序的其余部分会与记录日志并行执行,通常这种并行能让最终用户觉得性能更好。就像之前说的,goroutine 占用的资源更少,所以常常能启动成千上万个 goroutine。我们会在第6章更加深入地探讨 goroutine 和并发。

    2.通道

    通道是一种数据结构,可以让 goroutine 之间进行安全的数据通信。通道可以帮用户避免其他语言里常见的共享内存访问的问题。

    并发的最难的部分就是要确保其他并发运行的进程、线程或 goroutine 不会意外修改用户的数据。当不同的线程在没有同步保护的情况下修改同一个数据时,总会发生灾难。在其他语言中,如果使用全局变量或者共享内存,必须使用复杂的锁规则来防止对同一个变量的不同步修改。

    为了解决这个问题,通道提供了一种新模式,从而保证并发修改时的数据安全。通道这一模式保证同一时刻只会有一个 goroutine 修改数据。通道用于在几个运行的 goroutine 之间发送数据。在图1-3中可以看到数据是如何流动的示例。想象一个应用程序,有多个进程需要顺序读取或者修改某个数据,使用 goroutine 和通道,可以为这个过程建立安全的模型。

    enter image description here

    图1-3中有3个 goroutine,还有2个不带缓存的通道。第一个 goroutine 通过通道把数据传给已经在等待的第二个 goroutine。在两个 goroutine 间传输数据是同步的,一旦传输完成,两个 goroutine 都会知道数据已经完成传输。当第二个 goroutine 利用这个数据完成其任务后,将这个数据传给第三个正在等待的 goroutine。这次传输依旧是同步的,两个 goroutine 都会确认数据传输完成。这种在 goroutine 之间安全传输数据的方法不需要任何锁或者同步机制。

    需要强调的是,通道并不提供跨 goroutine 的数据访问保护机制。如果通过通道传输数据的一份副本,那么每个 goroutine 都持有一份副本,各自对自己的副本做修改是安全的。当传输的是指向数据的指针时,如果读和写是由不同的 goroutine 完成的,每个 goroutine 依旧需要额外的同步动作。

    1.1.3 Go 语言的类型系统

    Go 语言提供了灵活的、无继承的类型系统,无需降低运行性能就能最大程度上复用代码。这个类型系统依然支持面向对象开发,但避免了传统面向对象的问题。如果你曾经在复杂的 Java 和 C++程序上花数周时间考虑如何抽象类和接口,你就能意识到 Go语言的类型系统有多么简单。Go 开发者使用组合(composition)设计模式,只需简单地将一个类型嵌入到另一个类型,就能复用所有的功能。其他语言也能使用组合,但是不得不和继承绑在一起使用,结果使整个用法非常复杂,很难使用。在 Go 语言中,一个类型由其他更微小的类型组合而成,避免了传统的基于继承的模型。

    另外,Go 语言还具有独特的接口实现机制,允许用户对行为进行建模,而不是对类型进行建模。在 Go 语言中,不需要声明某个类型实现了某个接口,编译器会判断一个类型的实例是否符合正在使用的接口。Go 标准库里的很多接口都非常简单,只开放几个函数。从实践上讲,尤其对那些使用类似 Java 的面向对象语言的人来说,需要一些时间才能习惯这个特性。

    1.类型简单

    Go 语言不仅有类似intstring这样的内置类型,还支持用户定义的类型。在 Go 语言中,用户定义的类型通常包含一组带类型的字段,用于存储数据。Go 语言的用户定义的类型看起来和 C 语言的结构很像,用起来也很相似。不过 Go 语言的类型可以声明操作该类型数据的方法。传统语言使用继承来扩展结构——Client 继承自 User,User 继承自 Entity,Go 语言与此不同,Go 开发者构建更小的类型——Customer 和 Admin,然后把这些小类型组合成更大的类型。图1-4展示了继承和组合之间的不同。

    enter image description here

    2.Go 接口对一组行为建模

    接口用于描述类型的行为。如果一个类型的实例实现了一个接口,意味着这个实例可以执行一组特定的行为。你甚至不需要去声明这个实例实现某个接口,只需要实现这组行为就好。其他的语言把这个特性叫作鸭子类型——如果它叫起来像鸭子,那它就可能是只鸭子。Go 语言的接口也是这么做的。在 Go 语言中,如果一个类型实现了一个接口的所有方法,那么这个类型的实例就可以存储在这个接口类型的实例中,不需要额外声明。

    在类似 Java 这种严格的面向对象语言中,所有的设计都围绕接口展开。在编码前,用户经常不得不思考一个庞大的继承链。下面是一个 Java 接口的例子:

    interface User {    public void login();    public void logout();}

    在 Java 中要实现这个接口,要求用户的类必须满足User接口里的所有约束,并且显式声明这个类实现了这个接口。而 Go 语言的接口一般只会描述一个单一的动作。在 Go 语言中,最常使用的接口之一是io.Reader。这个接口提供了一个简单的方法,用来声明一个类型有数据可以读取。标准库内的其他函数都能理解这个接口。这个接口的定义如下:

    type Reader interface {    Read(p []byte) (n int, err error)}

    为了实现io.Reader这个接口,你只需要实现一个Read方法,这个方法接受一个byte切片,返回一个整数和可能出现的错误。

    这和传统的面向对象编程语言的接口系统有本质的区别。Go 语言的接口更小,只倾向于定义一个单一的动作。实际使用中,这更有利于使用组合来复用代码。用户几乎可以给所有包含数据的类型实现io.Reader接口,然后把这个类型的实例传给任意一个知道如何读取io.Reader的 Go 函数。

    Go 语言的整个网络库都使用了io.Reader接口,这样可以将程序的功能和不同网络的实现分离。这样的接口用起来有趣、优雅且自由。文件、缓冲区、套接字以及其他的数据源都实现了io.Reader接口。使用同一个接口,可以高效地操作数据,而不用考虑到底数据来自哪里。

    1.1.4 内存管理

    不当的内存管理会导致程序崩溃或者内存泄漏,甚至让整个操作系统崩溃。Go 语言拥有现代化的垃圾回收机制,能帮你解决这个难题。在其他系统语言(如 C 或者 C++)中,使用内存前要先分配这段内存,而且使用完毕后要将其释放掉。哪怕只做错了一件事,都可能导致程序崩溃或者内存泄漏。可惜,追踪内存是否还被使用本身就是十分艰难的事情,而要想支持多线程和高并发,更是让这件事难上加难。虽然 Go 语言的垃圾回收会有一些额外的开销,但是编程时,能显著降低开发难度。Go 语言把无趣的内存管理交给专业的编译器去做,而让程序员专注于更有趣的事情。

    1.2 你好,Go

    感受一门语言最简单的方法就是实践。让我们看看用 Go 语言如何编写经典的Hello World!应用程序:

    enter image description here

    运行这个示例程序后会在屏幕上输出我们熟悉的一句话。但是怎么运行呢?无须在机器上安装 Go 语言,在浏览器中就可以使用几乎所有 Go 语言的功能。

    介绍 Go Playground

    Go Playground 允许在浏览器里编辑并运行Go语言代码。在浏览器中打开http://play.golang.org。浏览器里展示的代码是可编辑的(见图1-5)。点击 Run,看看会发生什么。

    enter image description here

    可以把输出的问候文字改成别的语言。试着改动fmt.Println()里面的文字,然后再次点击 Run。

    分享Go代码
      Go 开发者使用 Playground 分享他们的想法,测试理论,或者调试代码。你也可以这么做。每次使用 Playground 创建一个新程序之后,可以点击 Share 得到一个用于分享的网址。
    任何人都能打开这个链接。试试http://play.golang.org/p/EWIXicJdmz

    要给想要学习写东西或者寻求帮助的同事或者朋友演示某个想法时,Go Playground 是非常好的方式。在 Go 语言的 IRC 频道、Slack 群组、邮件列表和 Go 开发者发送的无数邮件里,用户都能看到创建、修改和分享 Go Playground 上的程序。

    1.3 小结

    • Go 语言是现代的、快速的,带有一个强大的标准库。
    • Go 语言内置对并发的支持。
    • Go 语言使用接口作为代码复用的基础模块。
    第02章:快速开始一个 Go 程序(上)
    第02章:快速开始一个 Go 程序(下)
    第03章:打包和工具链 (上)
    第03章:打包和工具链 (下)
    第04章:数组、切片和映射 (上)
    第04章:数组、切片和映射 (中)
    第04章:数组、切片和映射 (下)
    第05章:Go 语言的类型系统(上)
    第05章:Go 语言的类型系统(中)
    第05章:Go 语言的类型系统(下)
    第06章:并发(上)
    第06章:并发(中)
    第06章:并发(下)
    第07章:并发模式(上)
    第07章:并发模式(中)
    第07章:并发模式(下)
    第08章:标准库 (上)
    第08章:标准库 (中)
    第08章:标准库 (下)
    第09章:测试和性能(上)
    第09章:测试和性能(中)
    第09章:测试和性能(下)

    阅读全文: http://gitbook.cn/gitchat/column/5a1e5ce8e33e7b6b9c1b5540

    展开全文
  • Go语言实战合集

    2019-11-29 14:55:37
    go语言实战合集,包括go语言在实际生活中的一些运用实例,帮助学员快速掌握go的操作。
  • Go语言实战 .pdf

    2018-02-11 14:25:02
    Go语言实战 .pdf Go语言实战 .pdf Go语言实战 .pdf Go语言实战 .pdf
  • Go语言实战 源码

    2018-12-11 22:34:09
    Go语言实战源码。包含本书的所有章节。Go语言实战源码。包含本书的所有章节。
  • Go语言实战: 编写可维护Go语言代码建议
  • Go语言实战笔记

    万次阅读 多人点赞 2018-08-03 10:45:26
    Go语言实战笔记(一)| Go包管理 Go语言实战笔记(二)| Go开发工具 Go语言实战笔记(三)| Go Doc 文档 Go语言实战笔记(四)| Go 数组 Go语言实战笔记(五)| Go 切片 Go语言实战笔记(六)| Go Map Go语言...
    展开全文
  • Go语言实战同时关注语言的规范和实现,涉及的内容包括语法、类型系统、并发、管道、测试,以及其他一些主题。评价:Go语言通过提供整洁快速的编译代码、垃圾回收、简单的并发Go模型以及丰富的标准库来满足人们对于可...

    a1f638157840046b541d2fdd6ff04f92.png


    b6d659b3fe00048d7c1dce46e74497b2.png

    作者:(美)威廉·肯尼迪

    推荐理由:Go语言结合了底层系统语言的能力以及现代语言的高级特性,旨在降低构建简单、可靠、高效软件的门槛。本书向读者提供一个专注、全面且符合语言习惯的视角。Go语言实战同时关注语言的规范和实现,涉及的内容包括语法、类型系统、并发、管道、测试,以及其他一些主题。

    评价:Go语言通过提供整洁快速的编译代码、垃圾回收、简单的并发Go模型以及丰富的标准库来满足人们对于可扩展、高性能Web、应用的需求,这种语言非常适用于编写微服务,又或者用于构建可扩展且可维护的系统。本书将教读者运用现代化设计理念构建Go Web应用的方法。阅读本书能让读者学会如何通过依赖注入设计模式来编写测试替身,如何在Web应用中使用并发特性,还有如何在Web服务中创建以及处理JSON数据和XML数据。除此之外,读者还将学会如何尽可能地减少应用对外部框架的依赖,并了解大量与应用测试以及应用部署有关的有价值的生产技术。

    馆藏地:电工电子计算机馆索书号:TP312GO/K44
    展开全文
  • Go语言实战电子书

    2017-10-21 09:58:15
    Go语言实战电子书完整版,不知道有什么内容的可以去当当网看目录
  • go in action go 语言实战

    2017-12-03 12:02:51
    go 语言实战,英文版文字版 pdf,没有长篇大论,简练 快速入门必看。
  • 既然是Go语言实战,那看了就必须动手敲出来,先把书本中的知识点,案例看一遍,有不懂或疑惑,感觉前后无法关联起来的地方,需要翻到前面看相关的知识点,保证能理解书中主要内容,然后在自己凭着自己的理解,重新...

    既然是Go语言实战,那看了就必须动手敲出来,先把书本中的知识点,案例看一遍,有不懂或疑惑,感觉前后无法关联起来的地方,需要翻到前面看相关的知识点,保证能理解书中主要内容,然后在自己凭着自己的理解,重新实现一遍。

    案例功能


    开启多个 goroutine 去请求 xml 资源, 然后解析xml 数据并存放到 channel 中,最后将 channel 中的数据读取出来,输出到控制台中。

    实现步骤


    1. 读取本地的 data.json 文件(该文件存放了需要请求的 url 等相关信息) ,读取文件需要用到 os 包,并且读取完文件后需要及时关闭文件(defer file.Close()), 然后解析 json 文件,解析 json 文件需要使用到 encoding/json 内置包,然后需要准备一个接受解析结果的结构体 Feed。

    2. 得到 Feed 数据后,循环所有的 Feed 数据进行 http 请求获取 xml 数据,每个请求开启 goroutine,为了防止主进程提前结束,此时需要使用 sync.WaitGroup 来阻塞主进程,等待所有 goroutine 结束后,再结束主进程,而 http 请求可以使用 go 提供的内置包 net/http, 需要注意的是,使用 http 请求时,记得及时关闭请求 resp.Body.Close()

    3. http 请求到 xml 数据后,需要解析 xml 数据,解析 xml 需要用到内置包 encoding/xml, 以及准备好相应的结构体(案例解析xml使用到的结构体有:image, item, Channel, RssDocument)来接收解析结果,结构体的字段需要跟返回的xml数据一一对应,写错可能会导致解析到空的数据。

    4. 得到解析后的xml数据之后,就可以根据传入的关键字进行查找了,匹配字符串的需要使用的内置的正则包 regexp, 如果匹配成功,则把相关的内容组装好,然后 append 到通道 results 中

    5. 最后遍历通道(channel) results,输出结果到控制台

    6. 完毕

    案例全部逻辑代码


    package mainimport (  "encoding/json"  "encoding/xml"  "fmt"  "log"  "net/http"  "os"  "regexp"  "sync")type (  Feed struct {    Name string `json:"site"`    URI string `json:"link"`    Type string `json:"type"`  }  item struct {    XMLName xml.Name `xml:"item"`    Title string `xml:"title"`    Description string `xml:"description"`    PubDate string `xml:"pubDate"`    Link string `xml:"link"`    Guid string `xml:"guid"`    ContentEncoded string `xml:"content:encoded"`    DcCreator string `xml:"dc:creator"`  }  image struct {    XMLName xml.Name `xml:"image"`    Url string `xml:"url"`    Title string `xml:"title"`    Link string `xml:"link"`  }  Channel struct {    XMLName xml.Name `xml:"channel"`    Title string `xml:"title"`    Link string `xml:"link"`    Description string `xml:"description"`    Language string `xml:"language"`    Copyright string `xml:"copyright"`    Generator string `xml:"generator"`    LastBuildDate string `xml:"lastBuildDate"`    Item []item `xml:"item"`    Image image `xml:"image"`  }  RssDocument struct {    XMLName xml.Name `xml:"rss"`    Channel Channel `xml:"channel"`  }  Result struct {    Field string    Content string  })// 用于阻塞进程,等待所有 goroutine 处理完毕,再结束主进程var wg sync.WaitGroup// 存放最终的匹配的结果var results = make(chan *Result)// 读取本地文件,解构json数据func readFile(path string) ([]*Feed, error) {  file, err := os.Open(path)  if err != nil {    return nil, err  }  // 注意:打开文件之后,记得要关闭文件  defer file.Close()  // 注意:文件读取后,需要结构体来解析json数据  var files []*Feed  json.NewDecoder(file).Decode(&files)  return files, nil}// 请求需要的数据源func retrieve(feed *Feed) (*RssDocument, error) {  // http 请求数据源  resp, err := http.Get(feed.URI)  // 请求出错  if err != nil {    return nil, err  }  // 请求结束记得 close 请求  defer resp.Body.Close()  // 状态码非200  if resp.StatusCode != 200 {    return nil, fmt.Errorf("状态码是 %d\n\n", resp.StatusCode)  }  // 解析 xml 数据  var document RssDocument  xml.NewDecoder(resp.Body).Decode(&document)  return &document, nil}// 查找需要查询的字符串func Match(searchTerm string, document *RssDocument)  {  var result Result  for _, item := range document.Channel.Item {    // 匹配标题    matched, err := regexp.MatchString(searchTerm, item.Title)    if err != nil {    }    if matched {      result = Result{        Field: "Title ",        Content: item.Title,      }      results     }    // 匹配内容    matched, err = regexp.MatchString(searchTerm, item.Description)    if err != nil {    }    if matched {      result = Result{        Field: "Description",        Content: item.Description,      }      results     }  }}// 查询func run(searchTerm string, feed *Feed) {  document, err := retrieve(feed)  if err != nil {    fmt.Println("请求出错啦!", err)    return  }  // 匹配字符串  Match(searchTerm, document)}func search(searchTerm string) {  // os内置库读取本地文件,解析 json 文件成 struct  // [https://github.com/goinaction/code/blob/master/chapter2/sample/data/data.json]  path := "./data.json"  feeds, err := readFile(path)  if err != nil {    fmt.Println("读取文件出错啦!")  }  // 需要等待的 goroutine 数量  wg.Add(len(feeds))  // 开启多个goroutine 并发请求数据源, 并 Decode 数据, 然后匹配字符串后,存放到 channel results 中  for _, feed := range feeds {    go func(feed *Feed) {      run(searchTerm, feed)      // 记得处理完进程后,执行 wg.Done 让等待的进程计数减一      wg.Done()    }(feed)  }  // 等待所有 goroutine 结束后,关闭 channel  go func() {    wg.Wait()    close(results)  }()  // 2. display:   display()}// 显示搜索结果func display() {  i := 0  for result := range results {    i++    log.Printf("[%d] %s:\n%s\n\n", i, result.Field, result.Content)  }}func main() {  fmt.Println("--------Start-------\n")  search("president")  fmt.Println("--------Done--------\n")}

    相关知识点


    1、如果需要声明引用类型(映射map, 切片slice, 通道chan)后赋值,需要使用 make 的方式声明,因为如果使用 var 声明引用类型,会返回默认值 nil, 后面使用赋值会导致出错。

    // map// 错误var m map[string]stringm["a"] = "xxx"fmt.Println(m)// 正确var m = make(map[string]string)m["a"] = "xxx"fmt.Println(m)// slice// 错误var s []strings[0] = "xxx"fmt.Println(s)// 正确, 但是 slice 声明的使用需要写第二个参数来表示长度var s = make([]string, 1)s[0] = "xxx"fmt.Println(s)// 正确var s []strings = append(s, "xxx")fmt.Println(s)// chan // 无缓冲 channel , 一般配合 goroutine 使用,// 而且需要等待接收方准备好之后才能发送数据 // 正确var wg sync.WaitGroupwg.Add(1)var ch = make(chan string)go func() {  fmt.Println(  wg.Done()}()ch "x"wg.Wait()  // 有缓冲 channel// 第一个参数表示要存放的数据类型,第二个参数表示缓存长度var ch = make(chan string1)ch "xx"fmt.Println(

    2、Golang 中所有的函数参数都是以传值方式传递,引用类型也是传值方式,只是传的值有点特殊,传的是指针变量所指向的内存地址,所以在函数内改变引用类型的变量,函数外部的变量也会被改变。

    3、go 中并发只需要在函数前面加 go 关键字即可,但是使用 goroutine 时,为了避免主进程提前终止,需要使用 waitGrout 来阻塞,而 waitGrout 是在内置的 sync 包中,使用时,需要 waitGroup.Add(int), waitGroup.Done(), waitGroup.Wait() 配合使用,而waitGroup.Add(int) 中的 int值,一般要和waitGroup.Done() 的次数相等,而waitGroup.Done() 一般是放在 goroutine 中使用,如果 int 小于 waitGroup.Done() 的次数,则会导致主进程提前结束;而 int 大于 waitGroup.Done() 的次数,会导致主进程一直被阻塞,无法继续后续的代码。

    goroutine 执行并发,以及waitGroup 配合使用如下:

    package mainimport (  "fmt"  "sync")func main() {  var wg sync.WaitGroup  books := []string{"a", "b", "c"}  // len(books)  wg.Add(len(books))  for i, book := range books {    go func(i int, book string) {      fmt.Printf("执行%d %s\n", i, book)      wg.Done()    }(i, book)  }  // 阻塞主进程,等待所有 goroutine 执行完成后才继续往下执行  wg.Wait() }

    4、go中闭包的使用,for 循环和闭包一起使用时,如果不将需要的参数传进函数,而是直接使用外部函数中的变量时,可能会导致使用的都是最后一个值。

    var wg sync.WaitGroup  books := []string{"a", "b", "c"}  // len(books)  wg.Add(len(books))  for i, book := range books {    go func() {      // 执行结果      // 执行2 c      // 执行2 c      // 执行2 c      fmt.Printf("执行%d %s\n", i, book)      wg.Done()    }()  }  // 阻塞主进程,等待所有 goroutine 执行完成后才继续往下执行  wg.Wait()  

    5、接口声明最佳实践,如果是单个方法则以 er 结尾;如果需要维护状态,方法的接受者最好声明为指针;

    // 无状态的结构体,即结构体中无任何字段type Dog struct{}// 有状态的结构体type Dog struct{  Name string}


    类型的接受者为类型值或类型值指针,那么类型值还是类型值指针都可以调用方法;

    而接口类型值只能只能调用接收者为类型值的方法;接口类型值指针可以调用接收者为类型值或类型指针的方法。

    package mainimport "log"type People struct {  Name string}// 值接收者func (p People) Hi() {  log.Print("Hi...", p.Name)}type Animal struct {  Species string}// 指针接收者func (a *Animal) yell() {  log.Print("Yelling...", a.Species)}type Skill interface {  Eat()}func (p People) Eat() {  log.Println("People is eating...")}func (a *Animal) Eat() {  log.Println("Animal is eating...")}func main()  {  // 类型调用方法  // 值接收者  //s := People{"Golang"}  //s.Hi()  //s := &People{"Golang"}  //s.Hi()  // 指针接收者  //d := Animal{"Dog"}  //d.yell()  //d := &Animal{"Dog"}  //d.yell()  // 接口实例调用方法  // 值接收者  //p := People{"Golang"}  // People 实现 Skill 接口  // 值类型赋值该接口类型  //var s Skill = p  //s.Eat() // 成功  // 指针类型赋值该接口类型  //var s Skill = &p  //s.Eat() // 成功  // 指针接收者  // c := Animal{"Cat"}  // Animal 实现 Skill 接口  // 值类型赋值该接口类型  //var s Skill = c  //s.Eat() // 直接提示有错误  // 指针类型赋值该接口类型  // var s Skill = &c  // s.Eat() // 成功}// 只要记住:// 结构体类型的方法, 不管是值类型接收者,还是指针j接收者// 结构体实例都能够调用其结构体的方法// 接口类型的方法,// 值类型接收者的实例(不管是值还是指针)赋值给接口实例,接口实例都可以调用方法// 指针接收者的实例,只有实例指针赋值给接口实例,接口实例才能调用方法

    6、包中的init方法会在 main 方法之前执行,可以在这做一些初始化的工作。

    7、可以使用下划线 _ 来忽略或者当做变量的占位,来处理不需要的变量或者接收未使用的导入的包(此时会执行该包中 init 方法)

    8、通道 channel 的使用,channel 分为无缓冲和有缓冲,有只读和只写。

    package mainimport "log"func main()  {  // 无缓冲  //var ch = make(chan string)  //ch  //log.Println(  // 有缓冲,第一个参数 存放的数据类型,第二个参数 缓冲长度  var ch = make(chan string, 2)  ch"How are u ?"  ch"I'm fine, and u?"  // 读, 第一个值:消息值,第二个值:通道是否已经关闭  //msg, ok :=   //log.Println(msg, ok)   只写缓冲  //var ch = make(chan  //ch  //log.Println(  // 只读缓冲  //var ch = make(  //ch  //log.Println(  // 关闭 channel  close(ch)}

    《Go语言实战》的完整代码传送门:

    https://github.com/goinaction/code/blob/master/chapter2/sample

    更多精彩推荐

    Go语言环境搭建详解(2020版)

    Go语言 | 几种常用的排序算法图文解析

    《Go语言实战》笔记(二十八) | 后记

    《Go语言实战》笔记(一) | Go包管理

    0ba5784a5b597a966c9c00069b9808dd.png

    展开全文
  • Go语言实战.pdf

    2017-07-03 17:02:29
    Go语言实战.pdf
  • Go语言实战 高清pdf

    2018-01-25 14:42:31
    Go语言结合了底层系统语言的能力以及现代语言的高级特性,旨在降低构建简单、可靠、高效软件的门槛。...Go语言实战同时关注语言的规范和实现,涉及的内容包括语法、类型系统、并发、管道、测试,以及其他一些主题。
  • Go语言实战完整版PDF

    2017-12-26 20:44:33
    Go语言结合了底层系统语言的能力以及现代语言的高级特性,旨在降低构建简单、可靠、高效软件的门槛。...Go语言实战同时关注语言的规范和实现,涉及的内容包括语法、类型系统、并发、管道、测试,以及其他一些主题。
  • Go语言实战 文字版PDF 免费

    热门讨论 2017-08-20 15:09:06
    Go语言实战 文字版PDF 非扫描版
  • Go语言实战高清完整版,手把手教你用Go语言搭建服务器
  • Go语言实战合集 毕业于清华大学,曾担任Google算法工程师,微软区块链领...

空空如也

1 2 3 4 5 ... 20
收藏数 2,283
精华内容 913
关键字:

go语言实战