蚊章汇 - golang
type: channel
link: https://linuxcn.bwsl.wang/tag/golang/
items: 使用 gosec 检查 Go 代码中的安全问题
items: 学习 Golang 的 4 个技巧
items: 从零开始学习 Go 语言的切片
items: OpenGL 与 Go 教程（三）实现游戏
items: OpenGL 与 Go 教程（二）绘制游戏面板
items: 论 HTTP 性能，Go 与 .NET Core 一争雌雄
items: 从 Node 到 Go：一个粗略的比较
items: 在 Go 中如何使用切片的容量和长度
items: 如何在 Docker 中设置 Go 并部署应用

使用 gosec 检查 Go 代码中的安全问题
type: item
date: 2020-10-04T20:51:53+08:00
description: 来学习下 Go 语言的安全检查工具 gosec。Go 语言写的代码越来越常见，尤其是在容器、Kubernetes 或云生态相关的开发中。Docker 是最早采用 Golang 的项目之一，随后是 Kubernetes，之后大量的新项目在众多编程语言中选择了 Go。像其他语言一样，Go 也有它的长处和短处（如安全缺陷）。这些缺陷可能会因为语言本身的缺陷加上程序员编码不当而产生，例如，C 代码中的内存安全问题。无论它们出现的原因是什么，安全问题都应该在开发过程的早期修复，以免在封装好的软件中出现。幸运的是，静态分析工具可以帮你以更可重复的方式处理这些问题。静态分析工具通过解析用某种编程语言写的代码来找到问题。这类工具中很多被称为 linter。传统意义上，linter 更注重的是检查代码中编码问题、bug、代码风格之类的问题，它们可能不会发现代码中的安全问题。例如，Coverity 是一个很流行的工具，它可以帮助寻找 C/C++ 代码中的问题。然而，也有一些工具专门用来检查源码中的安全问题。例如，Bandit 可以检查 Python 代码中的安全缺陷。而 gosec 则用来搜寻 Go 源码中的安全缺陷。gosec 通过扫描 Go 的 AST（ 抽象语法树 abstract syntax tree ）来检查源码中的安全问题。开始使用 gosec在开始学习和使用 gosec 之前，你需要准备一个 Go 语言写的项目。有这么多开源软件，我相信这不是问题。你可以在 GitHub 的 热门 Golang 仓库中找一个。本文中，我随机选了 Docker CE 项目，但你可以选择任意的 Go 项目。安装 Go 和 gosec如果你还没安装 Go，你可以先从仓库中拉取下来。如果你用的是 Fedora 或其他基于 RPM 的 Linux 发行版本：$ dnf install golang.x86_64 如果你用的是其他操作系统，请参照 Golang 安装页面。使用 version 参数来验证 Go 是否安装成功：$ go version go version go1.14.6 linux/amd64 运行 go get 命令就可以轻松地安装 gosec：$ go get github.com/securego/gosec/cmd/gosec 上面这行命令会从 GitHub 下载 gosec 的源码，编译并安装到指定位置。在仓库的 README 中你还可以看到安装该工具的其他方法。gosec 的源码会被下载到 $GOPATH 的位置，编译出的二进制文件会被安装到你系统上设置的 bin 目录下。你可以运行下面的命令来查看 $GOPATH 和 $GOBIN 目录：$ go env | grep GOBIN GOBIN="/root/go/gobin" $ go env | grep GOPATH GOPATH="/root/go" 如果 go get 命令执行成功，那么 gosec 二进制应该就可以使用了：$ ls -l ~/go/bin/ total 9260 -rwxr-xr-x. 1 root root 9482175 Aug 20 04:17 gosec 你可以把 $GOPATH 下的 bin 目录添加到 $PATH 中。这样你就可以像使用系统上的其他命令一样来使用 gosec 命令行工具（CLI）了。$ which gosec /root/go/bin/gosec $ 使用 gosec 命令行工具的 -help 选项来看看运行是否符合预期：$ gosec -help gosec - Golang security checker gosec analyzes Go source code to look for common programming mistakes that can lead to security problems. VERSION: dev GIT TAG: BUILD DATE: USAGE: 之后，创建一个目录，把源码下载到这个目录作为实例项目（本例中，我用的是 Docker CE）：$ mkdir gosec-demo $ cd gosec-demo/ $ pwd /root/gosec-demo $ git clone https://github.com/docker/docker-ce.git Cloning into 'docker-ce'... remote: Enumerating objects: 1271, done. remote: Counting objects: 100% (1271/1271), done. remote: Compressing objects: 100% (722/722…

