Docker 从入门到实践-1-Docker简介

wwdyy 发表了文章 • 0 个评论 • 421 次浏览 • 2016-10-14 18:11 • 来自相关话题

本文转自极客学院-wiki

Docker 从入门到实践

Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。容器是... 查看全部

本文转自极客学院-wiki


Docker 从入门到实践


Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 App)。几乎没有性能开销,可以很容易地在机器和数据中心中运行。最重要的是,他们不依赖于任何语言、框架包括系统。


Docker 是个伟大的项目,它彻底释放了虚拟化的威力,极大降低了云计算资源供应的成本,同时让应用的分发、测试、部署和分发都变得前所未有的高效和轻松!


适用人群 适用人群


本书既适用于具备基础 Linux 知识的 Docker 初学者,也希望可供理解原理和实现的高级用户参考。


学习前提 学习前提


学习本书前,需要你对 Linux 系统有一定的了解,此外,本书注重实践,所以需要读者能够自己搭建 Linux 环境。


什么是 Docker


Docker 是一个开源项目,诞生于 2013 年初,最初是 dotCloud 公司内部的一个业余项目。它基于 Google 公司推出的 Go 语言实现。 项目后来加入了 Linux 基金会,遵从了 Apache 2.0 协议,项目代码在 GitHub 上进行维护。


Docker 自开源后受到广泛的关注和讨论,以至于 dotCloud 公司后来都改名为 Docker Inc。Redhat 已经在其 RHEL6.5 中集中支持 Docker;Google 也在其 PaaS 产品中广泛应用。


Docker 项目的目标是实现轻量级的操作系统虚拟化解决方案。 Docker 的基础是 Linux 容器(LXC)等技术。


在 LXC 的基础上 Docker 进行了进一步的封装,让用户不需要去关心容器的管理,使得操作更为简便。用户操作 Docker 的容器就像操作一个快速轻量级的虚拟机一样简单。


下面的图片比较了 Docker 和传统虚拟化方式的不同之处,可见容器是在操作系统层面上实现虚拟化,直接复用本地主机的操作系统,而传统方式则是在硬件层面实现。



为什么要使用 Docker?


作为一种新兴的虚拟化方式,Docker 跟传统的虚拟化方式相比具有众多的优势。


首先,Docker 容器的启动可以在秒级实现,这相比传统的虚拟机方式要快得多。 其次,Docker 对系统资源的利用率很高,一台主机上可以同时运行数千个 Docker 容器。


容器除了运行其中应用外,基本不消耗额外的系统资源,使得应用的性能很高,同时系统的开销尽量小。传统虚拟机方式运行 10 个不同的应用就要起 10 个虚拟机,而Docker 只需要启动 10 个隔离的应用即可。


具体说来,Docker 在如下几个方面具有较大的优势。


更快速的交付和部署


对开发和运维(devop)人员来说,最希望的就是一次创建或配置,可以在任意地方正常运行。


开发者可以使用一个标准的镜像来构建一套开发容器,开发完成之后,运维人员可以直接使用这个容器来部署代码。 Docker 可以快速创建容器,快速迭代应用程序,并让整个过程全程可见,使团队中的其他成员更容易理解应用程序是如何创建和工作的。 Docker 容器很轻很快!容器的启动时间是秒级的,大量地节约开发、测试、部署的时间。


更高效的虚拟化


Docker 容器的运行不需要额外的 hypervisor 支持,它是内核级的虚拟化,因此可以实现更高的性能和效率。


更轻松的迁移和扩展


Docker 容器几乎可以在任意的平台上运行,包括物理机、虚拟机、公有云、私有云、个人电脑、服务器等。 这种兼容性可以让用户把一个应用程序从一个平台直接迁移到另外一个。


更简单的管理


使用 Docker,只需要小小的修改,就可以替代以往大量的更新工作。所有的修改都以增量的方式被分发和更新,从而实现自动化并且高效的管理。


对比传统虚拟机总结
































特性 容器 虚拟机
启动 秒级 分钟级
硬盘使用 一般为 MB 一般为 GB
性能 接近原生 弱于
系统支持量 单机支持上千个容器 一般几十个

使用channel代替条件变量

astaxie 回复了问题 • 2 人关注 • 1 个回复 • 925 次浏览 • 2016-10-14 17:25 • 来自相关话题

Python 程序员的 Golang 学习指南(III): 入门篇

Cloudinsight 发表了文章 • 0 个评论 • 1605 次浏览 • 2016-10-14 15:39 • 来自相关话题

Authors: startover

Authors: startover





基础语法


类型和关键字



  • 类型


// 基础类型
布尔类型: bool
整型: int8,uint8,int16,uint16,int32,uint32,int64,uint64,int,rune,byte,complex128, complex64,其中,byte 是 int8 的别名
浮点类型: float32 、 float64
复数类型: complex64 、 complex128
字符串: string
字符类型: rune(int32的别名)
错误类型: error

// 复合类型
指针(pointer)
数组(array)
切片(slice)
字典(map)
通道(chan)
结构体(struct)
接口(interface)


  • 关键字


break        default      func         interface    select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var

变量


Go 同其他语言不同的地方在于变量的类型在变量名的后面,不是 int a,而是 a int。至于为什么这么定义,Go 的官方博客有给出解释,有兴趣的可以参考下。


变量定义语法如下:


var a int
a = 2

// 或者
a := 2

// 同时定义多个变量
var (
a int
b bool
)

// 同时给多个变量赋值
a, b := 2, true

操作符


+    &     +=    &=     &&    ==    !=    (    )
- | -= |= || < <= [ ]
* ^ *= ^= <- > >= { }
/ << /= <<= ++ = := , ;
% >> %= >>= -- ! ... . :
&^ &^=

控制结构


Go 语言支持如下的几种流程控制语句:



  • 条件语句,对应的关键字为 if、else 和 else if;

  • 选择语句,对应的关键字为 switch、case 和 select;

  • 循环语句,对应的关键字为 for 和 range;

  • 跳转语句,对应的关键字为 goto。


值得一提的是,Go 语言并不支持 do 或者 while 关键字,而是对 for 关键字做了增强,以实现类似的效果,如下:


for {
// 实现无限循环,慎用!
}

常用内置函数



  • len:计算(字符串,数组或者切片,map)长度

  • cap:计算(数组或者切片,map)容量

  • close:关闭通道

  • append:追加内容到切片

  • copy:拷贝数组/切片内容到另一个数组/切片

  • delete:用于删除 map 的元素


array, slice 和 map


// array
a := [3]int{ 1, 2, 3 } // 等价于 a := [...]int{ 1, 2, 3 }

// slice
s := make([]int , 3) // 创建一个长度为 3 的 slice
s := append(s, 1) // 向 slice 追加元素
s := append(s, 2)

// map
m := make(map[string]int) // 使用前必须先初始化
m["golang"] = 7

关于 array, slice 和 map 的更多惯用法,有一篇文章介绍的挺详细,有兴趣的可以看看。


函数


Go 语言的函数有如下特性:



  • 不定参数


由于 Go 语言不支持函数重载(具体原因见 Go Language FAQ),但我们可以通过不定参数实现类似的效果。


func myfunc(args ...int) {
// TODO
}

// 可通过如下方式调用
myfunc(2)
myfunc(1, 3, 5)


  • 多返回值


与 C、C++ 和 Java 等开发语言的一个极大不同在于,Go 语言的函数或者成员的方法可以有多
个返回值,这个特性能够使我们写出比其他语言更优雅、更简洁的代码。


func (file *File) Read(b []byte) (n int, err error)

// 我们可以通过下划线(_)来忽略某个返回值
n, _ := f.Read(buf)


  • 匿名函数


匿名函数是指不需要定义函数名的一种函数实现方式,它并不是一个新概念,最早可以回溯
到 1958 年的 Lisp 语言。但是由于各种原因,C 和 C++ 一直都没有对匿名函数给以支持,其他的各
种语言,比如 JavaScript、C# 和 Objective-C 等语言都提供了匿名函数特性,当然也包含Go语言。


匿名函数由一个不带函数名的函数声明和函数体组成,如下:


func(a, b int) bool {
return a < b
}

匿名函数可以直接赋值给一个变量或者直接执行:


f := func(a, b int) bool {
return a < b
}

func(a, b int) bool {
return a < b
}(3, 4) // 花括号后直接跟参数列表表示函数调用


  • 闭包


闭包是可以包含自由(未绑定到特定对象)变量的代码块,这些变量不在这个代码块内或者
任何全局上下文中定义,而是在定义代码块的环境中定义。要执行的代码块(由于自由变量包含
在代码块中,所以这些自由变量以及它们引用的对象没有被释放)为自由变量提供绑定的计算环
境(作用域)。


Go 的匿名函数就是一个闭包。我们来看一个例子:


package main

import "fmt"

func main() {
j := 5
a := func() func() {
i := 10
return func() {
fmt.Printf("i, j: %d, %d\n", i, j)
}
}()
a()
j *= 2
a()
}

程序输出如下:


i, j: 10, 5
i, j: 10, 10

错误处理


Go 语言追求简洁优雅,所以,Go 语言不支持传统的 try...catch...finally 这种异常,因为 Go 语言的设计者们认为,将异常与控制结构混在一起会很容易使得代码变得混乱。因为开发者很容易滥用异常,甚至一个小小的错误都抛出一个异常。在 Go 语言中,使用多值返回来返回错误。不要用异常代替错误,更不要用来控制流程。在极个别的情况下,也就是说,遇到真正的异常的情况下(比如除数为0了),才使用 Go 中引入的Exception处理:defer, panic, recover。


