作者:Jack47

转载请保留作者和原文出处

欢迎关注我的微信公众账号程序员杰克,两边的文章会同步,也可以添加我的RSS订阅源

本文通过从无到有创建一个利用Go语言实现的非常简单的HttpServer,来让大家熟悉利用Go语言时的基本流程,工具和代码的基本布局,为学习Go语言时碰到的环境问题扫清障碍。

以一个简单的场景来举例,有一个可复用的库(hugger)和一个使用这个库的应用程序(hugmachine),代码都托管在Github上。

代码布局

go工具

go命令是一个管理Go源代码的工具,可以用来做很多事情:

  • build 编译包和依赖
  • clean 删除对象文件
  • fmt 对代码风格格式化
  • get 下载并安装包和依赖(递归地进行)
  • install 编译并安装包和依赖
  • list 列出所有的包
  • run 编译和运行Go程序
  • test 运行以包为单位的测试
  • vet 做Go源代码的检查,发现编译器没有发现的错误

go命令大部分(除了run)都是以包为单位的,而不是单个的文件。

概述

go这个工具要求程序猿按照特定的方式把代码组织在一起,主要体现在:

  • Go程序猿通常把所有的Go代码(多个项目)都放到一个工作区(workspace)下,这跟其他的编程环境不一样,其他的编程环境里,通常是每个项目有各自的工作区
  • 一个工作区下可以包含不同版本控制(Git, VCS)下的代码仓库

Go语言中,一个工作区的根目录下包含三个基本的目录:

  • src 里面是Go的源代码文件
  • pkg 里面是包(package)对象文件
  • bin 里面是可执行的命令

其中后两个目录是go工具自动产生的:它把源代码构建过程中生成的对象文件放到pkg目录下,然后把对应生成的二进制文件放到bin目录下。

GOPATH环境变量

GOPATH环境变量是必须要设置的,$GOPATH环境变量必须指向Go工作区(workspace)的根。开发Go代码,大部分情况下都只需设置这一个环境变量。Go的构建过程会按照go help gopath里描述的约定来走,例如Go会去GOPATH列出了的所有目录里寻找Go代码(所有的包都在$GOPATH/src目录下),会把新的包下载到GOPATH列出的第一个目录里。当然并不是所有的项目都会按照这些约定来,它们可以不使用go工具集。

接下来我们以一个简单的场景来举例,从零开始实现一个可复用的库(hugger)和一个使用这个库的应用程序(hugmachine),它们的代码都托管在Github上,各自在自己的repository里。你在Github上的这两个repository都会被clone$GOPATH/src下的各个文件夹下。即src/github.com/Jack47/下的每个目录都是一个独立git clone出来的repository。

你的代码布局可能看起来是这样的:

$GOPATH
src/
github.com/
Jack47/
hugger/
.git/
hugger.go
hugger_test.go
README.md
hugmachine/
.git/
hugmachine.go
hugmachine_test.go
README.md

创建工作区(workspace)

先在Github上创建这两个新的repositories。然后在本地设置GOPATH,建好上级目录,克隆出两个repository出来:

cd ~/
mkdir go-workspace
export GOPATH=~/go-workspace
cd $GOPATH
mkdir -p src/github.com/Jack47
cd src/github.com/Jack47
git clone git@github.com:Jack47/hugmachine.git
git clone git@github.com:Jack47/hugger.git

自己的程序库(Libraries)

按惯例,代码仓库的名称和它包含的Go包(package)的名称应该是保持一致的。我们的hugger代码仓库包含定义了hugger包的hugger.go文件:

package hugger

func Hugger() string {
return "You are warmly hugged!\n"
}

应用程序

一个应用程序--将被编译成可执行命令的Go代码--总是需要定义为package main并有一个main()函数。

因此hugmachine.go可能看起来是这样的:

package main

import (
"os"
"io"
"net/http"
"strconv" "github.com/Jack47/hugger"
logging "github.com/op/go-logging"
) func heartbreakerHandler(w http.ResponseWriter, req *http.Request) {
logger.Infof("Meet heartbreaker from %s", req.Host)
io.WriteString(w, hugger.Hug())
} var LISTENING_PORT = 1024
var logger = logging.MustGetLogger("hugmachine.log") func main() {
logging.NewLogBackend(os.Stderr, "", 0)
http.HandleFunc("/heartbreaker", heartbreakerHandler)
logger.Infof("Listening on port %d", LISTENING_PORT)
err := http.ListenAndServe("0.0.0.0:"+strconv.Itoa(LISTENING_PORT), nil)
if err != nil {
logger.Fatal("ListenAndServe: " + err.Error())
}
}

