Gop - 编译和管理在GOPATH之外的Go工程

安装

go get github.com/lunny/gop

起子

自开始使用Go进行开发之后,工程一直都保存在GOPATH之下,Go1.5支持 vendor 机制之后开始使用 govendor 来管理依赖项。其实一直都有需求要将 Go 的工程放在 GOPATH 之外,因为在一个大的项目中,各种语言写的内容放在一个 git 工程的子文件夹中,但一直没有很好的工具来解决依赖的问题。

几个月之前,这个问题已经严重影响到工作了,终于不能忍受了。于是动手写了 Gop 的首个版本,最近又升级到了0.3版本。最新版本的 Gop 工程目录结构如下:

<project root>
├── gop.yml
├── bin
├── doc
└── src
    ├── main
    │   └── main.go
    ├── models
    │   └── models.go
    ├── routes
    │   └── routes.go
    └── vendor
        └── github.com
            ├── go-xorm
            │   ├── builder
            │   ├── core
            │   └── xorm
            └── lunny
                ├── log
                └── tango

通过以上的目录结构可以看到,其实gop是兼容GOPATH的。只要把 <project root> 设置到GOPATH中,即使没有安装gop命令,通过go命令也可以编译,但是这时对 vendor 管理也是不太方便。但如果使用 gop 来管理项目,则问题迎刃而解。

其中 gop.yml 的结构如下:

targets:
- name: myproject1
  dir: main
  assets:
  - templates
  - public
  - config.ini
  - key.pem
  - cert.pem
- name: myproject2
  dir: web
  assets:
  - templates
  - public
  - config.ini

除了可以放到 GOPATH 之外,Gop 工程还具有以下特性:

  • 多编译目标支持

默认的编译目标是 src/main 目录,编译名字是 src 的父目录的名字,当然你也可以通过 gop.yml 来自定义。如果输入gop build 命令,默认会编译第一个编译目标。如果指定了编译目标,如 gop build myproject2 那么将会编译指定的目标。默认编译好的可执行文件会放在 src/main/ 目录下,也可以通过 gop build -o ./bin/my.exe 来进行指定。除了 <target> 参数外,其它的参数和 go build 的参数相同。

  • 依赖管理

一般只需要执行 gop ensure -g 即可将所有的依赖项全部下载且拷贝到 vendor 下。如果依赖项需要更新,也可以使用 gop ensure -u 将所有的依赖项更新到最新的版本。不再需要的依赖项,可以通过 gop rm <package1> <package2> 删除,通过 gop status 可以查看默认编译目标的依赖项的安装情况。

  • 编译并运行某个目标

gop run 可以让你编译并运行某个目标,通过 -w,在源码修改之后还可以自动重新编译并运行。

  • 发布管理

gop release 编译,打包资源并拷贝到 bin 目录下。

Go语言Web框架Tango中的中间件应用级别

Tango在创建之初就支持了全局中间件的支持,经过最近的改进,同时支持了Group级别和Route级别的中间件。下面我们就一起来看下这三种级别的中间件:

比如我们自定义了一个新的中间件:

func MyMiddleware() tango.HandlerFunc {
    return func(ctx *tango.Context) {
        ctx.Info("I'm a middleware")
        ctx.Next()
    }
}

全局中间件

全局中间件在任何一个请求调用时均会进行调用。用法如下:

t := tango.Classic()
t.Use(MyMiddleware())
t.Get("/", func() string {return "global"})

Group中间件

Group中间件在Group下匹配的路由被调用时会被调用。用法如下:

t := tango.Classic()
t.Group("/group", func(g *tango.Group) {
    g.Use(MyMiddleware())
    g.Get("/", func() string {return "group"})
})

Route中间件

Route中间件在该Route被调用时会被调用。如果有多个,会按照先后顺序调用。用法如下:

t := tango.Classic()
t.Get("/route", func() string {return "route"}, MyMiddleware())

中间件优先级

  • 全局中间件被先调用,Group中间件次之,最后是Route中间件
  • 相同级别的中间件,先加入的被先调用

Go语言Web框架Tango在Session中间件中的设计

Tango在创建之初的目标就是既有Beego的效率,又有martini的灵活中间件。因此Session也是在核心库 tango 之外作为官方中间件存在,地址是 tango-session

Session因为有着灵活的设计,因此可以完成很多我们希望的功能。首先看下Session中间件的选项:

type Options struct {
    MaxAge           time.Duration
    SessionIdName    string
    Store            Store
    Generator        IdGenerator
    Tracker          Tracker
    OnSessionNew     func(*Session)
    OnSessionRelease func(*Session)
}

其中我将要着重讲的是Store,Generator和Tracker。

Store

Store是一个接口,主要作用是存储Session中的内容,其定义如下:

type Store interface {
    Add(id Id) bool
    Exist(id Id) bool
    Clear(id Id) bool

    Get(id Id, key string) interface{}
    Set(id Id, key string, value interface{}) error
    Del(id Id, key string) bool

    SetMaxAge(maxAge time.Duration)
    SetIdMaxAge(id Id, maxAge time.Duration)

    Run() error
}

默认的内核自带了MemoryStore,这将会把所有Session内容保存在内存中。同时官方中间件中也提供了

这几种方式进行Session内容的存储。当然如果你愿意,也可以自己来实现一个Store。

Generator

Generator是一个接口,主要封装了SessionID的生成算法,其定义如下:

type IdGenerator interface {
    Gen(req *http.Request) Id
    IsValid(id Id) bool
}

默认的Generator是Sha1Generator,他是通过req.RemoteAddr,当前时间和随机字符串生成。 当然你也可以自定义更好的算法来生成SessionID。

Tracker

Tracker是一个接口,主要封装了Session的跟踪方式,其定义如下:

type Tracker interface {
    SetMaxAge(maxAge time.Duration)
    Get(req *http.Request) (Id, error)
    Set(req *http.Request, rw http.ResponseWriter, id Id)
    Clear(rw http.ResponseWriter)
}

默认的Tracker实现是CookieTracker,就是我们最常见的,将SessionID保存在cookie中,通过cookie来进行跟踪。Session中间件中也同时提供了HeaderTracker,支持将SessionID保存在自定义的Http Header中。当然你也可以自定义Tracker,比如通过URL参数来进行跟踪等等方式。

最后

看起来似乎很复杂,但是一般情况下都不需要去改变,你只需要

t := tango.Classic()
t.Use(session.New())
t.Run()

Go语言库net/smtp发送邮件故障一则

在通过net/smtp包发送邮件时遇到一个奇怪的错误,在mac和windows上运行都能够成功发送邮件,但是将程序跨平台编译后上传到debian linux执行报:

x509: failed to load system roots and no roots provided

的错误,看样子是在认证邮件服务器证书的根证书时没有找到本地的根证书库。最后通过运行

#apt-get install ca-certificates

之后,邮件成功发送。