用法如下:


package main

import "fmt"

func main() {
defer func() {
fmt.Println("recovered:", recover())
}()
panic("not good")
}

关于 Go 语言的错误处理机制和传统的 try...catch...finally 异常机制孰优孰劣,属于仁者见仁,智者见智,这里不做赘速。有兴趣的同学可以去看看知乎上的讨论:Go 语言的错误处理机制是一个优秀的设计吗?


面向对象 -> 一切皆类型


Python 推崇“一切皆对象”,而在 Go 语言中,类型才是一等公民。


我们可以这样定义一个结构体:


type Name struct {
First string
Middle string
Last string
}

同样也可以定义基础类型:


type SimpleName string

还能给任意类型定义方法:


func (s SimpleName) String() string { return string(s) }
// 或者
func (s string) NoWay()

Golang VS Python


最后我们通过几个例子来比较一下 Golang 与 Python 的一些基本用法,如下:


生成器(Generator)



  • Python 版本


def fib(n):
a, b = 0, 1
for i in range(n):
a, b = b, a + b
yield a

for x in fib(10):
print x

print 'done'


  • Golang 版本


package main

import "fmt"

func fib(n int) chan int {
c := make(chan int)
go func() {
a, b := 0, 1
for i := 0; i < n; i++ {
a, b = b, a+b
c <- a
}
close(c)
}()
return c
}

func main() {
for x := range fib(10) {
fmt.Println(x)
}
}

装饰器(Decorator)



  • Python 版本


from urlparse import urlparse, parse_qs
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler

def auth_required(myfunc):
def checkuser(self):
user = parse_qs(urlparse(self.path).query).get('user')
if user:
self.user = user[0]
myfunc(self)
else:
self.wfile.write('unknown user')
return checkuser

class myHandler(BaseHTTPRequestHandler):
@auth_required
def do_GET(self):
self.wfile.write('Hello, %s!' % self.user)

if __name__ == '__main__':
try:
server = HTTPServer(('localhost', 8080), myHandler)
server.serve_forever()
except KeyboardInterrupt:
server.socket.close()


  • Golang 版本


package main

import (
"fmt"
"net/http"
)

var hiHandler = authRequired(
func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hi, %v", r.FormValue("user"))
},
)

func authRequired(f http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.FormValue("user") == "" {
http.Error(w, "unknown user", http.StatusForbidden)
return
}
f(w, r)
}
}

func main() {
http.HandleFunc("/hi", hiHandler)
http.ListenAndServe(":8080", nil)
}

猴子补丁(Monkey patching)



  • Python 版本


import urllib

def say_hi(usr):
if auth(usr):
print 'Hi, %s' % usr
else:
print 'unknown user %s' % usr

def auth(usr):
try:
auth_url = 'localhost'
r = urllib.urlopen(auth_url + '/' + usr)
return r.getcode() == 200
except:
return False

def sayhitest():
# Test authenticated user
globals()['auth'] = lambda x: True
say_hi('John')

# Test unauthenticated user
globals()['auth'] = lambda x: False
say_hi('John')

if __name__ == '__main__':
sayhitest()


  • Golang 版本


package main

import (
"fmt"
"net/http"
)

func sayHi(user string) {
if !auth(user) {
fmt.Printf("unknown user %v\n", user)
return
}
fmt.Printf("Hi, %v\n", user)
}

var auth = func(user string) bool {
authURL := "localhost"
res, err := http.Get(authURL + "/" + user)
return err == nil && res.StatusCode == http.StatusOK
}

func testSayHi() {
auth = func(string) bool { return true }
sayHi("John")

auth = func(string) bool { return false }
sayHi("John")
}

func main() {
testSayHi()
}

相关链接:

https://blog.golang.org/gos-declaration-syntax

https://se77en.cc/2014/06/30/array-slice-map-and-set-in-golang/

https://golang.org/doc/faq#overloading

https://www.zhihu.com/question/27158146

https://talks.golang.org/2013/go4python.slide




本文章为 Cloudinsight 技术团队工程师原创,更多技术文章可访问 Cloudinsight 技术博客Cloudinsight 为可视化系统监控工具,涵盖 Windows、Linux 操作系统,用 Golang 开发的 Cloudinsight Agent 正式开源了,欢迎 fork,Github:https://github.com/cloudinsight/cloudinsight-agent


golang-for-pythonistas 系列持续更新中,欢迎关注,及时获取最新文章~

我想使用hugo,该如何入门?

itfanr 发表了文章 • 3 个评论 • 558 次浏览 • 2016-10-14 13:54 • 来自相关话题

我为什么又搞了一个Go社区

astaxie 发表了文章 • 54 个评论 • 3271 次浏览 • 2016-10-14 09:54 • 来自相关话题

昨天在v2ex和ruby-china的社区发了帖子宣传我们社区,有一些朋友问我有疑问:怎么又出来一个社区啊,我觉得我有必要把我的初心说清楚(留下证据给自己将来打脸)。:)

很多人的印象可能是怎么又一个Go社区啊,Go社区好分裂啊,能不能一起... 查看全部

昨天在v2ex和ruby-china的社区发了帖子宣传我们社区,有一些朋友问我有疑问:怎么又出来一个社区啊,我觉得我有必要把我的初心说清楚(留下证据给自己将来打脸)。:)


很多人的印象可能是怎么又一个Go社区啊,Go社区好分裂啊,能不能一起愉快的玩了,其实理由我上面都有写,目前的几个社区都是缺乏运营的,而为什么要在搞一个Go的社区呢?其实这个只是我其中要做的一个事情。


今年Gopher大会的时候我也说过要成立一个Go基金会,那么这个基金会也在最近开始运作起来了,http://golangfoundation.org/


基金会做什么



  • 大会和 Meetup

  • 线上社区

  • 指导 Go 的企业级应用

  • 校园培训

  • 开源引导

  • Workshop / Training


那么以现有的几个社区的运营我觉得是远远达不到这个要求的,我以前是玩PHP的,我也记得在PHPChina出来之前可能已经有差不多四个PHP的社区了吧,但是最后还是PHPChina统一了目前的社区,为什么呢?因为后期的运营以及一系列的活动。所以社区多其实是好事情,说明大家都很有热情去做这个事情,但是是不是能够持之以恒的去做好这个事情呢?这个才是最重要的。


既然我选择开始做Go基金会,在线社区是其中一个重要的环节,这是我们的其中一项使命。


那么另一个问题,你凭什么做的比其他几家社区会更好呢?



  • 运营的人才,目前这一块已经聚集了差不多十几个人的运营团队。

  • 基金会资金的支持,基金会企业会员的资金支持,同时我们也得到了极客邦科技的大力支持,极客邦在全国各地都有分布,而我们也和他们深度的合作,以后会有更多的线下活动。

  • 线下线上活动的结合,meetup,Gopher大会等线下社区的互动,可能后期也会引入直播之类的到线上来互动。

  • 开源项目社区的建立,目前Go基金会下面的几个项目的社区建设也会同步放在gocn的社区里面,这几个项目都是已经得到了大量企业的使用,我想这也是在线社区的价值,知识的积累。

  • 和更多的Go企业的合作,公众号对接,招聘对接。


我只想说我做这个社区是有备而来的,如果你觉得可以做的比我更好,或者有更好的idea,欢迎一起来玩。

10.14 每日早报

astaxie 发表了文章 • 2 个评论 • 494 次浏览 • 2016-10-14 08:19 • 来自相关话题

~~ 10.14 每日早报~~

新闻:

1.微软正式发布Windows Server 2016,全面开放下载

2.国务院办公厅公布互联网金融风险专项整治工作实施方案

3.英雄联盟(LOL)宣布11月... 查看全部

~~ 10.14 每日早报~~


新闻:


1.微软正式发布Windows Server 2016,全面开放下载


2.国务院办公厅公布互联网金融风险专项整治工作实施方案


3.英雄联盟(LOL)宣布11月起停止支持XP SP2或以下版本


4.索尼举办PS VR中国首发会,公布12款VR游戏、发布PS4主机


5.庞大集团与光大银行合作向叮叮约车提供50亿元,开拓网约车市场


6.继趣分期之后,校园贷平台我来贷正式关闭学生申请贷款通道


7.糖豆广场舞完成B轮1500万美元融资,顺为资本领投


资源:


中国互联网金融P2P行业合规转型策略
http://www.199it.com/archives/525685.html


注:上述内容来源于互联网,由EGO整理

16.10.13Docker最新动态 Docker在中国找到了第一个官方合作伙伴-阿里云

wwdyy 发表了文章 • 2 个评论 • 389 次浏览 • 2016-10-13 19:16 • 来自相关话题

10月13日,在2016杭州·云栖大会上,全球知名的容器技术公司Docker与阿里云宣布达成战略合作,双方将在容器服务领域进行紧密合作,阿里云将为客户提供更加先进的云上应用管理服务。 查看全部

10月13日,在2016杭州·云栖大会上,全球知名的容器技术公司Docker与阿里云宣布达成战略合作,双方将在容器服务领域进行紧密合作,阿里云将为客户提供更加先进的云上应用管理服务。


基于合作,双方在开源容器技术以及发展方向上共同努力,并提供本地化的Docker服务。Docker公司选择阿里云平台作为其Docker Hub在中国运营的基础服务。阿里云也获得Docker Engine商用版以及Docker DataCenter运营权,并为Docker客户提供企业级支持和咨询服务。同时,阿里云将成为Docker官方支持的云服务提供商。