学习 Golang 的 4 个技巧
type: item
date: 2018-11-24T20:21:37+08:00
description: 到达 Golang 大陆：一位资深开发者之旅。2014 年夏天……IBM：“我们需要你弄清楚这个 Docker。”我：“没问题。”IBM：“那就开始吧。”我：“好的。”（内心声音）：”Docker 是用 Go 编写的。是吗？“（Google 一下）“哦，一门编程语言。我在我的岗位上已经学习了很多了。这不会太难。”我的大学新生编程课是使用 VAX 汇编程序教授的。在数据结构课上，我们使用 Pascal —— 在图书馆计算机中心的旧电脑上使用软盘加载。在一门更高一级的课程中，我的教授教授喜欢用 ADA 去展示所有的例子。在我们的 Sun 工作站上，我通过各种 UNIX 的实用源代码学到了一点 C。在 IBM，OS/2 源代码中我们使用了 C 和一些 x86 汇编程序；在一个与 Apple 合作的项目中我们大量使用 C++ 的面向对象功能。不久后我学到了 shell 脚本，开始是 csh，但是在 90 年代中期发现 Linux 后就转到了 Bash。在 90 年代后期，我在将 IBM 的定制的 JVM 代码中的即时（JIT）编译器移植到 Linux 时，我不得不开始学习 m4（与其说是编程语言，不如说是一种宏处理器）。一晃 20 年……我从未因为学习一门新的编程语言而焦灼。但是 Go 让我感觉有些不同。我打算公开贡献，上传到 GitHub，让任何有兴趣的人都可以看到！作为一个 40 多岁的资深开发者的 Go 新手，我不想成为一个笑话。我们都知道程序员的骄傲，不想丢人，不论你的经验水平如何。我早期的调研显示，Go 似乎比某些语言更 “地道”。它不仅仅是让代码可以编译；也需要让代码可以 “Go Go Go”。现在，我的个人的 Go 之旅四年间有了几百个拉取请求（PR），我不是致力于成为一个专家，但是现在我觉得贡献和编写代码比我在 2014 年的时候更舒服了。所以，你该怎么教一个老人新的技能或者一门编程语言呢？以下是我自己在前往 Golang 大陆之旅的四个步骤。1、不要跳过基础虽然你可以通过复制代码来进行你早期的学习（谁还有时间阅读手册！？），Go 有一个非常易读的 语言规范，它写的很易于理解，即便你在语言或者编译理论方面没有取得硕士学位。鉴于 Go 的 参数：类型 顺序的特有习惯，以及一些有趣的语言功能，例如通道和 go 协程，搞定这些新概念是非常重要的是事情。阅读这个附属的文档 高效 Go 编程，这是 Golang 创造者提供的另一个重要资源，它将为你提供有效和正确使用语言的准备。2、从最好的中学习有许多宝贵的资源可供挖掘，可以将你的 Go 知识提升到下一个等级。最近在 GopherCon 上的所有讲演都可以在网上找到，如这个 GopherCon US 2018 的详尽列表。这些讲演的专业知识和技术水平各不相同，但是你可以通过它们轻松地找到一些你所不了解的事情。Francesc Campoy 创建了一个名叫 JustForFunc 的 Go 编程视频系列，其不断增多的剧集可以用来拓宽你的 Go 知识和理解。直接搜索 “Golang" 可以为那些想要了解更多信息的人们展示许多其它视频和在线资源。想要看代码？在 GitHub 上许多受欢迎的云原生项目都是用 Go 写的：Docker/Moby、Kubernetes、Istio、containerd、CoreDNS，以及许多其它的。语言纯粹主义者可能会认为一些项目比另外一些更地道，但这些都是很好的起点，可以看到在高度活跃的项目的大型代码库中使用 Go 的程度。3、使用优秀的语言工具你会很快了解到 gofmt 的宝贵之处。Go 最漂亮的一个地方就在于没有关于每个项目代码格式的争论 —— gofmt 内置在语言的运行环境中，并且根据一系列可靠的、易于理解的语言规则对 Go 代码进行格式化。我不知道有哪个基于 Golang 的项目会在持续集成中不坚持使用 gofmt 检查拉取请求。除了直接构建于运行环境和 SDK 中的一系列有价值的工具之外，我强烈建议使用一个对 Golang 的特性有良好支持的编辑器或者 IDE。由于我经常在命令行中进行工作，我依赖于 Vim 加上强大的 vim-go 插件。我也喜欢微软提供的 VS Code，特别是它的 Go 语言 插件。想要一个调试器？Delve 项目在不断的改进和成熟，它是在 Go 二进制文件上进行 gdb 式调试的强有力的竞争者。4、写一些代码你要是不开始尝试使用 Go 写代码，你永远不知道它有什么好的地方。找一个有 “需要帮助” 问题标签的项目，然后开始贡献代码。如果你已经使用了一个用 Go 编写的开源项目，找出它是否有一些可以用初学者方式解决的 Bug，然后开始你的第一个拉取请求。与生活中的大多数事情一样，实践出真知，所以开始吧。事实证明，你可以教会…

