为 Java 程序员准备的 Go 入门 PPT

这是 Google 的 Go 团队技术主管经理 Sameer Ajmani 分享的 PPT,为 Java 程序员快速入门 Go 而准备的。

视频

这个 PPT 是 2015年4月23日在 NYJavaSIG 中使用的。

前往 YouTube 观看视频

主要内容

1. Go 是什么,谁在使用 Go?
2. 比较 Go 和 Java
3. 代码示例
4. 并发
5. 工具

Go 是什么?

“Go 是开源的编程语言,可以很简单的构建简单,可靠和高效的软件。”

golang.org

Go 的历史

从 2007 后半年开始设计

  • Robert Griesemer, Rob Pike 和 Ken Thompson.
  • Ian Lance Taylor 和 Russ Cox

从 2009 年开始开源,有一个非常活跃的社区。

Go 语言稳定版本 Go 1 是在 2012 年早期发布的。

为什么有 Go?

Go 是解决 Google 规模的一个解决方案。

系统规模

  • 规划的规模为 10⁶⁺ 台机器
  • 每天在几千台机器上作业
  • 在系统中与其他作业进行协作,交互
  • 同一时间进行大量工作

解决方案:对并发的支持非常强大

第二个问题:工程规模

在 2011 年

  • 跨 40+ 办公室的 5000+ 名开发者
  • 每分钟有 20+ 修改
  • 每个月修改 50% 的代码基础库
  • 每天执行 5千万的测试用例
  • 单个代码树

解决方案:为大型代码基础库二设计的语言

谁在 Google 使用 Go?

大量的项目,几千位 Go 程序员,百万行的 Go 代码。

公开的例子:

  • 移动设备的 Chrome SPDY 代理
  • Chrome, ChromeOS, Android SDK, Earth 等等的下载服务器
  • YouTube Vitess MySQL 均衡器

主要任务是网络服务器,但是这是通用的语言。

除了 Google 还有谁在使用 Go?

golang.org/wiki/GoUsers

Apcera, Bitbucket, bitly, Canonical, CloudFlare, Core OS, Digital Ocean, Docker, Dropbox, Facebook, Getty Images, GitHub, Heroku, Iron.io, Kubernetes, Medium, MongoDB services, Mozilla services, New York Times, pool.ntp.org, Secret, SmugMug, SoundCloud, Stripe, Square, Thomson Reuters, Tumblr, …

比较 Go 和 Java

Go 和 Java 有很多共同之处

  • C 系列 (强类型,括号)
  • 静态类型
  • 垃圾收集
  • 内存安全 (nil 引用,运行时边界检查)
  • 变量总是初始化 (zero/nil/false)
  • 方法
  • 接口
  • 类型断言 (实例)
  • 反射

Go 与 Java 的不同之处

  • 代码程序直接编译成机器码,没有 VM
  • 静态链接二进制
  • 内存布局控制
  • 函数值和词法闭包
  • 内置字符串 (UTF-8)
  • 内置泛型映射和数组/片段
  • 内置并发

Go 特意去掉了大量的特性

  • 没有类
  • 没有构造器
  • 没有继承
  • 没有 final
  • 没有异常
  • 没有注解
  • 没有自定义泛型

为什么 Go 要省去那些特性?

代码清晰明了是首要的

当查看代码时,可以很清晰的知道程序将会做什么

当编写代码的时候,也可以很清晰的让程序做你想做的

有时候这意味着编写出一个循环而不是调用一个模糊的函数。

(不要变的太枯燥)

详细的设计背景请看:

示例

Java程序猿对Go应该很眼熟

Main.java

  1. public class Main {
  2.     public static void main(String[] args) {
  3.         System.out.println("Hello, world!");
  4.     }
  5. }

hello.go

  1. package main
  2. import "fmt"
  3. func main() {
  4.     fmt.Println("Hello, 世界!")
  5. }

Hello, web server(你好,web服务)