Docker技术是近年来最火的开源技术,正在改变企业应用从开发、构建到发布、运行的整个生命周期。 此前,Docker与AWS、Azure、IBM、HPE进行合作,阿里云是其在国内的第一个官方合作伙伴。


阿里云在2015年开始提供“容器服务”,在容器领域有着长期的技术积累和持续投入。阿里云将为客户提供更为多样的容器相关解决方案如DevOps、微服务改造,藉此帮助企业用户通过更为稳定、高效、安全的实现其对应用升级和改造,有效降低维护成本,交付效率提升13倍。同时,利用容器“一次构建,随处运行”的特点,可以帮助用户迁移无缝应用到云环境。


Docker Hub等服务落地中国,可以更好地服务国内开发者,促进中国技术社区的成长。得益于阿里云强大的云基础设施,将极大提升国内用户对Docker Hub的访问体验。


在阿里云上,华大基因正在利用容器服务构建基因数据的应用开发和共享平台,路特通过阿里云容器服务实现DevOps为企业客户提供SaaS服务,学霸君基于容器服务实现微服务架构应用和DevOps流程优化。


Docker CEO Ben Golub说,我们很高兴把Docker容器技术, 商业化的Docker Engine和Docker DataCenter引进到中国,我们选择了领先的的合作伙伴,让我们能够更好地服务于我们共同的客户。


阿里云总裁胡晓明表示,通过和Docker的战略合作,阿里云将更好地为企业级客户提供完善的云服务,使能客户,并实现时代转型。

Windows 原生 Docker 正式商用

DaoCloud 发表了文章 • 0 个评论 • 537 次浏览 • 2016-10-13 16:59 • 来自相关话题

上图显示:Windows 应用已经可以稳定地运行在原生容器管理平台之上

... 查看全部


上图显示:Windows 应用已经可以稳定地运行在原生容器管理平台之上


2016 年 9 月 26 日,微软 Ignite 技术大会在亚特兰大举行,微软官方正式发布了 Windows Server 2016。对于广大 Windows 开发人员和 IT 技术专家来说,Windows 最令人激动的新功能,非「容器」莫属。而运行在 Windows Server 2016 上的容器,正是由 Docker 公司所驱动。


本篇博客将详细剖析这些让 Docker 容器与 Windows 完美匹配的技术创新点,并尝试阐述这些成就的重要意义。我们也推荐你看看同期发布的几篇博客,其中一篇讲述了如何创建你的第一台 Windows 容器,还有一篇详细介绍了 Docker 公司和微软公司为了在 Windows 上支持 Docker 而开展的一系列商业合作。



2013 年,第一版 Docker 正式问世。从那以后的三年间,Docker 发展势如破竹,彻底改变了 Linux 开发者和运维人员 “构建、交付和运行(build,ship and run)” 应用的方式。如今,Docker Engine 和容器已经可以在 Windows 平台之上原生使用了,开发者和 IT 专家们也可以在 Windows 平台应用和基础架构上,体验一把 Linux 用户曾经经历过的大革新,享受 Linux 用户曾经享受过的福利:更好的安全性、更高的敏捷性、更优良的便捷性,以及随时可以把私有应用迁移到云端之上的灵活性。


而对于那些需要创建和维护 Linux 和 Windows 两大架构平台的开发人员和 IT 专家们来说,Docker 运行在 Windows 平台的重大意义更加不言而喻:现在,Docker 平台已经可以为 Linux 和 Windows 应用的管理提供一整套工具、API 和镜像格式了。随着 Linux 和 Windows 应用与服务器不断地 「Docker 化」,开发者和 IT 专家将能够通过使用 Docker 技术来管理和改进本地和云端的复杂微服务部署。


在 Windows Server 之上运行容器


Docker 和微软已经磨合了两年,期间开展了一系列的合作——Windows 内核中容器化基元(primitives)越来越多,为了充分利用这些新基元,双方协力把 Docker Engine 和 CLI 迁移到 Windows 平台上,Docker 还给 Docker Hub 增加了多架构镜像支持。今天 Windows 平台中正式引入 Docker,正是这两年合作的结晶。


结果就是,现在在 Windows 平台上已经可以完美使用强大无比的 docker run 来快速启动一个完全独立的新容器了。



内核容器化功能已经整合进所有版本的 Windows Server 2016,在 Win10 的周年更新系统中也有。Windows 原生的 Docker daemon 可以在 Windows Server 2016 和 Win10 系统上运行(虽然 Win10 上只能创建和运行基于 Windows Server 的容器)。


Windows 版的 docker run 和 Linux 版有着一样的意义:全进程隔离,自带层级变动支持的沙盒文件系统(还有 Windows 注册表!)。每个容器都只面向一个纯净的 Windows 系统,而且无法介入到系统上的其他进程(不管这个进程是否被容器化了)。


举个例子,两个用着不同网络信息服务(IIS)版本,用着不同 . NET 框架的 Docker 化应用,可以在同一个系统中友好共存,甚至可以在互不影响的情况下,给各自的文件系统和注册表读写数据。


容器化之后,Windows IT 专家们可以隔离全部进程,发布状态的工件将变得非常稳定,用起虚拟机来就更加得心应手了,不必担心在硬件虚拟化过程中产生的资源超支和灵敏度降低。


Linux 上的容器可以用不同的安全档案运行,Windows 上的容器也与此类似,可以运行以下两种隔离模式中的一种:


1、Windows Server 容器使用和 Linux 容器一样的共享内核进程隔离范式。由于容器作为标准(但是隔离)的进程来运行,所以启动很快,而资源超支可以降到最低。


2、有了 Hyper-V 隔离,容器一启动就会生成一个很小的管理程序,容器进程就在这个程序里运行。虽然启动速度会稍慢一点,资源占用也会略有增加,但整体的隔离环境却会好上不少。


可以通过 docker run 上一个简单的开关来设置隔离:



只要底层主机能支持所需的隔离模式,任何 Windows 容器镜像都能当作一台 Hyper-V 或服务器容器来运行,而且一台容器主机可以一个接着一个地运行这两者。基于容器的隔离模式,它是不知道容器进程的,而 Docker 控制 API 对于两种模式而言都是一样的。


这样一来,开发者就无需经常为隔离模式操心了,他们只需要用默认的设置就行,或者自己做些方便上手的设定。当 IT 专家需要选择如何在产品中部署容器化的应用时,隔离模式实实在在地为他们提供了选择的余地。


当然了,有一点要注意:尽管 Hyper-V 是支持 Hyper-V隔离的运行时技术,但 Hyper-V 隔离了的容器并不是 Hyper-V 虚拟机,不能用标准的 Hyper-V 工具来管理。


构建 Windows 容器镜像


得益于 Windows 注册表和文件系统的层级改进,docker build 和 Dockerfiles 都能完全支持创建 Windows Docker 镜像。下面是一个 Windows Dockerfile 样例文件,它是由斯蒂凡·施尔提交给 Node.js 官方 Docker 库的镜像。它能用 docker build 在 Windows 上创建出来:



来看看 PowerShell 是如何用于安装和启动 zip 文件和应用程序的吧:Windows 容器运行遵循 Windows API 的可执行程序。要创建和运行 Windows 容器,你需要一个 Windows 系统。虽然 Windows 和 Linux 上的 Docker 工具、控制 API 和镜像格式都是一样的,Docker Windows 容器不能在 Linux 系统上运行,反之亦然。


还请注意,开始层是 microsoft/windowsservercore。在创建 Windows 容器镜像时,开启 FROMScratch 是没用的。镜像要么是基于 microsoft/windowsserverco,要么是基于 microsoft/nanoserver。


Windows Server Core 镜像还配有一个近乎完整的用户态,这个用户态有各种进程,还有建立在标准 Windows Server Core 安装上的 DLL。除了 GUI 应用,以及那些需要 Windows 远程桌面的应用,大部分运行在 Windows Server 上的应用都能被 Docker 化,以最少的误差,在一个基于 microsoft / windowsservercore 的镜像上运行。举几个例子:MicrosoftSQL Server, Apache, 网络信息服务(IIS),以及整个 .NET 框架。


这种灵活性,是以容量的暴增为代价的:microsoft/windowsservercore 镜像高达 10 个 G。不过幸好有了 Docker 高效率的镜像层级,在实际操作中容量过大并不是什么大麻烦。任何一台 Docker 主机只需要拖进底层一次便足矣,任何拖进系统或创建在系统上的镜像只不过是在重复利用底层。


另一个底层选项是 Nano Server,这是一款全新的小体积 Windows 版本,带有一个精简版的 Windows API。包括 IIS、新版 .NET Core framework,Node.js 和 GO 在内,已经有大量的软件运行在 Nano Server 上了。而且 Nano Server 基本镜像的体积远小于 Windows Server Core,这意味着它的必备组件更少,保持刷新所需的表面积也更小。Nano Server 是一个令人激动的成果,不仅因为,作为小型容器的底层,它的创建和 boot 非常快,还因为,它作为一种极小主义操作系统,是为了另一款同样优秀的,专门运行 Docker 镜像和容器的容器主机 OS 而生的——这就是它的伟大之处。