import path

在Go中,约定是把代码的位置包含在import path中,例如 hugger 包:

	$GOPATH/src/github.com/Jack47/hugger

hugmachine.go中引用hugger时,是这样写的:

	import(
"github.com/Jack47/hugger"
)

让你的包可以通过go get下载

go get有一个特点是它可以识别出知名的代码托管网站的路径,例如Github,Bitbucket。这样它就可以把包的import path转换成正确的命令来check out出代码。这样你甚至可以通过go get import-path/package-name来自动下载并安装package-name及其依赖包。例如:

$ go get github.com/golang/example/hello
$ $GOPATH/bin/hello
Hello, Go examples!

如果go get时指定的包已经在工作区下,那么go get会跳过checkout远程代码这一步,执行类似go install的操作。此时如果需要更新这个包,使用go get -u。但是所有通过go get checkout出来的Github repo都默认使用只读的 https://。为了能把修改push回github,需要修改.git/config里的origin/master引用,指向Github上的SSH的repo。

依赖

你的项目可能会直接依赖很多已有的包。上面的hugmachine程序直接依赖于github.com/op/go-logginggithub.com/Jack47/hugger。通过go list ...命令可以看到这个项目所有的依赖包。可以通过在你的工作区(workspace)的根路径上运行 go get -v ./...(go help packages来看完整的对...语法的解释)来安装所有的依赖(直接和间接依赖)。go get命令跟go install命令类似,它会尝试build然后递归安装所有的依赖包到工作区下$GOPATH/src目录下。

构建

在开发过程中可以通过命令go build ...hugger来构建hugger库。也可以指定到包名的全路径,go build github.com/Jack47/hugger

使用命令go build ...hugmachine 来编译 hugmachine.go和相关依赖为可执行文件。此时在 $GOPATH目录下会出现hugmachine可执行文件。也可以使用go install ...hugmachine,他会编译并安装指定的包到$GOPATH/bin目录下。

此时执行hugmachine命令,会看到程序运行起来了:

$ ./hugmachine
2016/10/28 09:31:43 Listening on port 1024

通过在浏览器里访问http://localhost:1024/heartbreaker ,可以看到hugmachine的返回结果。

如果你的工程项目比较复杂,可以参考下这些开源软件的代码布局:

filebeat

参考资料

Github Code Layout

go lang wiki


如果您看了本篇博客,觉得对您有所收获,请点击右下角的“推荐”,让更多人看到!

资助Jack47写作,打赏一个鸡蛋灌饼吧
微信打赏
支付宝打赏

