10.20 每日早报

astaxie 发表了文章 • 1 个评论 • 391 次浏览 • 2016-10-20 08:04 • 来自相关话题

10.20 每日早报

新闻:

1.谷歌公布基于Java的顶级域名注册服务Nomulus开源代码

2.Fedora项目正式加入对树莓派2和树莓派3单板计算机的系统支持

3.惠普推出掌上打印机Sproc... 查看全部

10.20 每日早报


新闻:


1.谷歌公布基于Java的顶级域名注册服务Nomulus开源代码


2.Fedora项目正式加入对树莓派2和树莓派3单板计算机的系统支持


3.惠普推出掌上打印机Sprocket,通过蓝牙连接手机打印照片


4.Airbnb同SolarCity达成跨界合作,鼓励房东使用太阳能


5.摩拜推出新款单车Mobike Lite,价格更便宜每半小时0.5元


6.华为公布新一代自研芯片麒麟960,图形处理性能提升180%,GPU能效提升20%


7.企业空间交易服务平台空间家获得2.58亿元人民币A轮融资,翰同资本和永柏投资领投


8.滴滴出行宣布与旅游平台猫途鹰战略合作,在上百家五星酒店建滴滴车站


资源:


2016年中国场景营销市场研究报告
http://report.iresearch.cn/report/201610/2654.shtml


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

Python 程序员的 Golang 学习指南(IV): 包管理篇

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

Authors: startover

Authors: startover





第一篇文章我们有提到,Golang 官方并没有推荐最佳的包管理方案,对于像我这样习惯了 Python 包管理的开发者,自然还是希望有像 pip 一样好用的工具,帮助我们进行依赖管理,下面就让我们对 Golang 的包管理机制一探究竟。


Golang 包管理机制


Go 语言的包管理系统是去中心化的,我们可以通过 go get 命令获取存放在远程仓库的代码协议。实际上,go get 支持以下 VCS 协议:
































名称 主命令 说明
Mercurial hg Mercurial是一种轻量级分布式版本控制系统,采用Python语言实现,易于学习和使用,扩展性强。
Git git Git最开始是Linux Torvalds为了帮助管理 Linux 内核开发而开发的一个开源的分布式版本控制软件。但现在已被广泛使用。它是被用来进行有效、高速的各种规模项目的版本管理。
Subversion svn Subversion是一个版本控制系统,也是第一个将分支概念和功能纳入到版本控制模型的系统。但相对于Git和Mercurial而言,它只算是传统版本控制系统的一员。
Bazaar bzr Bazaar是一个开源的分布式版本控制系统。但相比而言,用它来作为VCS的项目并不多。

比如,我们现在需要获取 godep 这个项目,可以执行如下命令:


$ go get github.com/tools/godep

需要指出的是,go get 实际上执行了两个步骤:1. 下载源码包;2. 执行 go install,如果只下载不安装,则需要指定 -d 参数,如下:


$ go get -d github.com/tools/godep

除了 go get,Go 语言还提供了一个 Workspace 的机制,即通过设定 GOPATH 环境变量,指定除了 GOROOT 所指定的目录之外,Go 代码所在的位置(也就是 Workspace 的位置)。 一般来说,GOPATH 目录下会包含 pkg、src 和 bin 三个子目录,这三个目录各有用处。



  • bin 目录用于放置编译好的可执行文件,为了使得这里的可执行文件可以方便的运行, 可在 shell 中设置 PATH 环境变量。

  • src 目录用于放置代码源文件,在进行 import 时,是使用这个位置作为根目录的。自己编写的代码也应该放在这下面。

  • pkg 用来放置安装的包的链接对象(Object)的。这个概念有点类似于链接库,Go 会将编译出的可连接库放在这里, 方便编译时链接。不同的系统和处理器架构的对象会在 pkg 存放在不同的文件夹中。


Golang 包管理现状


显然,通过 go getWorkspace 的方式并不足以解决项目依赖和版本依赖的问题,主要有以下几点:



  1. 第三方包的版本控制。如果没有明确指定依赖的第三方包的版本,团队开发很容易导入不一样的版本,导致项目无法正常运行。

  2. 第三方包没有内容安全审计,很容易引入代码 Bug,这是泛中心化包管理普遍存在的问题。

  3. 依赖的完整性无法校验,程序编译时无法保障百分百成功。


因此,我们必须借助第三方工具来解决这些问题。


第三方解决方案


这里我从官方推荐包管理工具中挑选了几个比较常用的工具:Godep, Govendor 以及 Glide,作下简单介绍。


Godep



  • godep save


这个命令做了以下几件事:




  1. 查找项目中所用到的所有的第三方包。

  2. 在项目目录下创建 Godeps 目录,Godeps/Godeps.json 是依赖文件,包括了 go 的版本,用到的第三方包的引入路径,版本号等信息,json 文件需要一并加入到版本控制里。

  3. 所有依赖的第三方包的代码会被拷贝到 vendor/ 下,并且移除了 .git 这样的版本控制信息。




  • godep restore


当下载别人发布的项目时,如果下载的项目中只有 Godeps.json 文件,而没有包含第三方包,则可以使用 godep restore 这个命令将所有的依赖包下载到 $GOPATH 目录下,而不用一个一个去 go get,还是很方便的。


Govendor



  • govendor init


执行 govendor init 会在根目录下生成一个 vendor 文件夹,以及 vendor/vendor.json,其中 vendor.json 类似 godep 工具中的描述文件版本的功能。



  • govendor add +external


执行 govendor add +external 会将所有依赖的第三方包的代码拷贝到 vendor 文件夹下,并且移除了 .git 这样的版本控制信息,测试所需依赖以及依赖项目的测试文件。与 godep save 的功能类似。



  • govendor fetch


执行 govendor fetch 新增的第三方包直接被 get 到根目录的 vendor 文件夹下,不会与其它的项目混用第三方包,完美避免了多个项目同用同一个第三方包的不同版本问题。


这样,我们只需对 vendor/vendor.json 进行版本控制,即可对第三包依赖关系进行控制。


Glide



  • glide init


执行 glide initglide create 会在项目根目录下生成一个 glide.yaml,这个文件用来记录项目用到的第三方包的依赖关系,并支持编辑修改。



  • glide install