从零开始学习 Go 语言的切片
type: item
date: 2018-08-20T05:59:00+08:00
description: 这篇文章受到了我与同事讨论使用 切片 slice 作为 栈 stack 的一次聊天的启发。后来话题聊到了 Go 语言中的切片是如何工作的。我认为这些信息对别人也有用，所以就把它记录了下来。数组任何关于 Go 语言切片的讨论都要从另一个数据结构也就是 数组 array 开始。Go 的数组有两个特性：数组的长度是固定的；[5]int 是由 5 个 int 构成的数组，和 [3]int 不同。数组是值类型。看下面这个示例：package main import "fmt" func main() { var a [5]int b := a b[2] = 7 fmt.Println(a, b) // prints [0 0 0 0 0] [0 0 7 0 0] }语句 b := a 定义了一个类型是 [5]int 的新变量 b，然后把 a 中的内容 复制 到 b 中。改变 b 对 a 中的内容没有影响，因为 a 和 b 是相互独立的值。 1 切片Go 语言的切片和数组的主要有如下两个区别：切片没有一个固定的长度。切片的长度不是它类型定义的一部分，而是由切片内部自己维护的。我们可以使用内置的 len 函数知道它的长度。 2 将一个切片赋值给另一个切片时 不会 对切片内容进行复制操作。这是因为切片没有直接持有其内部数据，而是保留了一个指向 底层数组 3 的指针。数据都保留在底层数组里。基于第二个特性，两个切片可以享有共同的底层数组。看下面的示例：对切片取切片package main import "fmt" func main() { var a = []int{1,2,3,4,5} b := a[2:] b[0] = 0 fmt.Println(a, b) // prints [1 2 0 4 5] [0 4 5] }在这个例子里，a 和 b 享有共同的底层数组 —— 尽管 b 在数组里的起始偏移量不同，两者的长度也不同。通过 b 修改底层数组的值也会导致 a 里的值的改变。将切片传进函数package main import "fmt" func negate(s []int) { for i := range s { s[i] = -s[i] } } func main() { var a = []int{1, 2, 3, 4, 5} negate(a) fmt.Println(a) // prints [-1 -2 -3 -4 -5] }在这个例子里，a 作为形参 s 的实参传进了 negate 函数，这个函数遍历 s 内的元素并改变其符号。尽管 nagate 没有返回值，且没有访问到 main 函数里的 a。但是当将之传进 negate 函数内时，a 里面的值却被改变了。大多数程序员都能直观地了解 Go 语言切片的底层数组是如何工作的，因为它与其它语言中类似数组的工作方式类似。比如下面就是使用 Python 重写的这一小节的第一个示例：Python 2.7.10 (default, Feb 7 2017, 00:08:15) [GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.34)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> a = [1,2,3,4,5] >>> b = a >>> b[2] = 0 >>> a [1, 2, 0, 4, 5]以及使用 Ruby 重写的版本:irb(main):001:0> a = [1,2,3,4,5] => [1, 2, 3, 4, 5] irb(main):002:0> b = a => [1, 2, 3, 4, 5] irb(main):003:0> b[2] = 0 => 0 irb(main):004:0> a => [1, 2, 0, 4, 5]在大多数将数组视为对象或者是引用类型的语言也是如此。 4 切片头切片同时拥有值和指针特性的神奇之处在于理解切片实际上是一个 结构体 struct 类型。通常在 反射 reflect 包内相应部分之后的这个结构体被称作 切片头 slice header 。切片头的定义大致如下：package runtime type slice struct { ptr unsafe.Pointer len int cap int }这很重要，因为和 map 以及 chan 这两个类型不同，切片是值类型，当被赋值或者被作为参数传入函数时候会被复制过去。程序员们都能理解 square 的形参 v 和 main 中声明的 v 的是相互独立的。请看下面的例子：package main …

OpenGL 与 Go 教程（三）实现游戏
type: item
date: 2017-10-18T17:43:00+08:00
description: 第一节: Hello, OpenGL第二节: 绘制游戏面板第三节：实现游戏功能该教程的完整源代码可以从 GitHub 上找到。欢迎回到《OpenGL 与 Go 教程》！如果你还没有看过 第一节 和 第二节)，那就要回过头去看一看。到目前为止，你应该懂得如何创建网格系统以及创建代表方格中每一个单元的格子阵列。现在可以开始把网格当作游戏面板实现 康威生命游戏 Conway's Game of Life 。开始吧！实现康威生命游戏康威生命游戏的其中一个要点是所有 细胞 cell 必须同时基于当前细胞在面板中的状态确定下一个细胞的状态。也就是说如果细胞 (X=3,Y=4) 在计算过程中状态发生了改变，那么邻近的细胞 (X=4,Y=4) 必须基于 (X=3,Y=4) 的状态决定自己的状态变化，而不是基于自己现在的状态。简单的讲，这意味着我们必须遍历细胞，确定下一个细胞的状态，而在绘制之前不改变他们的当前状态，然后在下一次循环中我们将新状态应用到游戏里，依此循环往复。为了完成这个功能，我们需要在 cell 结构体中添加两个布尔型变量：type cell struct { drawable uint32 alive bool aliveNext bool x int y int } 这里我们添加了 alive 和 aliveNext，前一个是细胞当前的专题，后一个是经过计算后下一回合的状态。现在添加两个函数，我们会用它们来确定 cell 的状态：// checkState 函数决定下一次游戏循环时的 cell 状态 func (c *cell) checkState(cells [][]*cell) { c.alive = c.aliveNext c.aliveNext = c.alive liveCount := c.liveNeighbors(cells) if c.alive { // 1. 当任何一个存活的 cell 的附近少于 2 个存活的 cell 时，该 cell 将会消亡，就像人口过少所导致的结果一样 if liveCount < 2 { c.aliveNext = false } // 2. 当任何一个存活的 cell 的附近有 2 至 3 个存活的 cell 时，该 cell 在下一代中仍然存活。 if liveCount == 2 || liveCount == 3 { c.aliveNext = true } // 3. 当任何一个存活的 cell 的附近多于 3 个存活的 cell 时，该 cell 将会消亡，就像人口过多所导致的结果一样 if liveCount > 3 { c.aliveNext = false } } else { // 4. 任何一个消亡的 cell 附近刚好有 3 个存活的 cell，该 cell 会变为存活的状态，就像重生一样。 if liveCount == 3 { c.aliveNext = true } } } // liveNeighbors 函数返回当前 cell 附近存活的 cell 数 func (c *cell) liveNeighbors(cells [][]*cell) int { var liveCount int add := func(x, y int) { // If we're at an edge, check the other side of the board. if x == len(cells) { x = 0 } else if x == -1 { x = len(cells) - 1 } if y == len(cells[x]) { y = 0 } else if y == -1 { y = len(cells[x]) - 1 } if cells[x][y].alive { liveCount++ } } add(c.x-1, c.y) // To the left add(c.x+1, c.y) // To the right add(c.x, c.y+1) // up add(c.x, c.y-1) // down add(c.x-1, c.y+1) // top-left add(c.x+1, c.y+1) // top-right add(c.x-1, c.y-1) // bottom-left add(c.x+1, c.y-1) // bottom-right return liveCount } 在 checkState 中我们设置当前状态（alive） 等于我们最近迭代结果（aliveNext）。接下来我们计数邻居数量，并根据游戏的规则来决定 aliveNext 状态。该规则是比较清晰的，而且我们在上面的代码当中也有说明，所…