Go语言实战的更多相关文章

  1. R入门<三>-R语言实战第4章基本数据管理摘要

    入门书籍:R语言实战 进度:1-4章 摘要: 1)实用的包 forecast:用于做时间序列预测的,有auto.arima函数 RODBC:可以用来读取excel文件.但据说R对csv格式适应更加良好 ...

  2. R语言实战(三)基本图形与基本统计分析

    本文对应<R语言实战>第6章:基本图形:第7章:基本统计分析 =============================================================== ...

  3. R语言实战(二)数据管理

    本文对应<R语言实战>第4章:基本数据管理:第5章:高级数据管理 创建新变量 #建议采用transform()函数 mydata <- transform(mydata, sumx ...

  4. R语言实战(一)介绍、数据集与图形初阶

    本文对应<R语言实战>前3章,因为里面大部分内容已经比较熟悉,所以在这里只是起一个索引的作用. 第1章       R语言介绍 获取帮助函数 help(), ? 查看函数帮助 exampl ...

  5. Swift语言实战晋级

    Swift语言实战晋级基本信息作者: 老镇 丛书名: 爱上Swift出版社:人民邮电出版社ISBN:9787115378804上架时间:2014-12-26出版日期:2015 年1月开本:16开页码: ...

  6. swift语言实战晋级-第9章 游戏实战-跑酷熊猫-9-10 移除平台与视差滚动

    9.9 移除场景之外的平台 用为平台是源源不断的产生的,如果不注意销毁,平台就将越积越多,虽然在游戏场景中看不到.几十个还看不出问题,那几万个呢?几百万个呢? 所以我们来看看怎么移除平台,那什么样的平 ...

  7. swift语言实战晋级-第9章 游戏实战-跑酷熊猫-7-8 移动平台的算法

    在上个小节,我们完成了平台的产生.那么我们来实现一下让平台移动.平台的移动,我们只需要在平台工厂类中写好移动的方法,然后在GameScene类中统一控制就行了. 在GameScene类中,有个upda ...

  8. Swift语言实战晋级-第9章 游戏实战-跑酷熊猫-5-6 踩踏平台是怎么炼成的

    在游戏中,有很多分来飞去的平台,这个平台长短不一.如果每种长度都去创建一张图片那是比较繁琐的事情.实际上,我们只用到3张图.分别是平台的,平台的中间部分,平台的右边.关键是平台的中间部分,两张中间部分 ...

  9. Swift语言实战晋级-第9章 游戏实战-跑酷熊猫-4 熊猫的跳和打滚

    之前我们学会了跑的动作,现在我们可以利用同样的方法来实现了跳和打滚的动画. …… class Panda : SKSpriteNode { …… //跳的纹理集合 let jumpAtlas = SK ...

  10. Swift语言实战晋级-第9章 游戏实战-跑酷熊猫-3 显示一个动态的熊猫

    一个静态的熊猫明显不能满足我们的欲望,接下来我们就让熊猫跑起来.序列帧动画的原理就是不停的切换不同的图片.当我们将一张一张的切换Panda类的跑动文理后,熊猫就跑起来了.那么首先我们需要一个数组常量来 ...

随机推荐

  1. 重温Http协议--请求报文和响应报文

    http协议是位于应用层的协议,我们在日常浏览网页比如在导航网站请求百度首页的时候,会先通过http协议把请求做一个类似于编码的工作,发送给百度的服务器,然后在百度服务器响应请求时把相应的内容再通过h ...

  2. java中的字符串相关知识整理

    字符串为什么这么重要 写了多年java的开发应该对String不陌生,但是我却越发觉得它陌生.每学一门编程语言就会与字符串这个关键词打不少交道.看来它真的很重要. 字符串就是一系列的字符组合的串,如果 ...

  3. 关于 CSS 反射倒影的研究思考

    原文地址:https://css-tricks.com/state-css-reflections 译者:nzbin 友情提示:由于演示 demo 的兼容性,推荐火狐浏览.该文章篇幅较长,内容庞杂,有 ...

  4. Paypal开发中遇到请求被中止: 未能创建 SSL/TLS 安全通道及解决方案

    最近在基于ASP.NET上开发了Paypal支付平台,在ASP.NET开发的过程中没有遇到这个问题,但是引用到MVC开发模式中的时候就出现了"未能创建 SSL/TLS 安全通道及解决方案&q ...

  5. Discuz NT 架构剖析之Config机制

    接触了Discuz NT! 一段时间了,是时候做个总结了,标题好霸气,有木有? 都是托园子里的大牛代振军的福啊,哈哈哈哈. 首先论坛的信息不是完全存储在数据库里面的,一部分信息存储在config文件里 ...

  6. 使用RequireJS并实现一个自己的模块加载器 (一)

    RequireJS & SeaJS 在 模块化开发 开发以前,都是直接在页面上引入 script 标签来引用脚本的,当项目变得比较复杂,就会带来很多问题. JS项目中的依赖只有通过引入JS的顺 ...

  7. Visual Studio:error MSB8020(搬运)

    状况如下: error MSB8020: The builds tools for v120 (Platform Toolset = 'v120') cannot be found. To build ...

  8. JavaScript 正则表达式语法

    定义 JavaScript定义正则表达式有两种方法. 1.RegExp构造函数 var pattern = new RegExp("[bc]at","i"); ...

  9. H3 BPM让天下没有难用的流程之技术体系

    一.技术架构 H3 BPM 基于微软.NET 技术架构,采用C#语言开发,以高开放.高扩展.高性能为核心准则,遵循分层的设计原理,结合最新的B/S 以及智能手机应用开发技术研发的. 图:H3 BPM  ...

  10. Android 关于ijkplayer

    基于ijkplayer封装支持简单界面UI定制的视频播放器 可以解析ts格式的so库 怎样编译出可以解析ts等格式的so库?就是编译的时候需要在哪一步修改配置? 一些电视台的m3u8 CCTV1综合, ...