执行 glide install,会把所有依赖的第三方包都下载到 vendor 文件夹下,并且会在 glide.yaml 中添加所有依赖的第三方包名称,以及在 glide.lock 文件中记录具体的版本管理信息。


总结


上面我们分别对 Godep, Govendor 以及 Glide 这三种工具做了简单的介绍,对于 Python 开发者,个人还是比较认同 Govendor 的方式,因为其很容易实现类似 Virtualenv 的模式,从而实现不同程序使用不同版本依赖的目的。


当然,如果你是 Node.js 的开发者,可能对于 Godep 有更加熟悉的感觉,而对于 Ruby 开发者,gom 会让你感到更加亲切。


因此,针对第三方包管理工具的选择,现阶段还完全交由开发者做裁定,这里就“仁者见仁,智者见智”了。


相关链接:

http://www.infoq.com/cn/articles/golang-package-management

https://io-meter.com/2014/07/30/go's-package-management/

https://github.com/golang/go/wiki/PackageManagementTools




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


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

TensorFlow支持Go语言了

astaxie 发表了文章 • 1 个评论 • 2994 次浏览 • 2016-10-19 11:12 • 来自相关话题

TensorFlow 简介

TensorFlow是Google在2015年11月份开源的人工智能系统(Github项目地址),是之前所开发的深度学习基础架构DistBelief的改进版本,该系统可以被用于语音识别、图片识别等多个领域。<... 查看全部

TensorFlow 简介


TensorFlow是Google在2015年11月份开源的人工智能系统(Github项目地址),是之前所开发的深度学习基础架构DistBelief的改进版本,该系统可以被用于语音识别、图片识别等多个领域。


官网上对TensorFlow的介绍是,一个使用数据流图(data flow graphs)技术来进行数值计算的开源软件库。数据流图中的节点,代表数值运算;节点节点之间的边,代表多维数据(tensors)之间的某种联系。你可以在多种设备(含有CPU或GPU)上通过简单的API调用来使用该系统的功能。TensorFlow是由Google Brain团队的研发人员负责的项目。


https://github.com/tensorflow/tensorflow/tree/master/tensorflow/go


详细的讨论在这里,https://github.com/tensorflow/tensorflow/issues/10#issuecomment-245687757

10.19 每日早报

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

10.19 每日早报

新闻:

1.Linux基金会宣布JS Foundation基金会成立,其前身为jQuery团队

2.苏宁阿里拟共同出资10亿元设立重庆猫宁电子商务有限公司,苏宁占股51%

3.... 查看全部

10.19 每日早报


新闻:


1.Linux基金会宣布JS Foundation基金会成立,其前身为jQuery团队


2.苏宁阿里拟共同出资10亿元设立重庆猫宁电子商务有限公司,苏宁占股51%


3.锤子科技发布新机Smartisan M1和M1L


4.辣椒快打完成3000万Pre-A轮融资,用户可以通过手机在电视上打游戏


5.酒类快消B2B电商易酒批再获1亿美金C轮融资,景林资产领投


6.工信部指导的中国区块链技术和产业发展论坛成立大会暨首届开发者大会在北京举行


7.蚂蚁花呗启动双11临时提额,六成用户可获益,最高可提5.5万


资源:


中国互联网婚恋交友行业研究报告(2016年Q3)
http://www.bigdata-research.cn/content/201610/357.html


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

Go RPC 开发指南

astaxie 发表了文章 • 5 个评论 • 1442 次浏览 • 2016-10-18 10:18 • 来自相关话题

Go RPC 开发指南

本书首先介绍了使用Go官方库开发RPC服务的方法,然后介绍流行gRPC库以及其它一些RPC框架如Thrift等,后面重点介绍高性能的分布式全功能的RPC框架 rpcx。读者通过阅读本书,可以快速学习和了解Go生态... 查看全部

Go RPC 开发指南


本书首先介绍了使用Go官方库开发RPC服务的方法,然后介绍流行gRPC库以及其它一些RPC框架如Thrift等,后面重点介绍高性能的分布式全功能的RPC框架 rpcx。读者通过阅读本书,可以快速学习和了解Go生态圈的RPC开发技术,并且应用到产品的开发中。


RPC介绍


远程过程调用(Remote Procedure Call,缩写为 RPC)是一个计算机通信协议。 该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程。 如果涉及的软件采用面向对象编程,那么远程过程调用亦可称作远程调用或远程方法调用,比如 Java RMI。


有关RPC的想法至少可以追溯到1976年以“信使报”(Courier)的名义使用。RPC首次在UNIX平台上普及的执行工具程序是SUN公司的RPC(现在叫ONC RPC)。它被用作SUN的NFC的主要部件。ONC RPC今天仍在服务器上被广泛使用。 另一个早期UNIX平台的工具是“阿波罗”计算机网络计算系统(NCS),它很快就用做OSF的分布计算环境(DCE)中的DCE/RPC的基础,并补充了DCOM。


远程过程调用是一个分布式计算的客户端-服务器(Client/Server)的例子,它简单而又广受欢迎。 远程过程调用总是由客户端对服务器发出一个执行若干过程请求,并用客户端提供的参数。执行结果将返回给客户端。 由于存在各式各样的变体和细节差异,对应地派生了各式远程过程调用协议,而且它们并不互相兼容。


为了允许不同的客户端均能访问服务器,许多标准化的 RPC 系统应运而生了。其中大部分采用接口描述语言(Interface Description Language,IDL),方便跨平台的远程过程调用。
来自microsoft



从上图可以看出, RPC 本身是 client-server模型,也是一种 request-response 协议。
有些实现扩展了远程调用的模型,实现了双向的服务调用,但是不管怎样,调用过程还是由一个客户端发起,服务器端提供响应,基本模型没有变化。
服务的调用过程为:



  1. client调用client stub,这是一次本地过程调用

  2. client stub将参数打包成一个消息,然后发送这个消息。打包过程也叫做 marshalling

  3. client所在的系统将消息发送给server

  4. server的的系统将收到的包传给server stub

  5. server stub解包得到参数。 解包也被称作 unmarshalling

  6. 最后server stub调用服务过程. 返回结果按照相反的步骤传给client


https://smallnest.gitbooks.io/go-rpc/content/