有了 Windows Server Core 和 Nano Server 可供选择,开发者和 IT 大牛们就可以自由玩转了,要么把异彩纷呈的 Windows 平台应用 “lift-and-shift” 到 Server Core 容器里,要么采用 Nano Server 搞绿地模式开发,要么把整个应用分解成数量更多的小部分,整合进微服务的组件里。


Docker 目前正与微软及其社区携手,在 WindowsServer Core 和 Nano Server 上创建容器镜像。Golang、Python 和 Mongo 语言作为正式 Docker 镜像,都可以用,更多的 Docker 镜像也正在开发之中,而且微软还维护了一系列非常普及的样本镜像。


总结


今天,Docker Engine 能在 Windows 系统上创建、运行和管理容器,是微软团队、Docker 团队,以及 Docker 社区成员经年累月劳动的成果。Docker 与微软通力合作,把容器化的福音带给了 Windows 开发者和 IT专家,我们为此感到无比自豪;让 Windows 和 Linux 技术能用同一套工具和 API 来创建、交付和运行应用,我们也为此感到无比激动。

Docker源码分析,附阅读地址

wwdyy 发表了文章 • 3 个评论 • 433 次浏览 • 2016-10-13 11:26 • 来自相关话题

Docker是一个由GO语言写的程序运行的“容器”,开发者可以从操作系统到依赖库乱七八糟东西(tomcat,mysql等等)全给你“静态编译”到一起变成一容器中

即使不在项目中使用 Docker,本书也能够为 Go 语言程序员带来帮助。Do... 查看全部

Docker是一个由GO语言写的程序运行的“容器”,开发者可以从操作系统到依赖库乱七八糟东西(tomcat,mysql等等)全给你“静态编译”到一起变成一容器中


即使不在项目中使用 Docker,本书也能够为 Go 语言程序员带来帮助。Docker 项目
中大量采用了 Go 语言,尤其是在处理并发场景时,Docker 对 Go 语言的运用可谓出神入
化。本书可以帮助 Go 语言程序员亲身体验特大型项目中 Go 语言的威力,以及实战场景中
Golang 模式和功能的用法


推荐大家去买正版图书,工具书不同于小说,需要反反复复的学习


阅读地址(可以下载):