package main

  1. import (
  2.     "fmt"
  3.     "log"
  4.     "net/http"
  5. )
  6. func main() {
  7.     http.HandleFunc("/hello", handleHello)
  8.     fmt.Println("serving on http://localhost:7777/hello")
  9.     log.Fatal(http.ListenAndServe("localhost:7777", nil))
  10. }
  11. func handleHello(w http.ResponseWriter, req *http.Request) {
  12.     log.Println("serving", req.URL)
  13.     fmt.Fprintln(w, "Hello, 世界!")
  14. }

(访问权限)类型根据变量名来声明。
公共变量名首字大写,私有变量首字母小写。

示例:Google搜索前端

  1. func main() {
  2.     http.HandleFunc("/search", handleSearch)
  3.     fmt.Println("serving on http://localhost:8080/search")
  4.     log.Fatal(http.ListenAndServe("localhost:8080", nil))
  5. }
  6. // handleSearch handles URLs like "/search?q=golang" by running a
  7. // Google search for "golang" and writing the results as HTML to w.
  8. func handleSearch(w http.ResponseWriter, req *http.Request) {

请求验证

  1. func handleSearch(w http.ResponseWriter, req *http.Request) {
  2.     log.Println("serving", req.URL)
  3.     // Check the search query.
  4.     query := req.FormValue("q")
  5.     if query == "" {
  6.         http.Error(w, `missing "q" URL parameter`, http.StatusBadRequest)
  7.         return
  8.     }

FormValueis 是 *http.Request 的一个方法:

  1. package http
  2. type Request struct {...}
  3. func (*Request) FormValue(key string) string {...}

query := req.FormValue(“q”)初始化变量query,其变量类型是右边表达式的结果,这里是string类型.

取搜索结果

  1.  // Run the Google search.
  2.     start := time.Now()
  3.     results, err := Search(query)
  4.     elapsed := time.Since(start)
  5.     if err != nil {
  6.         http.Error(w, err.Error(), http.StatusInternalServerError)
  7.         return
  8.     }

Search方法有两个返回值,分别为结果results和错误error.

  1. func Search(query string) ([]Result, error) {...}

当error的值为nil时,results有效。

  1. type error interface {
  2.     Error() string // a useful human-readable error message
  3. }

Error类型可能包含额外的信息,可通过断言访问。

渲染搜索结果

  1. // Render the results.
  2.     type templateData struct {
  3.         Results []Result
  4.         Elapsed time.Duration
  5.     }
  6.     if err := resultsTemplate.Execute(w, templateData{
  7.         Results: results,
  8.         Elapsed: elapsed,
  9.     }); err != nil {
  10.         log.Print(err)
  11.         return
  12.     }

结果results使用Template.Execute生成HTML,并存入一个io.Writer:

  1. type Writer interface {
  2.         Write([]byte) (int, err error)
  3. }

http.ResponseWriter实现了io.Writer接口。

Go变量操作HTML模板

  1. // A Result contains the title and URL of a search result.
  2. type Result struct {
  3.     Title, URL string
  4. }
  5. var resultsTemplate = template.Must(template.New("results").Parse(`
  6. <html>
  7. <head/>
  8. <body>
  9.   <ol>
  10.   {{range .Results}}
  11.     <li>{{.Title}} - <a href="{{.URL}}">{{.URL}}</a></li>
  12.   {{end}}
  13.   </ol>
  14.   <p>{{len .Results}} results in {{.Elapsed}}</p>
  15. </body>
  16. </html>
  17. `))

请求Google搜索API

  1. func Search(query string) ([]Result, error) {
  2.     // Prepare the Google Search API request.
  3.     u, err := url.Parse("https://ajax.googleapis.com/ajax/services/search/web?v=1.0")
  4.     if err != nil {
  5.         return nil, err
  6.     }
  7.     q := u.Query()
  8.     q.Set("q", query)
  9.     u.RawQuery = q.Encode()
  10.     // Issue the HTTP request and handle the response.
  11.     resp, err := http.Get(u.String())
  12.     if err != nil {
  13.         return nil, err
  14.     }
  15.     defer resp.Body.Close()

defer声明使resp.Body.Close运行在Search方法返回时。

解析返回的JSON数据到Go struct类型

developers.google.com/web-search/docs/#fonje

  1.   var jsonResponse struct {
  2.         ResponseData struct {
  3.             Results []struct {
  4.                 TitleNoFormatting, URL string
  5.             }
  6.         }
  7.     }
  8.     if err := json.NewDecoder(resp.Body).Decode(&jsonResponse); err != nil {
  9.         return nil, err
  10.     }
  11.     // Extract the Results from jsonResponse and return them.
  12.     var results []Result
  13.     for _, r := range jsonResponse.ResponseData.Results {
  14.         results = append(results, Result{Title: r.TitleNoFormatting, URL: r.URL})
  15.     }
  16.     return results, nil
  17. }

这就是它的前端

所有引用的包都来自标准库:

  1. import (
  2.     "encoding/json"
  3.     "fmt"
  4.     "html/template"
  5.     "log"
  6.     "net/http"
  7.     "net/url"
  8.     "time"
  9. )

Go服务器规模:每一个请求都运行在自己的goroutine里。

让我们谈谈并发。

通信顺序进程(Hoare,1978)

并发程序作为独立进程,通过信息交流的顺序执行。

顺序执行很容易理解,异步则不是。

“不要为共亨内存通信,为通信共享内存。”

Go原理: goroutines, channels, 和 select声明.

Goroutines

Goroutines 就像轻量级线程。

它们通过小栈(tiny stacks)和按需调整运行。

Go 程序可以拥有成千上万个(goroutines)实例

使用go声明启动一个goroutines:

  1. go f(args)

Go运行时把goroutines放进OS线程里。

不要使用线程堵塞goroutines。

Channels

Channels被定义是为了与goroutines之间通信。

  1. := make(chan string)
  2.  
  3. // goroutine 1
  4. <- "hello!"
  5.  
  6. // goroutine 2
  7. := <-c
  8. fmt.Println(s) // "hello!"

Select

select声明一个语句块来判断执行。

  1. select {
  2. case n := <-in:
  3.   fmt.Println("received", n)
  4. case out <- v:
  5.   fmt.Println("sent", v)
  6. }

只有条件成立的case块会运行。

示例:Google搜索(后端)

问: Google搜索能做些什么?

答: 提出一个问题,它可以返回一个搜索结果的页面(和一些广告)。

问: 我们怎么得到这些搜索结果?

答: 发送一个问题到网页搜索、图片搜索、YouTube(视频)、地图、新闻,稍等然后检索出结果。

我们该怎么实现它?

Google搜索 : 一个假的框架

We can simulate a Search function with a random timeout up to 100ms.

我们要模拟一个搜索函数,让它随机超时0到100毫秒。

  1. var (
  2.     Web   = fakeSearch("web")
  3.     Image = fakeSearch("image")
  4.     Video = fakeSearch("video")
  5. )
  6. type Search func(query string) Result
  7. func fakeSearch(kind string) Search {
  8.     return func(query string) Result {
  9.         time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
  10.         return Result(fmt.Sprintf("%s result for %q/n", kind, query))
  11.     }
  12. }

Google搜索: 测试框架

  1. func main() {
  2.     start := time.Now()
  3.     results := Google("golang")
  4.     elapsed := time.Since(start)
  5.     fmt.Println(results)
  6.     fmt.Println(elapsed)
  7. }

Google搜索 (顺序)

Google函数获取一个查询,然后返回一个的结果集 (不一定是字符串).

Google按顺序调用Web(网页)、Image(图片)、Video(视频)并将返回加入到结果集中。

  1. func Google(query string) (results []Result) {
  2.     results = append(results, Web(query))
  3.     results = append(results, Image(query))
  4.     results = append(results, Video(query))
  5.     return
  6. }

Google搜索(并行)

同时执行 Web,、Image、 和Video搜索,并等待所有结果。

func方法是在query和c的地方关闭的。

  1. func Google(query string) (results []Result) {
  2. c := make(chan Result)
  3. go func() { c <- Web(query) }()
  4. go func() { c <- Image(query) }()
  5. go func() { c <- Video(query) }()
  6. for i := 0; i < 3; i++ {
  7. result := <-c
  8. results = append(results, result)
  9. }
  10. return
  11. }

Google搜索 (超时)

等待慢的服务器。

没有锁,没有条件变量,没有返回值。

  1.     c := make(chan Result, 3)
  2.     go func() { c <- Web(query) }()
  3.     go func() { c <- Image(query) }()
  4.     go func() { c <- Video(query) }()
  5.     timeout := time.After(80 * time.Millisecond)
  6.     for i := 0; i < 3; i++ {
  7.         select {
  8.         case result := <-c:
  9.             results = append(results, result)
  10.         case <-timeout:
  11.             fmt.Println("timed out")
  12.             return
  13.         }
  14.     }
  15.     return

防止超时

问: 如何防止丢掉慢的服务的结果?

答: 复制这个服务,然后发送请求到多个复制的服务,并使用第一个响应的结果。

  1. func First(query string, replicas ...Search) Result {
  2.     c := make(chan Result, len(replicas))
  3.     searchReplica := func(int) { c <- replicas[i](query) }
  4.     for i := range replicas {
  5.         go searchReplica(i)
  6.     }
  7.     return <-c
  8. }

使用First函数

  1. func main() {
  2.     start := time.Now()
  3.     result := First("golang",
  4.         fakeSearch("replica 1"),
  5.         fakeSearch("replica 2"))
  6.     elapsed := time.Since(start)
  7.     fmt.Println(result)
  8.     fmt.Println(elapsed)
  9. }

Google搜索 (复制)

使用复制的服务以减少多余延迟。

  1.     c := make(chan Result, 3)
  2.     go func() { c <- First(query, Web1, Web2) }()
  3.     go func() { c <- First(query, Image1, Image2) }()
  4.     go func() { c <- First(query, Video1, Video2) }()
  5.     timeout := time.After(80 * time.Millisecond)
  6.     for i := 0; i < 3; i++ {
  7.         select {
  8.         case result := <-c:
  9.             results = append(results, result)
  10.         case <-timeout:
  11.             fmt.Println("timed out")
  12.             return
  13.         }
  14.     }
  15.     return

其他

没有锁,没有条件变量,没有调用。

总结

经过一些简单转换,我们使用 Go 的并发原语来转换一个

  • 顺序性的
  • 故障敏感的

程序为一个

  • 并发
  • 可复用的
  • 健壮的

工具

Go 有很多强大的工具

  • gofmt 和 goimports
  • The go tool
  • godoc
  • IDE 和编辑器支持

这语言就是为工具链设计的。

gofmt 和 goimports

Gofmt 可以自动格式化代码,没有选项。

Goimports 基于你的工作空间更新导入声明

大部分人可以安全的使用这些工具。

play.golang.org/p/GPqra77cBK

The go tool

The go tool 可以在一个传统目录布局中用源代码构建 Go 程序。不需要 Makefiles 或者其他配置。

匹配这些工具及其依赖,然后进行构建,安装:

  1. % go get golang.org/x/tools/cmd/present

运行:

  1. % present

godoc

为世界上所有的开源 Go 代码生成文档:

godoc.org

IDE 和编辑器支持

Eclipse, IntelliJ, emacs, vim 等等:

  • gofmt
  • goimports
  • godoclookups
  • code completion
  • code navigation

但是没有 “Go IDE”.

Go 工具无处不在。

Go 的下一步计划

Go 路线在线查看

tour.golang.org

大量的学习资料

golang.org/wiki/Learn

完美的社区

golang.org/project

为 Java 程序员准备的 Go 入门 PPT的更多相关文章

  1. 来自Java程序员的Python新手入门小结

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  2. Java程序员之Spring(一) 入门

    一. Spring 原理讲解  Spring 是一个轻量容器框架(开源):Spring的核心是 IoC(控制反转) 和 AOP(面向切面编程): Spring 由7个模块组成: Spring Core ...

  3. Java程序员之JS(一) 入门

    背景:使用了JS做一个 WEB 项目之后,一直有使用JS 的一个功能,突然某一天项目重新规划,开始自己手动写一些原始JS,情况不妙,原来之前一直是用同事搭建好的框架在开发,对 JS 零基础的我一直在 ...

  4. Java 程序员的大数据入门指南

    项目 GitHub 地址:https://github.com/heibaiying/BigData-Notes ✒️ 前 言 大数据常用技术栈思维导图 大数据常用软件安装指南 一.Hadoop 分布 ...

  5. Java程序员快速入门Go语言

    这篇文章帮助Java程序员快速入门Go语言. 转载至 开源中国社区. http://www.oschina.net 本文将以一个有代表性的例子为开始,以此让Java程序员对Go语言有个初步认识,随后将 ...

  6. java程序员入门:英语好不好对编程到底有没有影响

    我想当码农,听说钱钱拿的多! 哦.是很有钱!么样? 可是我不会! 那你想么样?去学撒! 可是,我英语差-- 有多差??? 很差-- 那????? 关于英语水平对编程的影响,我们一起来看看啦!希望可以解 ...

  7. Efficient&Elegant:Java程序员入门Cpp

    最近项目急需C++ 的知识结构,虽说我有过快速学习很多新语言的经验,但对于C++ 老特工我还需保持敬畏(内容太多),本文会从一个Java程序员的角度,制定高效学习路线快速入门C++ . Java是为了 ...

  8. Java程序员的Golang入门指南(下)

    Java程序员的Golang入门指南(下) 4.高级特性 上面介绍的只是Golang的基本语法和特性,尽管像控制语句的条件不用圆括号.函数多返回值.switch-case默认break.函数闭包.集合 ...

  9. Java程序员的Golang入门指南(上)

    Java程序员的Golang入门指南 1.序言 Golang作为一门出身名门望族的编程语言新星,像豆瓣的Redis平台Codis.类Evernote的云笔记leanote等. 1.1 为什么要学习 如 ...

随机推荐

  1. 安卓基础之通过Intent跳转Activity

    通过Intent跳转Activity   一.通过意图开启Activity的方式:   隐式意图:通过指定一组数据或者动作实现 Intent intent=new Intent(); intent.s ...

  2. UDK命令

    UDK命令行参数与控制台命令都是大小写不敏感的 命令行  udn中文  udn英文 全词大小写匹配,正则表达式,在c++代码中搜索减号开头的命令行参数(如:-BENCHMARK.-onethread等 ...

  3. ASP.NET Zero--后端应用程序

    后端应用程序 这是用户名和密码输入的实际应用程序.您将主要在此应用程序上添加您的业务需求. 应用文件夹 后端应用程序默认内置在专用区域,名为“ App ”,但可以在创建解决方案时确定.因此,所有控制器 ...

  4. CSS盒子模型(Box Model)

    一.背景 作为CSS的重点,三大模块之一的盒子模型,这部分无论如何也要精通透彻.在任何一个网页当中,都有自己的布局方式,所谓网页布局方式就是如何把网页里面的文字.图片,很好的排版成美工设计的样式,这时 ...

  5. Threading.Timer用法

    protected System.Threading.Timer executeTimer;//定时器 private int interval;//定时器执行间隔周期 executeTimer = ...

  6. SQL Server数据库————连接查询和分组查询

    SQL Server数据库————连接查询和分组查询 分组查询 select 列from  <表名> where  …… group by  列 注意:跟order  by一样group ...

  7. 在Visual Studio 2017上配置并使用OpenGL

    在Visual Studio 2017上配置并使用OpenGL 作者:凯鲁嘎吉 - 博客园 http://www.cnblogs.com/kailugaji/ 首先在Windows下安装Visual ...

  8. .NET CORE学习笔记系列(5)——ASP.NET CORE的运行原理解析

    一.概述 在ASP.NET Core之前,ASP.NET Framework应用程序由IIS加载.Web应用程序的入口点由InetMgr.exe创建并调用托管,初始化过程中触发HttpApplicat ...

  9. JAVA常用API的总结(2)

    这篇是常用API的结束了,写完的话可以继续往后复习了. 1.基本类型包装类的介绍与相关代码实现 对于数据包装类的特点就是可以将基本数据类型与字符串来回切换,接下来我会通过介绍Integer类的形式,来 ...

  10. 通过C#学Proto.Actor模型》之Remote

    Proto.Actor中提供了基于tcp/ip的通迅来实现Remote,可以通过其Remot实现对Actor的调用. 先来看一个极简单片的远程调用. 码友看码: 引用NuGet包 Proto.Acto ...