10.18 每日早报

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

10.18 每日早报

新闻:

1.三星电子宣布使用10纳米技术量产芯片,率先实现业界突破

2.工商登记信息显示唯品会全资收购浙江贝付,正式获得第三方支付牌照

3.红点创投成立首支中国基金,完成首期基... 查看全部

10.18 每日早报


新闻:


1.三星电子宣布使用10纳米技术量产芯片,率先实现业界突破


2.工商登记信息显示唯品会全资收购浙江贝付,正式获得第三方支付牌照


3.红点创投成立首支中国基金,完成首期基金募集1.8亿美元,由中国团队独立运营


4.携程战略投资旅游连锁品牌旅游百事通,通过后者的5000多家门店布局二三线城市


5.豆瓣成立子公司飞船影业,发布青年导演短片计划


6.国务院第三次大督查:银行业面临不良贷款持续暴露等困难


7.消费金融平台分期乐宣布升级为乐信集团,平台注册用户突破1200万


资源:


2016Q2中国手机游戏市场季度监测报告
http://www.iimedia.cn/44558.html


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

Docker 从入门到实践-3-安装

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

Ubuntu 系列安装 Docker

官方网站上有各种环境下的 安装指南。

通过系统自带包安装

Ubuntu 14.04 版本系统中已经自带了 Docker 包,可以直接安装。

$ sudo... 查看全部

Ubuntu 系列安装 Docker


官方网站上有各种环境下的 安装指南。


通过系统自带包安装


Ubuntu 14.04 版本系统中已经自带了 Docker 包,可以直接安装。


$ sudo apt-get update
$ sudo apt-get install -y docker.io
$ sudo ln -sf /usr/bin/docker.io /usr/local/bin/docker
$ sudo sed -i '$acomplete -F _docker docker' /etc/bash_completion.d/docker.io
如果使用操作系统自带包安装 Docker,目前安装的版本是比较旧的 0.9.1。 要安装更新的版本,可以通过使用 Docker 源的方式。


通过Docker源安装最新版本


要安装最新的 Docker 版本,首先需要安装 apt-transport-https 支持,之后通过添加源来安装。


$ sudo apt-get install apt-transport-https
$ sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9
$ sudo bash -c "echo deb https://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list"
$ sudo apt-get update
$ sudo apt-get install lxc-docker


14.04 之前版本


如果是较低版本的 Ubuntu 系统,需要先更新内核。


$ sudo apt-get update
$ sudo apt-get install linux-image-generic-lts-raring linux-headers-generic-lts-raring
$ sudo reboot
然后重复上面的步骤即可。


安装之后启动 Docker 服务。


$ sudo service docker start


CentOS 系列安装 Docker


Docker 支持 CentOS6 及以后的版本。


CentOS6


对于 CentOS6,可以使用 EPEL 库安装 Docker,命令如下


$ sudo yum install http://mirrors.yun-idc.com/epel/6/i386/epel-release-6-8.noarch.rpm
$ sudo yum install docker-io


CentOS7


CentOS7 系统 CentOS-Extras 库中已带 Docker,可以直接安装:


$ sudo yum install docker
安装之后启动 Docker 服务,并让它随系统启动自动加载。


$ sudo service docker start
$ sudo chkconfig docker on

使用Go语言开发Android&IOS应用

田浩浩 发表了文章 • 1 个评论 • 839 次浏览 • 2016-10-17 14:59 • 来自相关话题

使用Golang开发手机应用的具体步骤 TL:DR


1. 下载安装Go语言(版本1.5+)

Golang链... 查看全部

使用Golang开发手机应用的具体步骤 TL:DR




1. 下载安装Go语言(版本1.5+)


Golang链接


2. 下载安装 gomobile




  • 下载
    $ go get golang.org/x/mobile/cmd/gomobile



  • 安装(需要等待几分钟)
    gomobile init


3. Golang开发手机应用有两种方式


a. 原生应用开发

$ go get -d golang.org/x/mobile/example/basic




  • 主要应用领域:



    • 应用控制管理与配置

    • OpenGL ES 2 绑定

    • Asset 管理

    • Event 管理

    • 试验中的包,含有OpenAL 绑定、音频、字体、图形以及运动传感器




  • Android开发




    • $ gomobile build -target=android golang.org/x/mobile/example/basic

      此命令会生成名为basic的apk安装包



    • $ gomobile install golang.org/x/mobile/example/basic
      此命令将安装apk包到已连接的android设备




  • IOS开发




    • $ gomobile build -target=ios golang.org/x/mobile/example/basic
      此命令会生成名为basic的app安装包



    • 下载IOS安装包命令行工具 - ios-deploy
      $ ios-deploy -b basic.app
      此命令将安装app文件到已开启的IOS模拟器或者已连接的IOS设备




b. 混合绑定开发

$ go get -d golang.org/x/mobile/example/bind/...



  • 优势

    • Go code复用

    • 在Android和IOS开发中共享通用的Go代码通过调用绑定的Golang包名



  • 限制



    • 当前仅支持的一些Go类型

    • 语言之间的绑定会有性能开销




  • Android开发




    • 开启Android studio,导入Project,选择路径$GOPATH/src/golang.org/x/mobile/example/bind/android




    • 修改配置文件hello/build.gradle



    • 最后 Build & Run <完>
      备用选项:
      $ gomobile bind -target=android golang.org/x/mobile/example/bind/hello
      此命令会在hello/路径下生成aar文件,用户可以直接在Android Studio内导入




  • IOS开发




    • $ cd $GOPATH/src/golang.org/x/mobile/example/bind $ gomobile bind -target=ios golang.org/x/mobile/example/bind/hello
      此命令在ios/路径下生成bind.xcodeprojxcode项目以及 在bind/目录下生成一个 hello.framework




    • 打开xcode项目
      $ open ios/bind.xcodeproj


      拖拽hello.framework文件到xcode项目内



    • 最后 Build & Run <完>



SOLID Go Design - Go语言面向对象设计

田浩浩 发表了文章 • 0 个评论 • 672 次浏览 • 2016-10-17 14:58 • 来自相关话题

代码评审

为什么要代码评审? 如果代码评审是要捕捉糟糕的代码,那么你如何知道你审查的代码是好的还是糟糕的?