[网易云阅读]( http://yuedu.163.com/source/37aa61f7a4874857bf7a57d7a2410b7f_4 )


[CSDN]( http://download.csdn.net/detail/hzbooks/8943237 )

【转】Go文件操作大全

故城 发表了文章 • 2 个评论 • 1858 次浏览 • 2016-10-13 09:57 • 来自相关话题

> 译者博客  查看全部
					
> 译者博客  http://colobu.com/2016/10/12/g ... erral

> 原作者博客 http://www.devdungeon.com/cont ... bytes

参考
Go官方库的文件操作分散在多个包中,比如os、ioutil包,我本来想写一篇总结性的Go文件操作的文章,却发现已经有人2015年已经写了一篇这样的文章,写的非常好,所以我翻译成了中文,强烈推荐你阅读一下。

原文: Working with Files in Go, 作者: NanoDano

介绍

万物皆文件

UNIX 的一个基础设计就是"万物皆文件"(everything is a file)。我们不必知道一个文件到底映射成什么,操作系统的设备驱动抽象成文件。操作系统为设备提供了文件格式的接口。

Go语言中的reader和writer接口也类似。我们只需简单的读写字节,不必知道reader的数据来自哪里,也不必知道writer将数据发送到哪里。
你可以在/dev下查看可用的设备,有些可能需要较高的权限才能访问。

基本操作

# 创建空文件

```go
package main
import (
"log"
"os"
)
var (
newFile *os.File
err error
)
func main() {
newFile, err = os.Create("test.txt")
if err != nil {
log.Fatal(err)
}
log.Println(newFile)
newFile.Close()
}

Truncate文件


package main
import (
"log"
"os"
)
func main() {
// 裁剪一个文件到100个字节。
// 如果文件本来就少于100个字节,则文件中原始内容得以保留,剩余的字节以null字节填充。
// 如果文件本来超过100个字节,则超过的字节会被抛弃。
// 这样我们总是得到精确的100个字节的文件。
// 传入0则会清空文件。
err := os.Truncate("test.txt", 100)
if err != nil {
log.Fatal(err)
}
}

得到文件信息


package main
import (
"fmt"
"log"
"os"
)
var (
fileInfo os.FileInfo
err error
)
func main() {
// 如果文件不存在,则返回错误
fileInfo, err = os.Stat("test.txt")
if err != nil {
log.Fatal(err)
}
fmt.Println("File name:", fileInfo.Name())
fmt.Println("Size in bytes:", fileInfo.Size())
fmt.Println("Permissions:", fileInfo.Mode())
fmt.Println("Last modified:", fileInfo.ModTime())
fmt.Println("Is Directory: ", fileInfo.IsDir())
fmt.Printf("System interface type: %T\n", fileInfo.Sys())
fmt.Printf("System info: %+v\n\n", fileInfo.Sys())
}

重命名和移动


package main
import (
"log"
"os"
)
func main() {
originalPath := "test.txt"
newPath := "test2.txt"
err := os.Rename(originalPath, newPath)
if err != nil {
log.Fatal(err)
}
}

译者按: rename 和 move 原理一样


删除文件


package main
import (
"log"
"os"
)
func main() {
err := os.Remove("test.txt")
if err != nil {
log.Fatal(err)
}
}

打开和关闭文件


package main
import (
"log"
"os"
)
func main() {
// 简单地以只读的方式打开。下面的例子会介绍读写的例子。
file, err := os.Open("test.txt")
if err != nil {
log.Fatal(err)
}
file.Close()
// OpenFile提供更多的选项。
// 最后一个参数是权限模式permission mode
// 第二个是打开时的属性
file, err = os.OpenFile("test.txt", os.O_APPEND, 0666)
if err != nil {
log.Fatal(err)
}
file.Close()
// 下面的属性可以单独使用,也可以组合使用。
// 组合使用时可以使用 OR 操作设置 OpenFile的第二个参数,例如:
// os.O_CREATE|os.O_APPEND
// 或者 os.O_CREATE|os.O_TRUNC|os.O_WRONLY
// os.O_RDONLY // 只读
// os.O_WRONLY // 只写
// os.O_RDWR // 读写
// os.O_APPEND // 往文件中添建(Append)
// os.O_CREATE // 如果文件不存在则先创建
// os.O_TRUNC // 文件打开时裁剪文件
// os.O_EXCL // 和O_CREATE一起使用,文件不能存在
// os.O_SYNC // 以同步I/O的方式打开
}

译者按:熟悉Linux的读者应该很熟悉权限模式,通过Linux命令chmod可以更改文件的权限
https://www.linux.com/learn/understanding-linux-file-permissions


补充了原文未介绍的flag


检查文件是否存在


package main
import (
"log"
"os"
)
var (
fileInfo *os.FileInfo
err error
)
func main() {
// 文件不存在则返回error
fileInfo, err := os.Stat("test.txt")
if err != nil {
if os.IsNotExist(err) {
log.Fatal("File does not exist.")
}
}
log.Println("File does exist. File information:")
log.Println(fileInfo)
}

检查读写权限


package main
import (
"log"
"os"
)
func main() {
// 这个例子测试写权限,如果没有写权限则返回error。
// 注意文件不存在也会返回error,需要检查error的信息来获取到底是哪个错误导致。
file, err := os.OpenFile("test.txt", os.O_WRONLY, 0666)
if err != nil {
if os.IsPermission(err) {
log.Println("Error: Write permission denied.")
}
}
file.Close()
// 测试读权限
file, err = os.OpenFile("test.txt", os.O_RDONLY, 0666)
if err != nil {
if os.IsPermission(err) {
log.Println("Error: Read permission denied.")
}
}
file.Close()
}

改变权限、拥有者、时间戳


package main
import (
"log"
"os"
"time"
)
func main() {
// 使用Linux风格改变文件权限
err := os.Chmod("test.txt", 0777)
if err != nil {
log.Println(err)
}
// 改变文件所有者
err = os.Chown("test.txt", os.Getuid(), os.Getgid())
if err != nil {
log.Println(err)
}
// 改变时间戳
twoDaysFromNow := time.Now().Add(48 * time.Hour)
lastAccessTime := twoDaysFromNow
lastModifyTime := twoDaysFromNow
err = os.Chtimes("test.txt", lastAccessTime, lastModifyTime)
if err != nil {
log.Println(err)
}
}

硬链接和软链接


一个普通的文件是一个指向硬盘的inode的地方。
硬链接创建一个新的指针指向同一个地方。只有所有的链接被删除后文件才会被删除。硬链接只在相同的文件系统中才工作。你可以认为一个硬链接是一个正常的链接。


symbolic link,又叫软连接,和硬链接有点不一样,它不直接指向硬盘中的相同的地方,而是通过名字引用其它文件。他们可以指向不同的文件系统中的不同文件。并不是所有的操作系统都支持软链接。


package main
import (
"os"
"log"
"fmt"
)
func main() {
// 创建一个硬链接。
// 创建后同一个文件内容会有两个文件名,改变一个文件的内容会影响另一个。
// 删除和重命名不会影响另一个。
err := os.Link("original.txt", "original_also.txt")
if err != nil {
log.Fatal(err)
}
fmt.Println("creating sym")
// Create a symlink
err = os.Symlink("original.txt", "original_sym.txt")
if err != nil {
log.Fatal(err)
}
// Lstat返回一个文件的信息,但是当文件是一个软链接时,它返回软链接的信息,而不是引用的文件的信息。
// Symlink在Windows中不工作。
fileInfo, err := os.Lstat("original_sym.txt")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Link info: %+v", fileInfo)
//改变软链接的拥有者不会影响原始文件。
err = os.Lchown("original_sym.txt", os.Getuid(), os.Getgid())
if err != nil {
log.Fatal(err)
}
}

读写


复制文件


package main
import (
"os"
"log"
"io"
)
func main() {
// 打开原始文件
originalFile, err := os.Open("test.txt")
if err != nil {
log.Fatal(err)
}
defer originalFile.Close()
// 创建新的文件作为目标文件
newFile, err := os.Create("test_copy.txt")
if err != nil {
log.Fatal(err)
}
defer newFile.Close()
// 从源中复制字节到目标文件
bytesWritten, err := io.Copy(newFile, originalFile)
if err != nil {
log.Fatal(err)
}
log.Printf("Copied %d bytes.", bytesWritten)
// 将文件内容flush到硬盘中
err = newFile.Sync()
if err != nil {
log.Fatal(err)
}
}

跳转到文件指定位置(Seek)


package main
import (
"os"
"fmt"
"log"
)
func main() {
file, _ := os.Open("test.txt")
defer file.Close()
// 偏离位置,可以是正数也可以是负数
var offset int64 = 5
// 用来计算offset的初始位置
// 0 = 文件开始位置
// 1 = 当前位置
// 2 = 文件结尾处
var whence int = 0
newPosition, err := file.Seek(offset, whence)
if err != nil {
log.Fatal(err)
}
fmt.Println("Just moved to 5:", newPosition)
// 从当前位置回退两个字节
newPosition, err = file.Seek(-2, 1)
if err != nil {
log.Fatal(err)
}
fmt.Println("Just moved back two:", newPosition)
// 使用下面的技巧得到当前的位置
currentPosition, err := file.Seek(0, 1)
fmt.Println("Current position:", currentPosition)
// 转到文件开始处
newPosition, err = file.Seek(0, 0)
if err != nil {
log.Fatal(err)
}
fmt.Println("Position after seeking 0,0:", newPosition)
}

写文件


可以使用os包写入一个打开的文件。
因为Go可执行包是静态链接的可执行文件,你import的每一个包都会增加你的可执行文件的大小。其它的包如io、`ioutil`、`bufio`提供了一些方法,但是它们不是必须的。


package main
import (
"os"
"log"
)
func main() {
// 可写方式打开文件
file, err := os.OpenFile(
"test.txt",
os.O_WRONLY|os.O_TRUNC|os.O_CREATE,
0666,
)
if err != nil {
log.Fatal(err)
}
defer file.Close()
// 写字节到文件中
byteSlice := []byte("Bytes!\n")
bytesWritten, err := file.Write(byteSlice)
if err != nil {
log.Fatal(err)
}
log.Printf("Wrote %d bytes.\n", bytesWritten)
}

快写文件


ioutil包有一个非常有用的方法WriteFile()可以处理创建/打开文件、写字节slice和关闭文件一系列的操作。如果你需要简洁快速地写字节slice到文件中,你可以使用它。


package main
import (
"io/ioutil"
"log"
)
func main() {
err := ioutil.WriteFile("test.txt", []byte("Hi\n"), 0666)
if err != nil {
log.Fatal(err)
}
}

使用缓存写


bufio包提供了带缓存功能的writer,所以你可以在写字节到硬盘前使用内存缓存。当你处理很多的数据很有用,因为它可以节省操作硬盘I/O的时间。在其它一些情况下它也很有用,比如你每次写一个字节,把它们攒在内存缓存中,然后一次写入到硬盘中,减少硬盘的磨损以及提升性能。


package main
import (
"log"
"os"
"bufio"
)
func main() {
// 打开文件,只写
file, err := os.OpenFile("test.txt", os.O_WRONLY, 0666)
if err != nil {
log.Fatal(err)
}
defer file.Close()
// 为这个文件创建buffered writer
bufferedWriter := bufio.NewWriter(file)
// 写字节到buffer
bytesWritten, err := bufferedWriter.Write(
[]byte{65, 66, 67},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Bytes written: %d\n", bytesWritten)
// 写字符串到buffer
// 也可以使用 WriteRune() 和 WriteByte()
bytesWritten, err = bufferedWriter.WriteString(
"Buffered string\n",
)
if err != nil {
log.Fatal(err)
}
log.Printf("Bytes written: %d\n", bytesWritten)
// 检查缓存中的字节数
unflushedBufferSize := bufferedWriter.Buffered()
log.Printf("Bytes buffered: %d\n", unflushedBufferSize)
// 还有多少字节可用(未使用的缓存大小)
bytesAvailable := bufferedWriter.Available()
if err != nil {
log.Fatal(err)
}
log.Printf("Available buffer: %d\n", bytesAvailable)
// 写内存buffer到硬盘
bufferedWriter.Flush()
// 丢弃还没有flush的缓存的内容,清除错误并把它的输出传给参数中的writer
// 当你想将缓存传给另外一个writer时有用
bufferedWriter.Reset(bufferedWriter)
bytesAvailable = bufferedWriter.Available()
if err != nil {
log.Fatal(err)
}
log.Printf("Available buffer: %d\n", bytesAvailable)
// 重新设置缓存的大小。
// 第一个参数是缓存应该输出到哪里,这个例子中我们使用相同的writer。
// 如果我们设置的新的大小小于第一个参数writer的缓存大小, 比如10,我们不会得到一个10字节大小的缓存,
// 而是writer的原始大小的缓存,默认是4096。
// 它的功能主要还是为了扩容。
bufferedWriter = bufio.NewWriterSize(
bufferedWriter,
8000,
)
// resize后检查缓存的大小
bytesAvailable = bufferedWriter.Available()
if err != nil {
log.Fatal(err)
}
log.Printf("Available buffer: %d\n", bytesAvailable)
}

读取最多N个字节


os.File提供了文件操作的基本功能, 而io、ioutil、bufio提供了额外的辅助函数。


package main
import (
"os"
"log"
)
func main() {
// 打开文件,只读
file, err := os.Open("test.txt")
if err != nil {
log.Fatal(err)
}
defer file.Close()
// 从文件中读取len(b)字节的文件。
// 返回0字节意味着读取到文件尾了
// 读取到文件会返回io.EOF的error
byteSlice := make([]byte, 16)
bytesRead, err := file.Read(byteSlice)
if err != nil {
log.Fatal(err)
}
log.Printf("Number of bytes read: %d\n", bytesRead)
log.Printf("Data read: %s\n", byteSlice)
}

读取正好N个字节


package main
import (
"os"
"log"
"io"
)
func main() {
// Open file for reading
file, err := os.Open("test.txt")
if err != nil {
log.Fatal(err)
}
// file.Read()可以读取一个小文件到大的byte slice中,
// 但是io.ReadFull()在文件的字节数小于byte slice字节数的时候会返回错误
byteSlice := make([]byte, 2)
numBytesRead, err := io.ReadFull(file, byteSlice)
if err != nil {
log.Fatal(err)
}
log.Printf("Number of bytes read: %d\n", numBytesRead)
log.Printf("Data read: %s\n", byteSlice)
}

读取至少N个字节


package main
import (
"os"
"log"
"io"
)
func main() {
// 打开文件,只读
file, err := os.Open("test.txt")
if err != nil {
log.Fatal(err)
}
byteSlice := make([]byte, 512)
minBytes := 8
// io.ReadAtLeast()在不能得到最小的字节的时候会返回错误,但会把已读的文件保留
numBytesRead, err := io.ReadAtLeast(file, byteSlice, minBytes)
if err != nil {
log.Fatal(err)
}
log.Printf("Number of bytes read: %d\n", numBytesRead)
log.Printf("Data read: %s\n", byteSlice)
}

读取全部字节


package main
import (
"os"
"log"
"fmt"
"io/ioutil"
)
func main() {
file, err := os.Open("test.txt")
if err != nil {
log.Fatal(err)
}
// os.File.Read(), io.ReadFull() 和
// io.ReadAtLeast() 在读取之前都需要一个固定大小的byte slice。
// 但ioutil.ReadAll()会读取reader(这个例子中是file)的每一个字节,然后把字节slice返回。
data, err := ioutil.ReadAll(file)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Data as hex: %x\n", data)
fmt.Printf("Data as string: %s\n", data)
fmt.Println("Number of bytes read:", len(data))
}

快读到内存


package main
import (
"log"
"io/ioutil"
)
func main() {
// 读取文件到byte slice中
data, err := ioutil.ReadFile("test.txt")
if err != nil {
log.Fatal(err)
}
log.Printf("Data read: %s\n", data)
}

使用缓存读


有缓存写也有缓存读。
缓存reader会把一些内容缓存在内存中。它会提供比os.File和io.Reader更多的函数,缺省的缓存大小是4096,最小缓存是16。


package main
import (
"os"
"log"
"bufio"
"fmt"
)
func main() {
// 打开文件,创建buffered reader
file, err := os.Open("test.txt")
if err != nil {
log.Fatal(err)
}
bufferedReader := bufio.NewReader(file)
// 得到字节,当前指针不变
byteSlice := make([]byte, 5)
byteSlice, err = bufferedReader.Peek(5)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Peeked at 5 bytes: %s\n", byteSlice)
// 读取,指针同时移动
numBytesRead, err := bufferedReader.Read(byteSlice)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Read %d bytes: %s\n", numBytesRead, byteSlice)
// 读取一个字节, 如果读取不成功会返回Error
myByte, err := bufferedReader.ReadByte()
if err != nil {
log.Fatal(err)
}
fmt.Printf("Read 1 byte: %c\n", myByte)
// 读取到分隔符,包含分隔符,返回byte slice
dataBytes, err := bufferedReader.ReadBytes('\n')
if err != nil {
log.Fatal(err)
}
fmt.Printf("Read bytes: %s\n", dataBytes)
// 读取到分隔符,包含分隔符,返回字符串
dataString, err := bufferedReader.ReadString('\n')
if err != nil {
log.Fatal(err)
}
fmt.Printf("Read string: %s\n", dataString)
//这个例子读取了很多行,所以test.txt应该包含多行文本才不至于出错
}

使用 scanner


Scanner是bufio包下的类型,在处理文件中以分隔符分隔的文本时很有用。
通常我们使用换行符作为分隔符将文件内容分成多行。在CSV文件中,逗号一般作为分隔符。
os.File文件可以被包装成bufio.Scanner,它就像一个缓存reader。
我们会调用Scan()方法去读取下一个分隔符,使用Text()或者Bytes()获取读取的数据。


分隔符可以不是一个简单的字节或者字符,有一个特殊的方法可以实现分隔符的功能,以及将指针移动多少,返回什么数据。
如果没有定制的SplitFunc提供,缺省的ScanLines会使用newline字符作为分隔符,其它的分隔函数还包括ScanRunes和ScanWords,皆在bufio包中。


// To define your own split function, match this fingerprint
type SplitFunc func(data []byte, atEOF bool) (advance int, token []byte, err error)
// Returning (0, nil, nil) will tell the scanner
// to scan again, but with a bigger buffer because
// it wasn't enough data to reach the delimiter
下面的例子中,为一个文件创建了bufio.Scanner,并按照单词逐个读取:

package main
import (
"os"
"log"
"fmt"
"bufio"
)
func main() {
file, err := os.Open("test.txt")
if err != nil {
log.Fatal(err)
}
scanner := bufio.NewScanner(file)
// 缺省的分隔函数是bufio.ScanLines,我们这里使用ScanWords。
// 也可以定制一个SplitFunc类型的分隔函数
scanner.Split(bufio.ScanWords)
// scan下一个token.
success := scanner.Scan()
if success == false {
// 出现错误或者EOF是返回Error
err = scanner.Err()
if err == nil {
log.Println("Scan completed and reached EOF")
} else {
log.Fatal(err)
}
}
// 得到数据,Bytes() 或者 Text()
fmt.Println("First word found:", scanner.Text())
// 再次调用scanner.Scan()发现下一个token
}

压缩


打包(zip) 文件


// This example uses zip but standard library
// also supports tar archives
package main
import (
"archive/zip"
"log"
"os"
)
func main() {
// 创建一个打包文件
outFile, err := os.Create("test.zip")
if err != nil {
log.Fatal(err)
}
defer outFile.Close()
// 创建zip writer
zipWriter := zip.NewWriter(outFile)
// 往打包文件中写文件。
// 这里我们使用硬编码的内容,你可以遍历一个文件夹,把文件夹下的文件以及它们的内容写入到这个打包文件中。
var filesToArchive = []struct {
Name, Body string
} {
{"test.txt", "String contents of file"},
{"test2.txt", "\x61\x62\x63\n"},
}
// 下面将要打包的内容写入到打包文件中,依次写入。
for _, file := range filesToArchive {
fileWriter, err := zipWriter.Create(file.Name)
if err != nil {
log.Fatal(err)
}
_, err = fileWriter.Write([]byte(file.Body))
if err != nil {
log.Fatal(err)
}
}
// 清理
err = zipWriter.Close()
if err != nil {
log.Fatal(err)
}
}

抽取(unzip) 文件


// This example uses zip but standard library
// also supports tar archives
package main
import (
"archive/zip"
"log"
"io"
"os"
"path/filepath"
)
func main() {
zipReader, err := zip.OpenReader("test.zip")
if err != nil {
log.Fatal(err)
}
defer zipReader.Close()
// 遍历打包文件中的每一文件/文件夹
for _, file := range zipReader.Reader.File {
// 打包文件中的文件就像普通的一个文件对象一样
zippedFile, err := file.Open()
if err != nil {
log.Fatal(err)
}
defer zippedFile.Close()
// 指定抽取的文件名。
// 你可以指定全路径名或者一个前缀,这样可以把它们放在不同的文件夹中。
// 我们这个例子使用打包文件中相同的文件名。
targetDir := "./"
extractedFilePath := filepath.Join(
targetDir,
file.Name,
)
// 抽取项目或者创建文件夹
if file.FileInfo().IsDir() {
// 创建文件夹并设置同样的权限
log.Println("Creating directory:", extractedFilePath)
os.MkdirAll(extractedFilePath, file.Mode())
} else {
//抽取正常的文件
log.Println("Extracting file:", file.Name)
outputFile, err := os.OpenFile(
extractedFilePath,
os.O_WRONLY|os.O_CREATE|os.O_TRUNC,
file.Mode(),
)
if err != nil {
log.Fatal(err)
}
defer outputFile.Close()
// 通过io.Copy简洁地复制文件内容
_, err = io.Copy(outputFile, zippedFile)
if err != nil {
log.Fatal(err)
}
}
}
}

压缩文件


// 这个例子中使用gzip压缩格式,标准库还支持zlib, bz2, flate, lzw
package main
import (
"os"
"compress/gzip"
"log"
)
func main() {
outputFile, err := os.Create("test.txt.gz")
if err != nil {
log.Fatal(err)
}
gzipWriter := gzip.NewWriter(outputFile)
defer gzipWriter.Close()
// 当我们写如到gizp writer数据时,它会依次压缩数据并写入到底层的文件中。
// 我们不必关心它是如何压缩的,还是像普通的writer一样操作即可。
_, err = gzipWriter.Write([]byte("Gophers rule!\n"))
if err != nil {
log.Fatal(err)
}
log.Println("Compressed data written to file.")
}

解压缩文件


// 这个例子中使用gzip压缩格式,标准库还支持zlib, bz2, flate, lzw
package main
import (
"compress/gzip"
"log"
"io"
"os"
)
func main() {
// 打开一个gzip文件。
// 文件是一个reader,但是我们可以使用各种数据源,比如web服务器返回的gzipped内容,
// 它的内容不是一个文件,而是一个内存流
gzipFile, err := os.Open("test.txt.gz")
if err != nil {
log.Fatal(err)
}
gzipReader, err := gzip.NewReader(gzipFile)
if err != nil {
log.Fatal(err)
}
defer gzipReader.Close()
// 解压缩到一个writer,它是一个file writer
outfileWriter, err := os.Create("unzipped.txt")
if err != nil {
log.Fatal(err)
}
defer outfileWriter.Close()
// 复制内容
_, err = io.Copy(outfileWriter, gzipReader)
if err != nil {
log.Fatal(err)
}
}

其它



临时文件和目录


ioutil提供了两个函数: TempDir() 和 TempFile()。
使用完毕后,调用者负责删除这些临时文件和文件夹。
有一点好处就是当你传递一个空字符串作为文件夹名的时候,它会在操作系统的临时文件夹中创建这些项目(/tmp on Linux)。
os.TempDir()返回当前操作系统的临时文件夹。


package main
import (
"os"
"io/ioutil"
"log"
"fmt"
)
func main() {
// 在系统临时文件夹中创建一个临时文件夹
tempDirPath, err := ioutil.TempDir("", "myTempDir")
if err != nil {
log.Fatal(err)
}
fmt.Println("Temp dir created:", tempDirPath)
// 在临时文件夹中创建临时文件
tempFile, err := ioutil.TempFile(tempDirPath, "myTempFile.txt")
if err != nil {
log.Fatal(err)
}
fmt.Println("Temp file created:", tempFile.Name())
// ... 做一些操作 ...
// 关闭文件
err = tempFile.Close()
if err != nil {
log.Fatal(err)
}
// 删除我们创建的资源
err = os.Remove(tempFile.Name())
if err != nil {
log.Fatal(err)
}
err = os.Remove(tempDirPath)
if err != nil {
log.Fatal(err)
}
}

通过HTTP下载文件


package main
import (
"os"
"io"
"log"
"net/http"
)
func main() {
newFile, err := os.Create("devdungeon.html")
if err != nil {
log.Fatal(err)
}
defer newFile.Close()
url := "http://www.devdungeon.com/archive"
response, err := http.Get(url)
defer response.Body.Close()
// 将HTTP response Body中的内容写入到文件
// Body满足reader接口,因此我们可以使用ioutil.Copy
numBytesWritten, err := io.Copy(newFile, response.Body)
if err != nil {
log.Fatal(err)
}
log.Printf("Downloaded %d byte file.\n", numBytesWritten)
}

哈希和摘要


package main
import (
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"log"
"fmt"
"io/ioutil"
)
func main() {
// 得到文件内容
data, err := ioutil.ReadFile("test.txt")
if err != nil {
log.Fatal(err)
}
// 计算Hash
fmt.Printf("Md5: %x\n\n", md5.Sum(data))
fmt.Printf("Sha1: %x\n\n", sha1.Sum(data))
fmt.Printf("Sha256: %x\n\n", sha256.Sum256(data))
fmt.Printf("Sha512: %x\n\n", sha512.Sum512(data))
}

上面的例子复制整个文件内容到内存中,传递给hash函数。
另一个方式是创建一个hash writer, 使用Write、WriteString、Copy将数据传给它。
下面的例子使用 md5 hash,但你可以使用其它的Writer。


```go
package main
import (
"crypto/md5"
"log"
"fmt"
"io"
"os"
)
func main() {
file, err := os.Open("test.txt")
if err != nil {
log.Fatal(err)
}
defer file.Close()
//创建一个新的hasher,满足writer接口
hasher := md5.New()
_, err = io.Copy(hasher, file)
if err != nil {
log.Fatal(err)
}
// 计算hash并打印结果。
// 传递 nil 作为参数,因为我们不通参数传递数据,而是通过writer接口。
sum := hasher.Sum(nil)
fmt.Printf("Md5 checksum: %x\n", sum)
}

10.13 每日早报

astaxie 发表了文章 • 0 个评论 • 406 次浏览 • 2016-10-13 08:16 • 来自相关话题

10.13 每日早报

新闻:

1.IMAX将于2016年底前在欧洲开设首个虚拟现实体验中心

2.饿了么、阿里旅行和51社保等企业接入阿里钉钉平台

3.网易有道与考神团队成立教育合资公司,将投入5亿元... 查看全部

10.13 每日早报


新闻:


1.IMAX将于2016年底前在欧洲开设首个虚拟现实体验中心


2.饿了么、阿里旅行和51社保等企业接入阿里钉钉平台


3.网易有道与考神团队成立教育合资公司,将投入5亿元孵化教育工作室


4.百度成立百度资本,投资泛互联网领域中后期项目,一期基金200亿


5.英特尔推出自主品牌工业无人机Falcon 8+,聚焦工业场合应用


6.亚马逊计划开设便利店和路边取货点,用户可在路边区域提取商品


7.阿里游戏、金立游戏等多家手游平台提醒开发者提供总局出版批文


8.德国航空航天中心完成高清版3D世界地图,可免费用于科研项目


资源:


2016年中国视频网站付费用户典型案例研究报告
http://report.iresearch.cn/report/201610/2653.shtml


中国的移动互联网Top 2000 APP秋季盘点
http://www.questmobile.com.cn/blog/blog_61.html


注:上述内容来源于互联网,由EGO整理

Python 程序员的 Golang 学习指南(II): 开发环境搭建

Cloudinsight 发表了文章 • 0 个评论 • 1631 次浏览 • 2016-10-12 15:44 • 来自相关话题

Authors: startover

Authors: startover





上一篇文章我们已经对 Golang 有了初步的了解,这篇主要介绍如何在 Ubuntu 14.04 上搭建 Golang 开发环境。


安装 Golang


这里就按照官方文档进行安装即可,如下:



  • 下载并解压安装包到指定目录


$ wget https://storage.googleapis.com ... ar.gz
$ tar -C /usr/local -xzf go1.6.3.linux-amd64.tar.gz


  • 设置 PATH


$ echo "export PATH=$PATH:/usr/local/go/bin" >> ~/.bashrc
$ source ~/.bashrc


  • 验证安装


$ go version
go version go1.6.3 linux/amd64

环境变量设置


$ echo "export GOROOT=/usr/local/go" >> ~/.bashrc
$ echo "export GOPATH=$HOME/go" >> ~/.bashrc
$ source ~/.bashrc

其中,GOROOT 为 Golang 的安装目录,只有当 Golang 安装到除 /usr/local 之外的路径时需要设置,反之则不用设置,GOPATH 是 Golang 的开发目录,详细可参考官方文档


开发工具


工欲善其事,必先利其器,作为一名伪 VIMer,这里主要介绍下如何在 Vim 下配置 Golang 开发环境。


由于之前一直使用 k-vim 作为 Python 开发环境,而 k-vim 已经集成了当前使用最为广泛的用于搭建 Golang 开发环境的 vim 插件 vim-go,只是默认没有开启,需要我们手动进行相关设置。


k-vim 中开启 Golang 语言的支持,非常简单,如下:



  • 修改 ~/.vimrc.bundles(开启 golang 支持,并修改 vim-go 的默认配置,增加快捷键配置等)。


let g:bundle_groups=['python', 'javascript', 'markdown', 'html', 'css', 'tmux', 'beta', 'json', 'golang']

" vimgo {{{
let g:go_highlight_functions = 1
let g:go_highlight_methods = 1
let g:go_highlight_structs = 1
let g:go_highlight_operators = 1
let g:go_highlight_build_constraints = 1

let g:go_fmt_fail_silently = 1
let g:go_fmt_command = "goimports"
let g:syntastic_go_checkers = ['golint', 'govet', 'errcheck']

" vim-go custom mappings
au FileType go nmap <Leader>s <Plug>(go-implements)
au FileType go nmap <Leader>i <Plug>(go-info)
au FileType go nmap <Leader>gd <Plug>(go-doc)
au FileType go nmap <Leader>gv <Plug>(go-doc-vertical)
au FileType go nmap <leader>r <Plug>(go-run)
au FileType go nmap <leader>b <Plug>(go-build)
au FileType go nmap <leader>t <Plug>(go-test)
au FileType go nmap <leader>c <Plug>(go-coverage)
au FileType go nmap <Leader>ds <Plug>(go-def-split)
au FileType go nmap <Leader>dv <Plug>(go-def-vertical)
au FileType go nmap <Leader>dt <Plug>(go-def-tab)
au FileType go nmap <Leader>e <Plug>(go-rename)
au FileType go nnoremap <leader>gr :GoRun %<CR>
" }}}



  • 在 Vim 内执行 :PlugInstall,安装 vim-go




  • 在 Vim 内执行 :GoInstallBinaries,下载并安装 vim-go 依赖的二进制工具,goimportsgolint 等。



  • 安装 gotags,使 tagbar 配置生效。


$ go get -u github.com/jstemmer/gotags

我们来看一下最终效果:


Image of Golang Environment in Vim


编写第一个程序


进入工作目录,新建文件 hello.go,如下:


$ cd $GOPATH
$ vim hello.go
package main

import "fmt"

func main() {
fmt.Println("Hello, World!")
}

运行程序:


$ go run hello.go
Hello, World!



本文章为 Cloudinsight 技术团队工程师原创,更多技术文章可访问 Cloudinsight 技术博客Cloudinsight 为可视化系统监控工具,涵盖 Windows、Linux 操作系统,用 Golang 开发的 Cloudinsight Agent 正式开源了,欢迎 fork,Github:https://github.com/cloudinsight/cloudinsight-agent


golang-for-pythonistas 系列持续更新中,欢迎关注~

Python 程序员的 Golang 学习指南(I): Go 之初体验

Cloudinsight 发表了文章 • 3 个评论 • 1605 次浏览 • 2016-10-12 15:27 • 来自相关话题

Authors: startover

Authors: startover





Go 语言简介


Go,又称 golang,是 Google 开发的一种静态强类型,编译型,并发型,并具有垃圾回收功能的编程语言。


Go 语言于2009年11月正式宣布推出,自2012年发布1.0,最新稳定版1.7。目前,Go的相关工具和生态已逐渐趋于完善,也不乏重量级项目,如 Docker, Kubernetes, Etcd, InfluxDB 等。


Go 语言能解决什么样的问题


同绝大多数通用型编程语言相比,Go 语言更多的是为了解决我们在构建大型服务器软件过程中所遇到的软件工程方面的问题而设计的。乍看上去,这么讲可能会让人感觉 Go 非常无趣且工业化,但实际上,在设计过程中就着重于清晰和简洁,以及较高的可组合性,最后得到的反而会是一门使用起来效率高而且很有趣的编程语言,很多程序员都会发现,它有极强的表达力而且功能非常强大。


总结为以下几点:



  • 清晰的依赖关系

  • 清晰的语法

  • 清晰的语义

  • 偏向组合而不是继承

  • 提供简单的编程模型(垃圾回收、并发)

  • 强大的内置工具(gofmt、godoc、gofix等)


建议有兴趣的同学看看 Go在谷歌:以软件工程为目的的语言设计


Go 语言相对 Python 有哪些优势


这里引用一段知乎上某大牛的回答,如下:




  • 部署简单。Go 编译生成的是一个静态可执行文件,除了 glibc 外没有其他外部依赖。这让部署变得异常方便:目标机器上只需要一个基础的系统和必要的管理、监控工具,完全不需要操心应用所需的各种包、库的依赖关系,大大减轻了维护的负担。这和 Python 有着巨大的区别。由于历史的原因,Python 的部署工具生态相当混乱【比如 setuptools, distutils, pip, buildout 的不同适用场合以及兼容性问题】。官方 PyPI 源又经常出问题,需要搭建私有镜像,而维护这个镜像又要花费不少时间和精力。




  • 并发性好。Goroutine 和 channel 使得编写高并发的服务端软件变得相当容易,很多情况下完全不需要考虑锁机制以及由此带来的各种问题。单个 Go 应用也能有效的利用多个 CPU 核,并行执行的性能好。这和 Python 也是天壤之比。多线程和多进程的服务端程序编写起来并不简单,而且由于全局锁 GIL 的原因,多线程的 Python 程序并不能有效利用多核,只能用多进程的方式部署;如果用标准库里的 multiprocessing 包又会对监控和管理造成不少的挑战【我们用的 supervisor 管理进程,对 fork 支持不好】。部署 Python 应用的时候通常是每个 CPU 核部署一个应用,这会造成不少资源的浪费,比如假设某个 Python 应用启动后需要占用 100MB 内存,而服务器有 32 个 CPU 核,那么留一个核给系统、运行 31 个应用副本就要浪费 3GB 的内存资源。




  • 良好的语言设计。从学术的角度讲 Go 语言其实非常平庸,不支持许多高级的语言特性;但从工程的角度讲,Go 的设计是非常优秀的:规范足够简单灵活,有其他语言基础的程序员都能迅速上手。更重要的是 Go 自带完善的工具链,大大提高了团队协作的一致性。比如 gofmt 自动排版 Go 代码,很大程度上杜绝了不同人写的代码排版风格不一致的问题。把编辑器配置成在编辑存档的时候自动运行 gofmt,这样在编写代码的时候可以随意摆放位置,存档的时候自动变成正确排版的代码。此外还有 gofix, govet 等非常有用的工具。



  • 执行性能好。虽然不如 C 和 Java,但通常比原生 Python 应用还是高一个数量级的,适合编写一些瓶颈业务。内存占用也非常省。


从个人对 Golang 的初步使用来说,体验还是相当不错的,但是也有下面几点需要注意:




  • 驼峰式命名风格(依据首字母大小写来决定其是否能被其他包引用),但我更喜欢 Python 的小写字母加下划线命名风格。




  • 没有好用的包管理器,Golang 官方也没有推荐最佳的包管理方案,目前公认的比较好用的有 Godeps, Govendor 及 Glide,而 Python 的包管理器 pip 已形成自己的一套标准。




  • 多行字符串的变量声明需要用反引号(`),Python 里是三个双引号("""),参考http://stackoverflow.com/questions/7933460/how-do-you-write-multiline-strings-in-go




  • Golang 中的类型匹配是很严格的,不同的类型之间通常需要手动转换,所以在字符串拼接时往往需要对整型进行显式转换,如 fmt.Println("num: " + strconv.Itoa(1))



  • Golang 语言语法里的语法糖并不多,如在 Python 中很流行的 map, reduce, range 等,在 Golang 里都没有得到支持。


另外,推荐阅读 Golang 新手开发者要注意的陷阱和常见错误


学习资料推荐


建议先把 Go 的官方文档过一遍,主要有以下几项:



官方文档看完后,基本也算入门了,这时候可以看看 Go 的示例代码,或者去 Project Euler 刷刷题。


当然也可以去知乎看看大牛们都是如何学习的,链接 https://www.zhihu.com/question/23486344


总结


虽然 Go 有很多被诟病的地方,比如 GC 和对错误的处理方式,但没有任何语言是完美的,从实用角度来讲,Go 有着不输于 Python 的开发效率,完善的第三方工具,以及强大的社区支持,这些就足够了。


相关链接:

https://golang.org/doc/

https://talks.golang.org/2012/splash.article

https://www.zhihu.com/question/21409296

https://www.zhihu.com/question/23486344

http://stackoverflow.com/questions/7933460/how-do-you-write-multiline-strings-in-go

http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/

http://www.oschina.net/translate/go-at-google-language-design-in-the-service-of-software-engineering




本文章为 Cloudinsight 技术团队工程师原创,更多技术文章可访问 Cloudinsight 技术博客Cloudinsight 为可视化系统监控工具,涵盖 Windows、Linux 操作系统,用 Golang 开发的 Cloudinsight Agent 正式开源了,欢迎 fork,Github:https://github.com/cloudinsight/cloudinsight-agent


golang-for-pythonistas 系列持续更新中,欢迎关注~

Go语言并发模型:使用 select

ming 发表了文章 • 0 个评论 • 1123 次浏览 • 2016-10-12 12:30 • 来自相关话题

此文章已得到翻译者授权转载,点击查看原文

... 查看全部


此文章已得到翻译者授权转载,点击查看原文



简介


作为一种现代语言,go语言实现了对并发的原生支持。上几期文章中,我们对goroutine 和 channel进行了详细的讲解。
但是要实现对 channel 的控制,从语言层面上来说,select 语句是必不可少的部分。本文中,我们就 select 语句的
行为和使用方法进行深入讨论。


阅读建议


本文中的内容是 Go语言并发模型的一篇,但是与上几期关系不是特别密切,可以独立阅读。本文的内容源自于
go language specifications
Rob Pike 在2012年进行的一场名为"concurrency"
的演讲。如果有时间的话,建议在 YouTube 上看一下他本人的演讲。


select 语句的行为


为了便于理解,我们首先给出一个代码片段:


// https://talks.golang.org/2012/concurrency.slide#32
select {
case v1 := <-c1:
fmt.Printf("received %v from c1\n", v1)
case v2 := <-c2:
fmt.Printf("received %v from c2\n", v1)
case c3 <- 23:
fmt.Printf("sent %v to c3\n", 23)
default:
fmt.Printf("no one was ready to communicate\n")
}

上面这段代码中,select 语句有四个 case 子语句,前两个是 receive 操作,第三个是 send 操作,最后一个是默认操作。
代码执行到 select 时,case 语句会按照源代码的顺序被评估,且只评估一次,评估的结果会出现下面这几种情况:



  1. 除 default 外,如果只有一个 case 语句评估通过,那么就执行这个case里的语句;

  2. 除 default 外,如果有多个 case 语句评估通过,那么通过伪随机的方式随机选一个;

  3. 如果 default 外的 case 语句都没有通过评估,那么执行 default 里的语句;

  4. 如果没有 default,那么 代码块会被阻塞,指导有一个 case 通过评估;否则一直阻塞


如果 case 语句中 的 receive 操作的对象是 nil channel,那么也会阻塞,下面我们看一个更全面、用法也更高级的例子:


// https://golang.org/ref/spec#Select_statements
var a []int
var c, c1, c2, c3, c4 chan int
var i1, i2 int
select {
case i1 = <-c1:
print("received ", i1, " from c1\n")
case c2 <- i2:
print("sent ", i2, " to c2\n")
case i3, ok := (<-c3): // same as: i3, ok := <-c3
if ok {
print("received ", i3, " from c3\n")
} else {
print("c3 is closed\n")
}
case a[f()] = <-c4:
// same as:
// case t := <-c4
// a[f()] = t
default:
print("no communication\n")
}

for { // 向 channel c 发送随机 bit 串
select {
case c <- 0: // note: no statement, no fallthrough, no folding of cases
case c <- 1:
}
}

select {} // 永久阻塞

注意:与 C/C++ 等传统编程语言不同,go语言的 case 语句不需要 break 关键字去跳出 select。


select 的使用


为请求设置超时时间


在 golang 1.7 之前, http 包并没有引入 context 支持,通过 http.Client 向一个坏掉的服务发送请求会导致响应缓慢。
类似的场景下,我们可以使用 select 控制服务响应时间,下面是一个简单的demo:


func main() {
c := boring("Joe")
timeout := time.After(5 * time.Second)
for {
select {
case s := <-c:
fmt.Println(s)
case <-timeout:
fmt.Println("You talk too much.")
return
}
}
}

done channel


上几期的文章中,我们均讨论过 done channel,它可以用于保证流水线上每个阶段goroutine 的退出。在 golang.org/x/net 包中,
done channel 被广泛应用。这里我们看一眼 golang.org/x/net/context/ctxhttp 中 Do 方法的实现:


// https://github.com/golang/net/ ... tp.go

// Do sends an HTTP request with the provided http.Client and returns
// an HTTP response.
//
// If the client is nil, http.DefaultClient is used.
//
// The provided ctx must be non-nil. If it is canceled or times out,
// ctx.Err() will be returned.
func Do(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) {
if client == nil {
client = http.DefaultClient
}
resp, err := client.Do(req.WithContext(ctx))
// If we got an error, and the context has been canceled,
// the context's error is probably more useful.
if err != nil {
select {
case <-ctx.Done():
err = ctx.Err()
default:
}
}
return resp, err
}

quit channel


在很多场景下,quit channel 和 done channel 是一个概念。在并发程序中,通常 main routine 将
任务分给其它 go routine 去完成,而自身只是起到调度作用。这种情况下,main 函数无法知道 其它goroutine
任务是否完成,此时我们需要 quit channel;对于更细粒度的控制,比如完成多少,还是需要 done channel (参考WaitGroup)。
下面是 quit channel 的一个例子,首先是 main routine:


// 创建 quit channel
quit := make(chan string)
// 启动生产者 goroutine
c := boring("Joe", quit)
// 从生产者 channel 读取结果
for i := rand.Intn(10); i >= 0; i-- { fmt.Println(<-c) }
// 通过 quit channel 通知生产者停止生产
quit <- "Bye!"
fmt.Printf("Joe says: %q\n", <-quit)

我们再看 生产者 go routine 中与 quit channel 相关的部分:


select {
case c <- fmt.Sprintf("%s: %d", msg, i):
// do nothing
case <-quit:
cleanup()
quit <- "See you!"
return
}

Google Search (延伸阅读)


Google Search 是一个很经典的例子,由于代码较多,有兴趣的童鞋查看 Rob Pike 的 ppt
更高阶的并发方式可以阅读 Sameer Ajmani 的 ppt Advanced Go Concurrency Patterns


并发相关的主题就先到这里,下一期文章中,我们会讨论go语言测试工具链中的单元测试。


相关链接:



  1. Rob Pike演讲:concurrency

  2. [language specification: select statement](https://golang.org/ref/spec#Select_statements “select")


扫码关注微信公众号“深入Go语言”


在这里