OpenGL 与 Go 教程（二）绘制游戏面板
type: item
date: 2017-10-08T06:35:00+08:00
description: 第一节: Hello, OpenGL第二节: 绘制游戏面板第三节：实现游戏功能这篇教程的所有源代码都可以在 GitHub 上找到。欢迎回到《OpenGL 与 Go 教程》。如果你还没有看过第一节，那就要回过头去看看那一节。你现在应该能够创造一个漂亮的白色三角形，但我们不会把三角形当成我们游戏的基本单元，是时候把三角形变成正方形了，然后我们会做出一个完整的方格。让我们现在开始做吧！利用三角形绘制方形在我们绘制方形之前，先把三角形变成直角三角形。打开 main.go 文件，把 triangle 的定义改成像这个样子：triangle = []float32{ -0.5, 0.5, 0, -0.5, -0.5, 0, 0.5, -0.5, 0, } 我们做的事情是，把最上面的顶点 X 坐标移动到左边（也就是变为 -0.5），这就变成了像这样的三角形：很简单，对吧？现在让我们用两个这样的三角形顶点做成正方形。把 triangle 重命名为 square，然后添加第二个倒置的三角形的顶点数据，把直角三角形变成这样的：square = []float32{ -0.5, 0.5, 0, -0.5, -0.5, 0, 0.5, -0.5, 0, -0.5, 0.5, 0, 0.5, 0.5, 0, 0.5, -0.5, 0, } 注意：你也要把在 main 和 draw 里面命名的 triangle 改为 square。我们通过添加三个顶点，把顶点数增加了一倍，这三个顶点就是右上角的三角形，用来拼成方形。运行它看看效果：很好，现在我们能够绘制正方形了！OpenGL 一点都不难，对吧？在窗口中绘制方形格子现在我们能画一个方形，怎么画 100 个吗？我们来创建一个 cell 结构体，用来表示格子的每一个单元，因此我们能够很灵活的选择绘制的数量：type cell struct { drawable uint32 x int y int } cell 结构体包含一个 drawable 属性，这是一个顶点数组对象，就像我们在之前创建的一样，这个结构体还包含 X 和 Y 坐标，用来表示这个格子的位置。我们还需要两个常量，用来设定格子的大小和形状：const ( ... rows = 10 columns = 10 ) 现在我们添加一个创建格子的函数：func makeCells() [][]*cell { cells := make([][]*cell, rows, rows) for x := 0; x < rows; x++ { for y := 0; y < columns; y++ { c := newCell(x, y) cells[x] = append(cells[x], c) } } return cells } 这里我们创建多维的 切片 slice ，代表我们的游戏面板，用名为 newCell 的新函数创建的 cell 来填充矩阵的每个元素，我们待会就来实现 newCell 这个函数。在接着往下阅读前，我们先花一点时间来看看 makeCells 函数做了些什么。我们创造了一个切片，这个切片的长度和格子的行数相等，每一个切片里面都有一个 细胞 cell 的切片，这些细胞的数量与列数相等。如果我们把 rows 和 columns 都设定成 2，那么就会创建如下的矩阵：[ [cell, cell], [cell, cell] ] 还可以创建一个更大的矩阵，包含 10x10 个细胞：[ [cell, cell, cell, cell, cell, cell, cell, cell, cell, cell], [cell, cell, cell, cell, cell, cell, cell, cell, cell, cell], [cell, cell, cell, cell, cell, cell, cell, cell, cell, cell], [cell, cell, cell, cell, cell, cell, cell, cell, cell, cell], [cell, cell, cell, cell, cell, cell, cell, cell, cell, cell], [cell, cell, cell, cell, cell, cell, cell, cell, cell, cell], [cell, cell, cell, cell, cell, cell, cell, cell, cell, cell], [cell, cell, cell, cell, cell, cell, cell, cell, cell, cell], [cell, cell, cell, cell, cell, cell, cell,…

论 HTTP 性能，Go 与 .NET Core 一争雌雄
type: item
date: 2017-10-06T19:13:00+08:00
description: 朋友们，你们好！近来，我听到了大量的关于新出的 .NET Core 和其性能的讨论，尤其在 Web 服务方面的讨论更甚。因为是新出的，我不想立马就比较两个不同的东西，所以我耐心等待，想等发布更稳定的版本后再进行。本周一（8 月 14 日），微软发布 .NET Core 2.0 版本，因此，我准备开始。您们认为呢？如前面所提的，我们会比较它们相同的东西，比如应用程序、预期响应及运行时的稳定性，所以我们不会把像对 JSON 或者 XML 的编码、解码这些烦多的事情加入比较游戏中来，仅仅只会使用简单的文本消息。为了公平起见，我们会分别使用 Go 和 .NET Core 的 MVC 架构模式。参赛选手Go （或称 Golang）： 是一种快速增长的开源编程语言，旨在构建出简单、快捷和稳定可靠的应用软件。用于支持 Go 语言的 MVC web 框架并不多，还好我们找到了 Iris ，可胜任此工作。Iris： 支持 Go 语言的快速、简单和高效的微型 Web 框架。它为您的下一代网站、API 或分布式应用程序奠定了精美的表现方式和易于使用的基础。C#： 是一种通用的、面向对象的编程语言。其开发团队由 Anders Hejlsberg 领导。.NET Core： 跨平台，可以在极少时间内开发出高性能的应用程序。可从 https://golang.org/dl下载Go，从https://www.microsoft.com/net/core 下载 .NET Core。在下载和安装好这些软件后，还需要为 Go 安装 Iris。安装很简单，仅仅只需要打开终端，然后执行如下语句：go get -u github.com/kataras/iris 基准硬件处理器： Intel(R) Core(TM) i7–4710HQ CPU @ 2.50GHz 2.50GHz内存： 8.00 GB软件操作系统： 微软 Windows [10.0.15063 版本]， 电源计划设置为“高性能”HTTP 基准工具： https://github.com/codesenberg/bombardier， 使用最新的 1.1 版本。.NET Core： https://www.microsoft.com/net/core， 使用最新的 2.0 版本。Iris： https://github.com/kataras/iris， 使用基于 Go 1.8.3 构建的最新 8.3 版本。两个应用程序都通过请求路径 “api/values/{id}” 返回文本“值”。.NET Core MVCLogo 由 Pablo Iglesias 设计。可以使用 dotnet new webapi 命令创建项目，其 webapi 模板会为您生成代码，代码包含 GET 请求方法的 返回“值”。源代码：using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; namespace netcore_mvc { public class Program { public static void Main(string[] args) { BuildWebHost(args).Run(); } public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup<Startup>() .Build(); } } using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extens…

从 Node 到 Go：一个粗略的比较
type: item
date: 2017-10-01T16:46:00+08:00
description: 在 XO 公司，我们最初使用 Node 和 Ruby 构建相互连接的服务系统。我们享受 Node 带来的明显性能优势，以及可以访问已有的大型软件包仓库。我们也可以轻松地在公司内部发布并复用已有的插件和模块。极大地提高了开发效率，使得我们可以快速编写出可拓展的和可靠的应用。而且，庞大的 Node 社区使我们的工程师向开源软件贡献更加容易（比如 BunnyBus 和 Felicity）。虽然我在大学时期和刚刚工作的一些时间在使用更严谨的编译语言，比如 C++ 和 C#，而后来我开始使用 JavaScript。我很喜欢它的自由和灵活，但是我最近开始怀念静态和结构化的语言，因为当时有一个同事让我对 Go 语言产生了兴趣。我从写 JavaScript 到写 Go，我发现两种语言有很多相似之处。两者学习起来都很快并且易于上手，都具有充满表现力的语法，并且在开发者社区中都有很多工作机会。没有完美的编程语言，所以你应该总是选择一个适合手头项目的语言。在这篇文章中，我将要说明这两种语言深层次上的关键区别，希望能鼓励没有用过 Go 语言的用户可以有机会使用 Go 。大体上的差异在深入细节之前，我们应该先了解一下两种语言之间的重要区别。Go，或称 Golang，是 Google 在 2007 年创建的自由开源编程语言。它以快速和简单为设计目标。Go 被直接编译成机器码，这就是它速度的来源。使用编译语言调试是相当容易的，因为你可以在早期捕获大量错误。Go 也是一种强类型的语言，它有助于数据完整，并可以在编译时查找类型错误。另一方面，JavaScript 是一种弱类型语言。除了忽略验证数据的类型和真值判断陷阱所带来的额外负担之外，使用弱类型语言也有自己的好处。比起使用 接口 interfaces 和 范型 generics ， 柯里化 currying 和 可变的形参个数 flexible arity 让函数变得更加灵活。JavaScript 在运行时进行解释，这可能导致错误处理和调试的问题。Node 是一款基于 Google V8 虚拟机的 JavaScript 运行库，这使它成为一个轻量和快速的 Web 开发平台。语法作为原来的 JavaScript 开发者，Go 简单和直观的语法很吸引我。由于两种语言的语法可以说都是从 C 语言演变而来的，所以它们的语法有很多相同之处。Go 被普遍认为是一种“容易学习的语言”。那是因为它的对开发者友好的工具、精简的语法和固守惯例（LCTT 译注：惯例优先）。Go 包含大量有助于简化开发的内置特性。你可以用标准 Go 构建工具把你的程序用 go build 命令编译成二进制可执行文件。使用内置的测试套件进行测试只需要运行 go test 即可。 诸如原生支持的并发等特性甚至在语言层面上提供。Google 的 Go 开发者认为，现在的编程太复杂了，太多的“记账一样，重复劳动和文书工作”。这就是为什么 Go 的语法被设计得如此简单和干净，以减少混乱、提高效率和增强可读性。它还鼓励开发人员编写明确的、易于理解的代码。Go 只有 25 个保留关键字和一种循环（for 循环），而不像 JavaScript 有 大约 84 个关键字（包括保留关键字字、对象、属性和方法）。为了说明语法的一些差异和相似之处，我们来看几个例子：标点符号： Go 去除了所有多余的符号以提高效率和可读性。尽管 JavaScript 中需要符号的地方也不多（参见： Lisp），而且经常是可选的，但我更加喜欢 Go 的简单。// JavaScript 的逗号和分号 for (var i = 0; i < 10; i++) { console.log(i); } JavaScript 中的标点// Go 使用最少数量标点 for i := 0; i < 10; i++ { fmt.Println(i) } Go 中的标点赋值：由于 Go 是强类型语言，所以你在初始化变量时可以使用 := 操作符来进行类型推断，以避免重复声明，而 JavaScript 则在运行时声明类型。// JavaScript 赋值 var foo = "bar"; JavaScript 中的赋值// Go 的赋值 var foo string //不使用类型推导 foo = "bar" foo := "bar" //使用类型推导 Go 的赋值导出：在 JavaScript 中，你必须从某个模块中显式地导出。 在 Go 中，任何大写的函数将被默认导出。const Bar = () => {}; module.exports = { Bar } JavaScript 中的导出// Go 中的导出 package foo // 定义包名 func Bar (s string) string {…

在 Go 中如何使用切片的容量和长度
type: item
date: 2017-06-02T17:49:00+08:00
description: 快速测试 - 下面的代码输出什么？vals := make([]int, 5) for i := 0; i < 5; i++ { vals = append(vals, i) } fmt.Println(vals) 在 Go Playground 运行一下如果你猜测的是 [0 0 0 0 0 0 1 2 3 4]，那你是对的。等等，什么？ 为什么不是 [0 1 2 3 4]?如果你在测试中做错了，你也不用担心。这是在过渡到 Go 语言的过程中相当常见的错误，在这篇文章中，我们将说明为什么输出不是你预期的，以及如何利用 Go 的细微差别来使你的代码更有效率。切片 vs 数组在 Go 中同时有数组（array）和切片（slice）。这可能令人困惑，但一旦你习惯了，你会喜欢上它。请相信我。切片和数组之间存在许多差异，但我们要在本文中重点介绍的内容是数组的大小是其类型的一部分，而切片可以具有动态大小，因为它们是围绕数组的封装。这在实践中意味着什么？那么假设我们有数组 val a [10]int。该数组具有固定大小，且无法更改。如果我们调用 len(a)，它总是返回 10，因为这个大小是类型的一部分。因此，如果你突然需要在数组中超过 10 个项，则必须创建一个完全不同类型的新对象，例如 val b [11]int，然后将所有值从 a 复制到 b。在特定情况下，含有集合大小的数组是有价值的，但一般而言，这不是开发人员想要的。相反，他们希望在 Go 中使用类似于数组的东西，但是随着时间的推移，它们能够随时增长。一个粗略的方式是创建一个比它需要大得多的数组，然后将数组的一个子集视为数组。下面的代码是个例子。var vals [20]int for i := 0; i < 5; i++ { vals[i] = i * i } subsetLen := 5 fmt.Println("The subset of our array has a length of:", subsetLen) // Add a new item to our array vals[subsetLen] = 123 subsetLen++ fmt.Println("The subset of our array has a length of:", subsetLen) 在 Go Playground 中运行在代码中，我们有一个长度为 20 的数组，但是由于我们只使用一个子集，代码中我们可以假定数组的长度是 5，然后在我们向数组中添加一个新的项之后是 6。这是（非常粗略地说）切片是如何工作的。它们包含一个具有设置大小的数组，就像我们前面的例子中的数组一样，它的大小为 20。它们还跟踪程序中使用的数组的子集 - 这就是 append 属性，它类似于上一个例子中的 subsetLen 变量。最后，一个切片还有一个 capacity，类似于前面例子中我们的数组的总长度（20）。这是很有用的，因为它会告诉你的子集在无法容纳切片数组之前可以增长的大小。当发生这种情况时，需要分配一个新的数组，但所有这些逻辑都隐藏在 append 函数的后面。简而言之，使用 append 函数组合切片给我们一个非常类似于数组的类型，但随着时间的推移，它可以处理更多的元素。我们再来看一下前面的例子，但是这次我们将使用切片而不是数组。var vals []int for i := 0; i < 5; i++ { vals = append(vals, i) fmt.Println("The length of our slice is:", len(vals)) fmt.Println("The capacity of our slice is:", cap(vals)) } // Add a new item to our array vals = append(vals, 123) fmt.Println("The length of our slice is:", len(vals)) fmt.Println("The capacity of our slice is:", cap(vals)) // Accessing items is the same as an array fmt.Println(vals[5]) fmt.Println(vals[2]) 在 Go Playground 中运行我们仍然可以像数组一样访问我们的切片中的元素，但是通过使用切片和 append 函数，我们不再需要考虑背后数组的大小。我们仍然可以通过使用 len 和 cap 函数来计算出这些东西，但是我们不用担心太多。简洁吧？回到测试记住这点，让我们回顾前面的测试，看下什么出错了。vals := make([]in…

如何在 Docker 中设置 Go 并部署应用
type: item
date: 2017-01-12T17:46:39+08:00
description: 嗨，在本教程中，我们将学习如何使用 docker 部署 golang web 应用程序。 你可能已经知道，由于 golang 的高性能和可靠性，docker 是完全是用 golang 写的。在我们详细介绍之前，请确保你已经安装了 docker 以及 golang 并对它们有基本了解。关于 dockerDocker 是一个开源程序，它可以将应用及其完整的依赖包捆绑到一起，并打包为容器，与宿主机共享相同的 Linux 内核。另一方面，像 VMware 这样的基于 hypervisor 的虚拟化操作系统容器提供了高级别的隔离和安全性，这是由于客户机和主机之间的通信是通过 hypervisor 来实现的，它们不共享内核空间。但是硬件仿真也导致了性能的开销，所以容器虚拟化诞生了，以提供一个轻量级的虚拟环境，它将一组进程和资源与主机以及其它容器分组及隔离，因此，容器内部的进程无法看到容器外部的进程或资源。用 Go 语言创建一个 “Hello World” web 应用首先我们为 Go 应用创建一个目录，它会在浏览器中显示 “Hello World”。创建一个 web-app 目录并使它成为当前目录。进入 web-app 应用目录并编辑一个名为 main.go 的文件。root@demohost:~# mkdir web-app root@demohost:~# cd web-app/ root@demohost:~/web-app# vim.tiny main.go package main import ( "fmt" "net/http" ) func handler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello %s", r.URL.Path[1:]) } func main() { http.HandleFunc("/World", handler) http.ListenAndServe(":8080", nil) } 使用下面的命令运行上面的 “Hello World” Go 程序。在浏览器中输入 http://127.0.0.1:8080/World 测试，你会在浏览器中看到 “Hello World”。root@demohost:~/web-app# PORT=8080 go run main.go 下一步是将上面的应用在 docker 中容器化。因此我们会创建一个 dockerfile 文件，它会告诉 docker 如何容器化我们的 web 应用。root@demohost:~/web-app# vim.tiny Dockerfile # 得到最新的 golang docker 镜像 FROM golang:latest # 在容器内部创建一个目录来存储我们的 web 应用，接着使它成为工作目录。 RUN mkdir -p /go/src/web-app WORKDIR /go/src/web-app # 复制 web-app 目录到容器中 COPY . /go/src/web-app # 下载并安装第三方依赖到容器中 RUN go-wrapper download RUN go-wrapper install # 设置 PORT 环境变量 ENV PORT 8080 # 给主机暴露 8080 端口，这样外部网络可以访问你的应用 EXPOSE 8080 # 告诉 Docker 启动容器运行的命令 CMD ["go-wrapper", "run"] 构建/运行容器使用下面的命令构建你的 Go web-app，你会在成功构建后获得确认。root@demohost:~/web-app# docker build --rm -t web-app . Sending build context to Docker daemon 3.584 kB Step 1 : FROM golang:latest latest: Pulling from library/golang 386a066cd84a: Already exists 75ea84187083: Pull complete 88b459c9f665: Pull complete a31e17eb9485: Pull complete 1b272d7ab8a4: Pull complete eca636a985c1: Pull complete 08158782d330: Pull complete Digest: sha256:02718aef869a8b00d4a36883c82782b47fc01e774d0ac1afd434934d8ccfee8c Status: Downloaded…