我在找一些客观的方式来谈论代码的好坏属性。

糟糕的代码

你可能会在代码审查中遇到以... 查看全部

代码评审


为什么要代码评审?
如果代码评审是要捕捉糟糕的代码,那么你如何知道你审查的代码是好的还是糟糕的?


我在找一些客观的方式来谈论代码的好坏属性。


糟糕的代码


你可能会在代码审查中遇到以下这些糟糕的代码:



  • Rigid - 代码是否死板?它是否有强类型或参数以至于修改起来很困难?

  • Fragile - 代码是否脆弱?对代码做轻微的改变是否就会引起程序极大的破坏?

  • Immobile - 代码是否很难重构?

  • Complex - 代码是否过于复杂,是否过度设计?

  • Verbose - 代码是否过于冗长而使用起来很费劲?当查阅代码是否很难看出来代码在做什么?


当你做代码审查的时候是否会很高兴看到这些词语?


当然不会。


好的设计


如果有一些描述优秀的设计属性的方式就更好了,不仅仅是糟糕的设计,是否能在客观条件下做?


SOLID - 面向对象设计


在2002年,Robert Martin的Agile Software Development, Principles, Patterns, and Practices 书中提到了五个可重用软件设计的原则 - "SOLID"(英文首字母缩略字):



这本书有点点过时,使用的语言也是十多年前的。但是,或许SOLID原则的某些方面可以给我们一个有关如何谈论一个精心设计的Go语言程序的线索。


1) Single Responsibility Principle - 单一功能原则



A class should have one, and only one, reason to change.
–Robert C Martin



现在Go语言显然没有classses - 相反,我们有更为强大的组合的概念 - 但是如果你可以看到过去class的使用,我认为这里有其价值。


为什么一段代码应该只有一个原因改变如此重要?当然,和你自己的代码要修改比较起来,发现自己代码所依赖的代码要修改会更令人头疼。而且,当你的代码不得不要修改的时候,它应该对直接的刺激有反应,而不应该是一个间接伤害的受害者。


所以,代码有单一功能原则从而有最少的原因来改变。




  • Coupling & Cohesion - 耦合与内聚

    这两个词语描绘了修改一段软件代码是何等的简单或困难。


    Coupling - 耦合是两个东西一起改变 - 一个移动会引发另一个移动。
    Cohesion - 内聚是相关联但又隔离,一种相互吸引的力量。


    在软件方面,内聚是形容代码段自然吸引到另一个的属性。


    要描述Go语言的耦合与内聚,我们可以要谈论一下functions和methods,当讨论单一功能原则时它们很常见,但是我相信它始于Go语言的package模型。




  • Pakcage命名

    在Go语言中,所有的代码都在某个package中。好的package设计始于他的命名。package名字不仅描述了它的目的而且还是一个命名空间的前缀。Go语言标准库里有一些好的例子:



    • net/http - 提供了http客户端和服务

    • os/exec - 执行外部的命令

    • encoding/json - 实现了JSON的编码与解码




在你自己的项目中使用其他pakcage时要用import声明,它会在两个package之间建立一个源码级的耦合。




  • 糟糕的pakcage命名

    关注于命名并不是在卖弄。糟糕的命名会失去罗列其目的的机会。


    比如说serverprivatecommonutils 这些糟糕的命名都很常见。这些package就像是一个混杂的场所,因为他们好多都是没有原因地经常改变。




  • Go语言的UNIX哲学

    以我的观点,涉及到解耦设计必须要提及Doug McIlroy的Unix哲学:小巧而锋利的工具的结合解决更大的任务或者通常原创作者并没有预想到的任务。


    我认为Go语言的Package体现了UNIX哲学精神。实际上每个package自身就是一个具有单一原则的变化单元的小型Go语言项目。




2) Open / Closed Principle - 开闭原则


 Bertrand Meyer曾经写道:



Software entities should be open for extension, but closed for modification.
–Bertrand Meyer, Object-Oriented Software Construction



该建议如何应用到现在的编程语言上:


package main

type A struct {

 year int

}

func (a A) Greet() { fmt.Println("Hello GolangUK", a.year) }

type B struct {

 A

}

func (b B) Greet() { fmt.Println("Welcome to GolangUK", b.year) }

func main() {

 var a A

a.year = 2016

 var b B

b.year = 2016

 a.Greet() // Hello GolangUK 2016

 b.Greet() // Welcome to GolangUK 2016

}

typeA有一个year字段以及Greet方法。
typeB嵌入了A做为字段,从而,使B提供的Greet方法遮蔽了A的,调用时可以看到B的方法覆盖了A


但是嵌入不仅仅是对于方法,它还能提供嵌入type的字段访问。如你所见,由于AB都在同一个package内,B可以访问A的私有year字段就像B已经声明过。


因此 嵌入是一个强大的工具,它允许Go语言type对扩展是开放的。


package main

type Cat struct {

Name string

}

func (c Cat) Legs() int { return 4 }

func (c Cat) PrintLegs() {

 fmt.Printf("I have %d legs\n", c.Legs())

}

type OctoCat struct {

Cat

}

func (o OctoCat) Legs() int { return 5 }

func main() {

var octo OctoCat

 fmt.Println(octo.Legs()) // 5

octo.PrintLegs() // I have 4 legs

}

在上边这个例子中,typeCatLegs方法来计算它有几条腿。我们嵌入Cat到一个新的typeOctoCat中,并声明Octocats有五条腿。然而,尽管OctoCat定义了自己有五条腿,但是PrintLegs方法被调用时会返回4。


这是因为PrintLegs在typeCat中定义。它会将Cat做为它的接收者,因此它会使用CatLegs方法。Cat并不了解已嵌入的type,因此它的嵌入方法不能被修改。


由此,我们可以说Go语言的types对扩展开放,但是对修改是关闭的。


事实上,Go语言接收者的方法仅仅是带有预先声明形式的参数的function的语法糖而已:


func (c Cat) PrintLegs() {
fmt.Printf("I have %d legs\n", c.Legs())
}

func PrintLegs(c Cat) {
fmt.Printf("I have %d legs\n", c.Legs())
}

