323 lines
14 KiB
HTML
Executable File
323 lines
14 KiB
HTML
Executable File
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<title>风笑痴 - 一个脱离了低级懒惰的人</title>
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<meta name="author" content="lunny">
|
||
<meta name="keywords" content="lunny,golang,xorm,tango,blog"/>
|
||
<meta name="description" content="风笑痴,一个脱离了低级懒惰的人"/>
|
||
<link href="/css/pure-min.css" type="text/css" rel="stylesheet" media="all">
|
||
<link href="/css/blog.css" type="text/css" rel="stylesheet" media="all">
|
||
<link href="/css/railscasts.css" type="text/css" rel="stylesheet" media="all"/>
|
||
</head>
|
||
<body class="index" data-perma="index">
|
||
<div class="pure-g-r" id="layout">
|
||
<div class="sidebar pure-u">
|
||
<header class="header">
|
||
<hgroup>
|
||
<h1 class="brand-title">
|
||
<a href="/" class="brand-title">风笑痴</a>
|
||
</h1>
|
||
<h2 class="brand-tagline">一个脱离了低级懒惰的人</h2>
|
||
</hgroup>
|
||
<nav class="nav">
|
||
<ul class="nav-list">
|
||
<li class="">
|
||
<a href="/">文章</a>
|
||
</li>
|
||
<li class="">
|
||
<a href="/archive">归档</a>
|
||
</li>
|
||
<li class="">
|
||
<a href="/about" target="_blank" >关于</a>
|
||
</li>
|
||
</ul>
|
||
</nav>
|
||
</header>
|
||
</div>
|
||
<div class="pure-u-1">
|
||
<div class="content">
|
||
<div class="page-header">
|
||
<h1>所有文章</h1>
|
||
</div>
|
||
|
||
<div class="post">
|
||
<h3 class="post-header"><a href="/2017/8/15/Gop---%e7%bc%96%e8%af%91%e5%92%8c%e7%ae%a1%e7%90%86%e5%9c%a8GOPATH%e4%b9%8b%e5%a4%96%e7%9a%84Go%e5%b7%a5%e7%a8%8b.html">Gop - 编译和管理在GOPATH之外的Go工程</a></h3>
|
||
<div class="post-meta">
|
||
<small>2017-8-15</small>
|
||
</div>
|
||
<div class="post-description"><h1 id="安装">安装</h1>
|
||
<pre><code class="language-go">go get github.com/lunny/gop
|
||
</code></pre>
|
||
|
||
<h1 id="起子">起子</h1>
|
||
|
||
<p>自开始使用Go进行开发之后,工程一直都保存在GOPATH之下,Go1.5支持 <code>vendor</code> 机制之后开始使用 <code>govendor</code> 来管理依赖项。其实一直都有需求要将 Go 的工程放在 GOPATH 之外,因为在一个大的项目中,各种语言写的内容放在一个 git 工程的子文件夹中,但一直没有很好的工具来解决依赖的问题。</p>
|
||
|
||
<p>几个月之前,这个问题已经严重影响到工作了,终于不能忍受了。于是动手写了 <code>Gop</code> 的首个版本,最近又升级到了0.3版本。最新版本的 <code>Gop</code> 工程目录结构如下:</p>
|
||
<pre><code><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
|
||
</code></pre>
|
||
|
||
<p>通过以上的目录结构可以看到,其实gop是兼容GOPATH的。只要把 <code><project root></code> 设置到GOPATH中,即使没有安装<code>gop</code>命令,通过<code>go</code>命令也可以编译,但是这时对 <code>vendor</code> 管理也是不太方便。但如果使用 <code>gop</code> 来管理项目,则问题迎刃而解。</p>
|
||
|
||
<p>其中 <code>gop.yml</code> 的结构如下:</p>
|
||
<pre><code class="language-yml">targets:
|
||
- name: myproject1
|
||
dir: main
|
||
assets:
|
||
- templates
|
||
- public
|
||
- config.ini
|
||
- key.pem
|
||
- cert.pem
|
||
- name: myproject2
|
||
dir: web
|
||
assets:
|
||
- templates
|
||
- public
|
||
- config.ini
|
||
</code></pre>
|
||
|
||
<p>除了可以放到 <code>GOPATH</code> 之外,Gop 工程还具有以下特性:</p>
|
||
|
||
<ul>
|
||
<li>多编译目标支持</li>
|
||
</ul>
|
||
|
||
<p>默认的编译目标是 <code>src/main</code> 目录,编译名字是 <code>src</code> 的父目录的名字,当然你也可以通过 <code>gop.yml</code> 来自定义。如果输入<code>gop build</code> 命令,默认会编译第一个编译目标。如果指定了编译目标,如 <code>gop build myproject2</code> 那么将会编译指定的目标。默认编译好的可执行文件会放在 <code>src/main/</code> 目录下,也可以通过 <code>gop build -o ./bin/my.exe</code> 来进行指定。除了 <code><target></code> 参数外,其它的参数和 <code>go build</code> 的参数相同。</p>
|
||
|
||
<ul>
|
||
<li>依赖管理</li>
|
||
</ul>
|
||
|
||
<p>一般只需要执行 <code>gop ensure -g</code> 即可将所有的依赖项全部下载且拷贝到 <code>vendor</code> 下。如果依赖项需要更新,也可以使用 <code>gop ensure -u</code> 将所有的依赖项更新到最新的版本。不再需要的依赖项,可以通过 <code>gop rm <package1> <package2></code> 删除,通过 <code>gop status</code> 可以查看默认编译目标的依赖项的安装情况。</p>
|
||
|
||
<ul>
|
||
<li>编译并运行某个目标</li>
|
||
</ul>
|
||
|
||
<p><code>gop run</code> 可以让你编译并运行某个目标,通过 <code>-w</code>,在源码修改之后还可以自动重新编译并运行。</p>
|
||
|
||
<ul>
|
||
<li>发布管理</li>
|
||
</ul>
|
||
|
||
<p><code>gop release</code> 编译,打包资源并拷贝到 <code>bin</code> 目录下。</p>
|
||
</div>
|
||
<div class="more">
|
||
<a href="/2017/8/15/Gop---%e7%bc%96%e8%af%91%e5%92%8c%e7%ae%a1%e7%90%86%e5%9c%a8GOPATH%e4%b9%8b%e5%a4%96%e7%9a%84Go%e5%b7%a5%e7%a8%8b.html" class="btn pure-button pure-button-primary">继续阅读..</a>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="post">
|
||
<h3 class="post-header"><a href="/2016/5/12/Go%e8%af%ad%e8%a8%80Web%e6%a1%86%e6%9e%b6Tango%e4%b8%ad%e7%9a%84%e4%b8%ad%e9%97%b4%e4%bb%b6%e5%ba%94%e7%94%a8%e7%ba%a7%e5%88%ab.html">Go语言Web框架Tango中的中间件应用级别</a></h3>
|
||
<div class="post-meta">
|
||
<small>2016-5-12</small>
|
||
</div>
|
||
<div class="post-description"><p>Tango在创建之初就支持了全局中间件的支持,经过最近的改进,同时支持了Group级别和Route级别的中间件。下面我们就一起来看下这三种级别的中间件:</p>
|
||
|
||
<p>比如我们自定义了一个新的中间件:</p>
|
||
<pre><code class="language-go">func MyMiddleware() tango.HandlerFunc {
|
||
return func(ctx *tango.Context) {
|
||
ctx.Info("I'm a middleware")
|
||
ctx.Next()
|
||
}
|
||
}
|
||
</code></pre>
|
||
|
||
<h1 id="全局中间件">全局中间件</h1>
|
||
|
||
<p>全局中间件在任何一个请求调用时均会进行调用。用法如下:</p>
|
||
<pre><code class="language-go">t := tango.Classic()
|
||
t.Use(MyMiddleware())
|
||
t.Get("/", func() string {return "global"})
|
||
</code></pre>
|
||
|
||
<h1 id="group中间件">Group中间件</h1>
|
||
|
||
<p>Group中间件在Group下匹配的路由被调用时会被调用。用法如下:</p>
|
||
<pre><code class="language-go">t := tango.Classic()
|
||
t.Group("/group", func(g *tango.Group) {
|
||
g.Use(MyMiddleware())
|
||
g.Get("/", func() string {return "group"})
|
||
})
|
||
</code></pre>
|
||
|
||
<h1 id="route中间件">Route中间件</h1>
|
||
|
||
<p>Route中间件在该Route被调用时会被调用。如果有多个,会按照先后顺序调用。用法如下:</p>
|
||
<pre><code class="language-go">t := tango.Classic()
|
||
t.Get("/route", func() string {return "route"}, MyMiddleware())
|
||
</code></pre>
|
||
|
||
<h1 id="中间件优先级">中间件优先级</h1>
|
||
|
||
<ul>
|
||
<li>全局中间件被先调用,Group中间件次之,最后是Route中间件</li>
|
||
<li>相同级别的中间件,先加入的被先调用</li>
|
||
</ul>
|
||
</div>
|
||
<div class="more">
|
||
<a href="/2016/5/12/Go%e8%af%ad%e8%a8%80Web%e6%a1%86%e6%9e%b6Tango%e4%b8%ad%e7%9a%84%e4%b8%ad%e9%97%b4%e4%bb%b6%e5%ba%94%e7%94%a8%e7%ba%a7%e5%88%ab.html" class="btn pure-button pure-button-primary">继续阅读..</a>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="post">
|
||
<h3 class="post-header"><a href="/2016/2/18/Go%e8%af%ad%e8%a8%80Web%e6%a1%86%e6%9e%b6Tango%e5%9c%a8Session%e4%b8%ad%e9%97%b4%e4%bb%b6%e4%b8%ad%e7%9a%84%e8%ae%be%e8%ae%a1.html">Go语言Web框架Tango在Session中间件中的设计</a></h3>
|
||
<div class="post-meta">
|
||
<small>2016-2-18</small>
|
||
</div>
|
||
<div class="post-description"><p>Tango在创建之初的目标就是既有Beego的效率,又有martini的灵活中间件。因此Session也是在核心库 <a href="https://github.com/lunny/tango">tango</a> 之外作为官方中间件存在,地址是 <a href="https://github.com/tango-contrib/session">tango-session</a>。</p>
|
||
|
||
<p>Session因为有着灵活的设计,因此可以完成很多我们希望的功能。首先看下Session中间件的选项:</p>
|
||
<pre><code class="language-go">type Options struct {
|
||
MaxAge time.Duration
|
||
SessionIdName string
|
||
Store Store
|
||
Generator IdGenerator
|
||
Tracker Tracker
|
||
OnSessionNew func(*Session)
|
||
OnSessionRelease func(*Session)
|
||
}
|
||
</code></pre>
|
||
|
||
<p>其中我将要着重讲的是Store,Generator和Tracker。</p>
|
||
|
||
<h2 id="store">Store</h2>
|
||
|
||
<p>Store是一个接口,主要作用是存储Session中的内容,其定义如下:</p>
|
||
<pre><code class="language-go">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
|
||
}
|
||
</code></pre>
|
||
|
||
<p>默认的内核自带了<code>MemoryStore</code>,这将会把所有Session内容保存在内存中。同时官方中间件中也提供了</p>
|
||
|
||
<ul>
|
||
<li><a href="http://github.com/tango-contrib/session-nodb">nodb</a></li>
|
||
<li><a href="http://github.com/tango-contrib/session-redis">redis</a></li>
|
||
<li><a href="http://github.com/tango-contrib/session-ledis">ledis</a></li>
|
||
<li><a href="http://github.com/tango-contrib/session-ssdb">ssdb</a></li>
|
||
</ul>
|
||
|
||
<p>这几种方式进行Session内容的存储。当然如果你愿意,也可以自己来实现一个Store。</p>
|
||
|
||
<h2 id="generator">Generator</h2>
|
||
|
||
<p>Generator是一个接口,主要封装了SessionID的生成算法,其定义如下:</p>
|
||
<pre><code class="language-go">type IdGenerator interface {
|
||
Gen(req *http.Request) Id
|
||
IsValid(id Id) bool
|
||
}
|
||
</code></pre>
|
||
|
||
<p>默认的Generator是Sha1Generator,他是通过<code>req.RemoteAddr</code>,当前时间和随机字符串生成。
|
||
当然你也可以自定义更好的算法来生成SessionID。</p>
|
||
|
||
<h2 id="tracker">Tracker</h2>
|
||
|
||
<p>Tracker是一个接口,主要封装了Session的跟踪方式,其定义如下:</p>
|
||
<pre><code class="language-go">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)
|
||
}
|
||
</code></pre>
|
||
|
||
<p>默认的Tracker实现是<code>CookieTracker</code>,就是我们最常见的,将SessionID保存在cookie中,通过cookie来进行跟踪。Session中间件中也同时提供了<code>HeaderTracker</code>,支持将SessionID保存在自定义的Http Header中。当然你也可以自定义Tracker,比如通过URL参数来进行跟踪等等方式。</p>
|
||
|
||
<h2 id="最后">最后</h2>
|
||
|
||
<p>看起来似乎很复杂,但是一般情况下都不需要去改变,你只需要</p>
|
||
<pre><code class="language-go">t := tango.Classic()
|
||
t.Use(session.New())
|
||
t.Run()
|
||
</code></pre>
|
||
</div>
|
||
<div class="more">
|
||
<a href="/2016/2/18/Go%e8%af%ad%e8%a8%80Web%e6%a1%86%e6%9e%b6Tango%e5%9c%a8Session%e4%b8%ad%e9%97%b4%e4%bb%b6%e4%b8%ad%e7%9a%84%e8%ae%be%e8%ae%a1.html" class="btn pure-button pure-button-primary">继续阅读..</a>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="post">
|
||
<h3 class="post-header"><a href="/2016/1/7/Go%e8%af%ad%e8%a8%80%e5%ba%93net/smtp%e5%8f%91%e9%80%81%e9%82%ae%e4%bb%b6%e6%95%85%e9%9a%9c%e4%b8%80%e5%88%99.html">Go语言库net/smtp发送邮件故障一则</a></h3>
|
||
<div class="post-meta">
|
||
<small>2016-1-7</small>
|
||
</div>
|
||
<div class="post-description"><p>在通过net/smtp包发送邮件时遇到一个奇怪的错误,在mac和windows上运行都能够成功发送邮件,但是将程序跨平台编译后上传到debian linux执行报:</p>
|
||
<pre><code>x509: failed to load system roots and no roots provided
|
||
</code></pre>
|
||
|
||
<p>的错误,看样子是在认证邮件服务器证书的根证书时没有找到本地的根证书库。最后通过运行</p>
|
||
<pre><code>#apt-get install ca-certificates
|
||
</code></pre>
|
||
|
||
<p>之后,邮件成功发送。</p>
|
||
</div>
|
||
<div class="more">
|
||
<a href="/2016/1/7/Go%e8%af%ad%e8%a8%80%e5%ba%93net/smtp%e5%8f%91%e9%80%81%e9%82%ae%e4%bb%b6%e6%95%85%e9%9a%9c%e4%b8%80%e5%88%99.html" class="btn pure-button pure-button-primary">继续阅读..</a>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
<div class="pagination pure-g">
|
||
<ul>
|
||
|
||
<li class="next pure-u-1-3"><a href="/posts/2.html">以前的 →</a></li>
|
||
</ul>
|
||
</div>
|
||
<div class="footer">
|
||
<p>© 风笑痴 2015
|
||
powered by <a href="http://github.com/go-xiaohei/pugo">PuGo</a> with <a href="http://purecss.io" target="_blank">Pure</a>
|
||
</p>
|
||
|
||
|
||
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<script src="/js/jquery-2.1.4.min.js"></script>
|
||
<script src="/js/highlight.pack.js"></script>
|
||
<script>
|
||
$(document).ready(function() {
|
||
$('pre code').each(function(i, block) {
|
||
hljs.highlightBlock(block);
|
||
});
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|