第一个function的接收者就是你传进去的参数,而且由于Go语言不知道重载,所以说OctoCats并不能替换普通的Cats,这就引出了接下来一个原则:


3) Liskov Substitution Principle - 里氏替换原则


该原则由Barbara Liskov提出,大致上,它规定了两种类型如果调用者不能区分出他们行为的不同,那么他们是可替代的。


基于class的编程语言,里氏替换原则通常被解释为一个抽象基类的各种具体子类的规范。但是Go语言没有class或者inheritance(继承),因此就不能以抽象类的层次结构实现替换。




  • Interfaces - 接口

    相反,Go语言的interface才有权替换。在Go语言中,type不需要声明他们具体要实现的某个interface,相反的,任何想要实现interface的type仅需提供与interface声明所匹配的方法。


    就Go语言而言,隐式的interface要比显式的更令人满意,这也深刻地影响着他们使用的方式。


    精心设计的interface更可能是小巧的,流行的做法是一个interface只包含一个方法。逻辑上来讲小巧的interface使实现变得简单,反之就很难做到。这就导致了由常见行为连接的简单实现而组成的package。




  • io.Reader

    type Reader interface {
    // Read reads up to len(buf) bytes into buf.
    Read(buf []byte) (n int, err error)
    }

    我最喜爱的Go语言interface - io.Reader


    interfaceio.Reader非常简单,Read读取数据到提供的buffer,并返回调用者读取数据的bytes的数量以及读取期间的任何错误。它看起来简单但是很强大。


    因为io.Reader可以处理任何能转换为bytes流的数据,我们可以在任何事情上构建readers:string常量、byte数组、标准输入、网络数据流、gzip后的tar文件以及通过ssh远程执行的命令的标准输出。


    所有这些实现对于另外一个都是可替换的,因为他们都履行了相同的简单合同。


    因此,里氏替换原则在Go语言的应用,可以用 Jim Weirich 的格言来总结:



    Require no more, promise no less.
    –Jim Weirich





接下来就到了"SOLID"第四个原则。


4) Interface Segregation Principle - 接口隔离原则



Clients should not be forced to depend on methods they do not use.
–Robert C. Martin



在Go语言中,接口隔离原则的应用是指一个方法来完成其工作的孤立行为的过程。举个“栗子”,编写方法来保存一个文档结构到磁盘的任务。


// Save writes the contents of doc to the file f.
func Save(f *os.File, doc *Document) error

我可以这样定义这个Save方法,使用*os.File做为保存Document的文件。但是这样做会有一些问题。


Save方法排除了保存数据到网络位置的选项。假如过后要加入网络储存的需求,那么该方法就需要修改也就意味着要影响到所有使用该方法的调用者。


因为Save直接地操作磁盘上的文件,测试起来很不方便。要验证其操作,测试不得不在文件被写入后读取其内容。另外测试必须确保f被写入一个临时的位置而且过后还要删除。


*os.File还包含了许多跟Save无关的方法,像读取路径以及检查路径是否是软连接。如果Save方法只使用*os.File相关的部分将会非常有用。


我们如何做呢:


// Save writes the contents of doc to the supplied ReadWriterCloser.
func Save(rwc io.ReadWriteCloser, doc *Document) error

使用io.ReadWriteCloser来应用接口隔离原则,这样就重新定义了Save方法使用一个interface来描述更为通用的类型。


随着修改,任何实现了io.ReadWriteCloser接口的type都可以代替之前的*os.File。这使得 Save不仅扩展了它的应用范围同时也给Save的调用者说明了type*os.File哪些方法是操作相关的。


做为Save的作者,我没有了在*os.File上调用无关的方法选项了,因为他们都被隐藏于io.ReadWriteCloser接口。我们可以进一步地应用接口隔离原则。


首先,Save方法不太可能会保持单一功能原则,因为它要读取的文件内容应该是另外一段代码的责任。(译注:待更新)因此我们可以缩小接口范围,只传入writingclosing


// Save writes the contents of doc to the supplied WriteCloser.
func Save(wc io.WriteCloser, doc *Document) error

其次,通过向Save提供一种机制来关闭它的数据流,会导致另外一个问题:wc会在什么情况下关闭。Save可能会无条件的调用Close或在成功的情况下调用Close


如果它想要在写入document之后再写入额外的数据时会引起Save的调用者一个问题。


type NopCloser struct {
io.Writer
}

// Close has no effect on the underlying writer.
func (c *NopCloser) Close() error { return nil }

一个原始解决方案回事定义一个新的type,在其内嵌入io.Writer以及重写Close方法来阻止Save方法关闭底层数据流。


但是这样可能会违反里氏替换原则,如果NopCloser并没有关闭任何东西。


// Save writes the contents of doc to the supplied Writer.
func Save(w io.Writer, doc *Document) error

一个更好的解决办法是重新定义Save只传入io.Writer,剥离它的所有责任除了写入数据到数据流。


通过对Save方法应用接口隔离原则,同时得到了最具体以及最通用的需求函数。我们现在可以使用Save方法来保存数据到任何实现了io.Writer的地方。



A great rule of thumb for Go is accept interfaces, return structs.
–Jack Lindamood



5) Dependency Inversion Principle - 依赖反转原则



High-level modules should not depend on low-level modules. Both should depend on abstractions.
Abstractions should not depend on details. Details should depend on abstractions.
–Robert C. Martin



对于Go语言来讲,依赖反转意味着什么呢:


如果你应用以上所有的原则,代码已经被分解成离散的有明确责任和目的的package,你的代码应该描述了它的依赖interface以及这些interface应该只描述他们需要的功能行为。换句话说就是他们不会再过多的改变。


因此,我认为Martin所讲的在Go语言的应用是context,即你import graph(译注:后文用“导入图”代替)的结构。


在Go语言中,你的导入图必须是非循环。不遵守此非循环的需求会导致编译错误,但是更为严重的是它代表了一系列的设计错误。


所有条件都相同的情况下精心设计的导入图应该是广泛的以及相对平坦的,而不是又高又窄。如果你有一个package的函数在没有其他package的情况下就无法操作,也许这就表明了代码没有考虑pakcage的边界。


依赖反转原则鼓励你尽可能地像导入图一样在mainpackage或者最高层级的处理程序内对具体细节负责,让低层级代码来处理抽象的接口。


“SOLID” Go语言设计


回顾一下,当应用到Go语言设计中,每个“SOLID”原则都是强有力的声明,但是加在一起他们有一个中心主题。



  • 单一功能原则鼓励你在package中构建functions、types以及方法表现出自然的凝聚力。types属于彼此,functions为单一目的服务。

  • 开闭原则鼓励你使用嵌入将简单的type组合成更为复杂的。

  • 里氏替换原则鼓励你在package之间表达依赖关系时用interface,而非具体类型。通过定义小巧的interface,我们可以更有信心地切实满足其合约。

  • 接口隔离原则鼓励你仅取决于所需行为来定义函数和方法。如果你的函数仅仅需要有一个方法的interface做为参数,那么它很有可能只有一个责任。

  • 依赖反转原则鼓励你在编译时将package所依赖的东西移除 - 在Go语言中我们可以看到这样做使得运行时用到的某个特定的package的import声明的数量减少。(译注:待更新)


如果总结这个演讲(译注:该篇文章取自Dave大神在Golang UK Conference 2016的演讲文字内容,文章结尾处有YouTube链接(需要翻墙))它可能会是:



interfaces let you apply the SOLID principles to Go programs



因为interface描绘了他们的pakcage的规定,而不是如何规定的。换个说法就是“解耦”,这确实是我们的目标,因为解耦的软件修改起来更容易。


就像Sandi Metz提到的:



Design is the art of arranging code that needs to work today, and to be easy to change forever.
–Sandi Metz



因为如果Go语言想要成为公司长期投资的编程语言,Go程序的维护,更容易的变更将是他们决定的关键因素。


结尾


最后,问个问题这个世界上有多少个Go语言程序员,我的回答是:



By 2020, there will be 500,000 Go developers.
-me



五十万Go语言程序员会做什么?显然,他们会写好多Go代码。实话实说,并不是所有的都是好的代码,一些可能会很糟糕。


...


Go语言程序员应当讨论更多的是设计而非框架。我们应当不惜一切代价地关注重用而非性能。


我想要看到是今天的人们谈论关于如何使用编程语言,无论是设计解决方案还是解决实际问题的选择和局限性。


我想要听到的是人们谈论如何通过精心设计、解耦、重用以及适应变化的方式来设计Go语言程序。


...还有一点


我们需要告诉世界优秀的软件该如何编写。告诉他们使用Go语言如何编写优秀的、可组合的及易于变化的软件。


...


感谢!


相关博文:



  1. Inspecting errors

  2. Should methods be declared on T or *T

  3. Ice cream makers and data races

  4. Stupid Go declaration tricks




原文视频:Golang UK Conference 2016 - Dave Cheney - SOLID Go Design


原文链接:SOLID Go Design




[转]Golang资料集

itfanr 发表了文章 • 0 个评论 • 1051 次浏览 • 2016-10-17 13:55 • 来自相关话题

Golang资料集

介绍:跨平台的... 查看全部

Golang资料集



介绍:跨平台的golang GUI库,支持Windows(xp以上),Unix,Mac OS X(Mac OS X 10.7以上)



介绍:Gopm(Go 包管理工具) 是一个用于搜索、安装、更新和分享 Go 包的管理工具。



介绍:本文以 Go 的标准库为例,介绍了 Go 编译过程的工作原理。



介绍:在之前的 Go语言的国际化支持(基于gettext-go)中, 讲到了如何翻译源代码中的字符串.



介绍:Go语言作为一个现代化的编程语言以及支持垃圾内存的自动回收特性(GC). 这篇文章主要介的是非内存资源的自动回收技术.



介绍:无需 Git 和 Hg 等版本管理工具,就可以下载指定版本的 Go 语言包



介绍:本文主介绍Go 语言中的方法,接口和嵌入类型。原文地址



介绍:详解golang 的interface和nil.



介绍:Go并发编程之Go语言概述,主要是一些介绍与语法基础部分



介绍:里面讲解了变量,函数,方法,接口,并发等相关的知识。而且还有可运行的代码。跨平台



介绍:Golang的开发环境与一些基础知识的介绍



介绍:不用VPN



介绍:这是一本开源书籍,本书的作者目前开发了一个开源框架beego,非常的有名。就目前2014年10月2日截止。在github上面有超过3000多个star了



介绍:构建一个可测试的go web 应用,step by step



介绍:随着 Go 语言的快速发展,各类实用、强大的官方库和第三方库层出不穷。但种类繁多与功能强大的同时,也给不少初学者带来了许多选择和使用上的困扰。很多同学因为时间和精力上的限制,无法很好地体验这些数量庞大的库。因此,作者特别制作本套教程,专门针对当中比较知名和受关注度较高的库进行基于 博客、示例和视频 的三位一体讲解。



介绍:作者就自身学习go语言的学习经历来讲解,并且讲述了作者在学习中遇到的坑。非常适合零基础的朋友



介绍:《Go编程基础》的第二部,本套教程将以搭建个人博客作为实战目标,由浅至深地讲解使用 Go 开发 Web 应用程序的必备知识与技巧。



介绍:《The Way to Go》中文译本.



介绍:免费电子书,如何构建一个web app



介绍:Golang generate 草案,翻译自Go generate: A Proposal



介绍:golang的面向对象设计



介绍:使用golang编写的web ide



介绍:Fluent HTTP client for Golang. With timeout, retries and exponential back-off support.



介绍:Go 1.4 将支持 Android



介绍:使用Golang打电话,接电话



介绍:国外的Go资源



介绍:A curated list of awesome Go frameworks, libraries and software



介绍:汇总平时看到的好文章,技术、产品、管理均有,尽量保证一周汇总一篇



介绍:一个简单实用的 Go 语言重构工具



介绍:码农周刊分类整理Golang



介绍:Go语言诞生5周年!10大Go语言开源项目推荐



介绍:Interface的前世今生。



介绍:一个 Go 语言实现的网络连接池



介绍:一个物联网相关的Go语言框架



介绍:作者的一些Go中的困扰,经验之谈



介绍:使用GO改善Ruby性能



介绍:为什么golang在中国如此流行?



介绍:Linux网络驱动



介绍:Go 语言入门教程



介绍:一个 Go 语言跨平台 UI 库



介绍:Go最新资料汇总



介绍:猎豹移动技术博客文章



介绍:A fast and modern static website engine.



介绍:scriptrock公司使用Golang的经验,此外还推荐一个公司的应用Golang at Runscope



介绍:Dropbox开源Go开发包,github.



介绍:如何使用Go构建自己的Web应用教程,此外还推荐GoLang Tutorials.



介绍:Dependency Injection with Go.



介绍:Go Programming Language Resources.



介绍:2015年中国Gopher大会资料.



介绍:新手使用Go构建应用建议.



介绍:Golang 图书列表.



介绍:CloudFlare的Go密码库crypto:弥合性能差距,项目地址.



介绍:Generates a lean and mean Go web project.



介绍:用Golang实现linux命令.



介绍:深入解析Go书籍.



介绍:Go编程陷阱.



介绍:a project based Golang build tool.



介绍:Go并发编程基础,译版.



介绍:fleet 绑定了systemd 和etcd 到一个分布式init 系统,可以认为是systemd 的扩展,但是并不是机器级别的,而是集群级别的.



介绍:Golang中国社区golang资料汇总.



介绍:用go开发的实时通讯平台,数据库使用的pgsql.



介绍:Golang Performance Tips.



介绍:推荐一个博客,以go开发为主,文如其名.



介绍:GoLang 书籍集合.



介绍:Go开发新手常犯的50个错误.



介绍:Golang基本代码实例,中文版.



介绍:Golang news.



介绍:Fast HTTP package for Go.



介绍:《The Go Programming Language》 的 读书笔记 和 习题解答.



介绍:Go 语言圣经中文版



介绍:这是一本关于使用golang写网络程序的电子书.



介绍:谷歌开源的一款负载均衡器,官方介绍.注意:非官方项目



介绍:Effective Go几乎是学习Go语言所必须阅读的重要的文档,涉及内容丰富,很多精要部分。中文参考译文参考1译文参考2



介绍:Go语言日报,每天新鲜事



介绍:Golang小书,作者主页的go资料也不错



介绍:在Go中使用Qt构建跨平台应用.



介绍:这篇文章是 Golang 开源库 Negroni 的README.md中推荐一篇的文章,讲的是 Golang 中如何处理请求的上下文信息.译文



介绍:国内七牛云存储应用实践.



介绍:Shell脚本编写良好实践指南.



介绍:Golang官方开源项目wiki主页,内容丰富.是学习golang的重要参考,文中还推荐了awesome golang.



介绍:国外很多大学把Golang作为学习课程,本页推荐了很多相关的课程,新手可以学习参考.



介绍:作者写这本书主要是灵感来自于: https://github.com/thekarangoel/Projects ,然后作者就想到了当初做PHP的时候,也有类似的项目,觉得golang也可以实现一个类似的书籍,暂且把书名定为《Go实战开发》.



介绍:Golang 语言学习基础教程.



介绍:Collection of Go programs for quick reference,golang新手入门看源码就可以了.



介绍:国外的一位开发者在实践开发一个大型项目之后所写的笔记



介绍:利用go进行词法分析


本文转自:https://raw.githubusercontent.com/ty4z2008/Qix/master/golang.md

10.17 每日早报

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

10.17 每日早报

新闻:

1.可穿戴式空气过滤设备——忻风随身空气匣在北京发布

2.中国标准化协会携海尔旗下物流品牌日日顺共同推出智慧物流全流程服务标准

3.零零无限在美发布跟拍无人机Hover... 查看全部

10.17 每日早报


新闻:


1.可穿戴式空气过滤设备——忻风随身空气匣在北京发布


2.中国标准化协会携海尔旗下物流品牌日日顺共同推出智慧物流全流程服务标准


3.零零无限在美发布跟拍无人机Hover Camera Passport,主打安全、便携和易用


4.生日管家和心意点点完成合并,新公司取名心生家族(集团),两个品牌保持独立


5.Google News在美英推出Fact Check(事实检查)标签,协助分辨新闻报道真伪


6.金融科技初创公司Affirm宣布获得一笔1亿美元债务融资,融资总额达5.25亿美元


7.老牌健身房青鸟体育获3000万A轮融资,中体鼎新领投


资源:


2016中国移动电商市场研究报告
http://lab.cmcm.com/sjfx/2016-10-11/119.html


2016 Q3独角兽公司融资概况
http://36kr.com/p/5054569.html


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

[译]空结构体

itfanr 发表了文章 • 12 个评论 • 3005 次浏览 • 2016-10-16 21:45 • 来自相关话题

这篇文章探讨了我喜欢的Go数据类型,空结构体。

空结构体是一个没有field的结构体类型。这里有几个例子,有命名和匿名形式:

... 查看全部

这篇文章探讨了我喜欢的Go数据类型,空结构体。


空结构体是一个没有field的结构体类型。这里有几个例子,有命名和匿名形式:


type Q struct{}
var q struct{}

所以,如果空结构体没有成员,我们该怎么使用它?


width


在深入研究空结构体本身前,我想简要讨论下width


术语width来自于gc编译器,尽管它的词源可能追溯到几十年年。


width描述了类型实例占用的字节数目。因为一个进程的地址空间是一维的,我认为witdh比size更合适。


width一个类型的属性。因为Go程序的每个值都有一个类型,值类型定义了它的witdh,一般是8比特的倍数。


我们可以发现任何值的宽度,它的类型的width使用unsafe.Sizeof()函数:


var s string
var c complex128
fmt.Println(unsafe.Sizeof(s)) // prints 8
fmt.Println(unsafe.Sizeof(c)) // prints 16

http://play.golang.org/p/4mzdOKW6uQ


数组类型的width是它的元素类型的倍数:


var a [3]uint32
fmt.Println(unsafe.Sizeof(a)) // prints 12

http://play.golang.org/p/YC97xsGG73


结构体提供了更灵活的方式来定义组合类型,它的width是所有组成类型的width的总和,加上padding:


type S struct {
a uint16
b uint32
}
var s S
fmt.Println(unsafe.Sizeof(s)) // prints 8, not 6

上面的例子演示了padding的一方面,值必须在内存中对齐为它的width的倍数。在这个场景中,在a和b中间被编译器加入了2个字节的padding。


更新:Russ Cox已经解释了width和对齐无关。你可以阅读下面的评论


空类型


现在,我们已经探讨了width,很明显空类型的width是零。它占用了零字节的存储空间:


var s struct{}
fmt.Println(unsafe.Sizeof(s)) // prints 0

因为空类型占用了零字节,所以它不需要填充。这样,空结构体组成的一个结构体也不占用存储空间:


type S struct {
A struct{}
B struct{}
}
var s S
fmt.Println(unsafe.Sizeof(s)) // prints 0

http://play.golang.org/p/PyGYFmPmMt


我们可以用空类型做什么


适用于Go语言的正交性,空类型和其他类型一样,是一个结构类型。你所使用的正常的结构体的所有的属性适用于空的结构。


你可以声明一个结构体数组struct{}s,但是他们当然不会占用存储空间:


var x [1000000000]struct{}
fmt.Println(unsafe.Sizeof(x)) // prints 0

http://play.golang.org/p/0lWjhSQmkc


struct{}s的切片仅仅消耗他们的slice头的空间。就像上面演示的那样,他们的后端数组不消耗空间:


var x = make([]struct{}, 1000000000)
fmt.Println(unsafe.Sizeof(x)) // prints 12 in the playground

http://play.golang.org/p/vBKP8VQpd8


当然,正常的子切片,内置的len和cap和预期一样工作:


var x = make([]struct{}, 100)
var y = x[:50]
fmt.Println(len(y), cap(y)) // prints 50 100

http://play.golang.org/p/8cO4SbrWVP


你可以获取struct{}值的地址,当它的可以地址化,就像其他值一样:


var a struct{}
var b = &a

有意思的是,两个struct{}值的地址可能是相同的:


var a, b struct{}
fmt.Println(&a == &b) // true

http://play.golang.org/p/uMjQpOOkX1


对于[]struct{}s,这个属性也是可见的:


a := make([]struct{}, 10)
b := make([]struct{}, 20)
fmt.Println(&a == &b) // false, a and b are different slices
fmt.Println(&a[0] == &b[0]) // true, their backing arrays are the same

http://play.golang.org/p/oehdExdd96


为什么是这样?如果你考虑一下,空结构体不包含成员,所以可以不包含数据。如果空结构体不包含数据,不能决定是否两个struct{}值是否是相同的。它们在效果上,是可替代的。


a := struct{}{} // not the zero value, a real new struct{} instance
b := struct{}{}
fmt.Println(a == b) // true

http://play.golang.org/p/K9qjnPiwM8


注意:这个属性不是spec所需要的,但是注意:Two distinct zero-size variables may have the same address in memory.


struct{} 作为 method receiver


现在,我们已经演示了空结构体有任何其他类型一样的行为,因此,我们可以把它们作为函数接收者来使用:


type S struct{}

func (s *S) addr() { fmt.Printf("%p\n", s) }

func main() {
var a, b S
a.addr() // 0x1beeb0
b.addr() // 0x1beeb0
}

http://play.golang.org/p/YSQCczP-Pt


在这个例子中,展示了all zero sized值的地址为0x1beeb0。精确的地址可能因Go的版本而不同。


封装


谢谢你的阅读。本文已接近800字,比预期更多,我还有更多的计划。


尽管本文关注于语言黑盒,有一个空结构体重要的实际用途。chan struct{}用来在不同的go routine之间发送信号。


翻译


本文的中文版在这里


更新:Damian Gryski指出我忽略了 Brad Fitzpatrick的iter包。我留下它作为读者的练习来探索Brad的深远影响的贡献。


相关文章



  1. Struct composition with Go

  2. Friday pop quiz: the smallest buffer

  3. Constant errors

  4. Stupid Go declaration tricks


英文原文


如果我翻译得不对,请帮我改善:
https://github.com/itfanr/articles-about-golang/blob/master/2016-10/2.the-empty-struct.md

Docker 从入门到实践-2-基本概念

wwdyy 发表了文章 • 0 个评论 • 464 次浏览 • 2016-10-15 20:46 • 来自相关话题

镜像

Docker 包括三个基本概念

镜像(Image)
容器(Container)
仓库(Repository)

理解了这三个概念,就理解了 Docker 的整个生命周... 查看全部

镜像


Docker 包括三个基本概念


镜像(Image)
容器(Container)
仓库(Repository)

理解了这三个概念,就理解了 Docker 的整个生命周期。


Docker 镜像


Docker 镜像就是一个只读的模板。


例如:一个镜像可以包含一个完整的 ubuntu 操作系统环境,里面仅安装了 Apache 或用户需要的其它应用程序。


镜像可以用来创建 Docker 容器。


Docker 提供了一个很简单的机制来创建镜像或者更新现有的镜像,用户甚至可以直接从其他人那里下载一个已经做好的镜像来直接使用。


Docker 容器


Docker 利用容器来运行应用。


容器是从镜像创建的运行实例。它可以被启动、开始、停止、删除。每个容器都是相互隔离的、保证安全的平台。


可以把容器看做是一个简易版的 Linux 环境(包括root用户权限、进程空间、用户空间和网络空间等)和运行在其中的应用程序。


注:镜像是只读的,容器在启动的时候创建一层可写层作为最上层。


Docker 仓库


仓库是集中存放镜像文件的场所。有时候会把仓库和仓库注册服务器(Registry)混为一谈,并不严格区分。实际上,仓库注册服务器上往往存放着多个仓库,每个仓库中又包含了多个镜像,每个镜像有不同的标签(tag)。


仓库分为公开仓库(Public)和私有仓库(Private)两种形式。


最大的公开仓库是 Docker Hub,存放了数量庞大的镜像供用户下载。 国内的公开仓库包括 Docker Pool 等,可以提供大陆用户更稳定快速的访问。


当然,用户也可以在本地网络内创建一个私有仓库。


当用户创建了自己的镜像之后就可以使用 push 命令将它上传到公有或者私有仓库,这样下次在另外一台机器上使用这个镜像时候,只需要从仓库上 pull 下来就可以了。


注:Docker 仓库的概念跟 Git 类似,注册服务器可以理解为 GitHub 这样的托管服务。