這是一個(gè)Web Server的時(shí)代,,apache2與nginx共舞,在追求極致性能的路上,,沒(méi)有最高,,只有更高。但這又是一個(gè)追求個(gè)性化的時(shí)代,,有些Web Server并沒(méi)有去擠“Performance提升”這一獨(dú)木橋,,而是有著自己的定位,Caddy就是這樣一個(gè)開(kāi)源Web Server,。
Caddy的作者M(jìn)att Holt在caddy官網(wǎng)以及FAQ中對(duì)caddy的目標(biāo)闡釋如下: 其他Web Server為Web而設(shè)計(jì),,Caddy為human設(shè)計(jì)。功能定位上,,與經(jīng)常充當(dāng)最前端反向代理的nginx不同,,caddy致力于成為一個(gè)易用的靜態(tài) 文件Web Server??梢钥闯鯟addy主打易用性,,使用配置簡(jiǎn)單,。并且得益于Go的跨平臺(tái)特性,caddy很容易的支持了三大主流平臺(tái):Windows,、 Linux,、Mac。在Caddy開(kāi)發(fā)者文檔中,,我們可以看到caddy還可以在Android(linux arm)上運(yùn)行,。caddy目前版本為0.7.1,還不穩(wěn)定,,且后續(xù)版本可能變化較大,,甚至與前期版本不兼容,因此作者目前不推薦caddy在生產(chǎn)環(huán)境被 重度使用,。
關(guān)注caddy,,是因?yàn)閏addy填補(bǔ)了go在通用web server這塊的空白(也許有其他,但我還不知道),,同時(shí)Web server in go也“響應(yīng)”了近期Golang去C化的趨勢(shì)(Go 1.5中C is gone,!),即便caddy作者提到caddy的目標(biāo)并非如nginx那樣,。但未來(lái)誰(shuí)知道呢,?一旦Go性能足夠高時(shí),一旦caddy足夠穩(wěn)定時(shí),,自然而 然的就會(huì)有人將其用在某些應(yīng)用的生產(chǎn)環(huán)境中替代nginx或apache2了,。一套全Go的系統(tǒng),在部署,、運(yùn)維方面也是有優(yōu)勢(shì)的,。
一、安裝和運(yùn)行caddy
和諸多go應(yīng)用一樣,,我們可以直接從caddy的github.com releases頁(yè)中找到最新發(fā)布版(目前是0.7.1)的二進(jìn)制包,。這里使用的是caddy_darwin_amd64.zip。
下載解壓后,,進(jìn)入目錄,,直接執(zhí)行./caddy即可將caddy運(yùn)行起來(lái),。
$caddy
0.0.0.0:2015
在瀏覽器里訪問(wèn)localhost:2015,,頁(yè)面上沒(méi)有預(yù)期顯示的類似"caddy works!”之類的默認(rèn)Welcome頁(yè)面,而是“404 Not Found",。雖然這說(shuō)明caddy已經(jīng)work了,,但沒(méi)有一個(gè)default welcome page畢竟對(duì)于caddy beginer來(lái)說(shuō)并不友好。這里已經(jīng)向作者提了一個(gè)sugguestion issue,。
二,、caddy原理
Go的net/http標(biāo)準(zhǔn)庫(kù)已經(jīng)提供了http server的實(shí)現(xiàn),,大多數(shù)場(chǎng)合這個(gè)http server都能滿足你的需要,無(wú)論是功能還是性能,。Caddy實(shí)質(zhì)上也是一個(gè)Go web app,,它也import net/http,嵌入*http.Server,,并通過(guò)handler的ServeHTTP方法為每個(gè)請(qǐng)求提供服務(wù),。caddy使用 http.FileServer作為處理 靜態(tài)文件的基礎(chǔ)。caddy的誘人之處在于其middleware,,將諸多middleware串成一個(gè)middleware chain以提供了靈活的web服務(wù),。另外caddy中的middleware還可以獨(dú)立于caddy之外使用。
caddy從當(dāng)前目錄的Caddyfile(默認(rèn))文件中讀取配置,,當(dāng)然你也可以通過(guò)-conf指定配置文件路徑,。Caddyfile的配置格式 的確非常easy,這也符合caddy的目標(biāo),。
Caddyfile總是以站點(diǎn)的Addr開(kāi)始的,。
單一站點(diǎn)的Caddyfile樣例如下:
//Caddyfile
localhost:2015
gzip
log ./2015.log
Caddy也支持配置多個(gè)站點(diǎn),類似virtualhost的 配置(80端口多路復(fù)用):
//Caddyfile
foo.com:80 {
log ./foo.log
gzip
}
bar.com:80 {
log ./bar.log
gzip
}
為了實(shí)現(xiàn)風(fēng)格上的統(tǒng)一,單一站點(diǎn)也最好配置為如下這種格式(代碼內(nèi)部稱之為 Server Block):
localhost:2015 {
gzip
log ./2015.log
}
這樣Caddyfile的配置文件模板樣式類似于下面這樣:
host1:port {
middleware1
middleware2 {
… …
}
… …
}
host2:port {
middleware1
middleware2 {
… …
}
… …
}
… …
關(guān)于middleware,,在caddy文檔中有較為詳細(xì)的說(shuō)明和例子,。對(duì)于caddy這樣一個(gè)年輕的開(kāi)源項(xiàng)目而言,其文檔還算是相對(duì)較全的,,雖 然現(xiàn)在還不能和nginx,、 apache比。
caddy中的middleware就是一個(gè)實(shí)現(xiàn)了middleware.Handler接口的struct,,例如gzip這個(gè) middleware:
// middleware.go
type Middleware func(Handler) Handler
type Handler interface {
ServeHTTP(http.ResponseWriter, *http.Request) (int, error)
}
// gzip/gzip.go
type Gzip struct {
Next middleware.Handler
}
func (g Gzip) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
return g.Next.ServeHTTP(w, r)
}
…. …
gz := gzipResponseWriter{Writer: gzipWriter, ResponseWriter: w}
// Any response in forward middleware will now be compressed
status, err := g.Next.ServeHTTP(gz, r)
… …
}
middleware.Handler的函數(shù)原型與http.Handler的不同,,不能直接作為http.Server的Handler使用。caddy使用了下面這個(gè)idiomatic go pattern:
type appHandler func(http.ResponseWriter, *http.Request) (int, error)
func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if status, err := fn(w, r); err != nil {
http.Error(w, err.Error(), status)
}
}
當(dāng)然這個(gè)pattern有很多變種,,但思路大致類似,。一個(gè)middleware chain大致就是handler1(handler2(handler3))的調(diào)用傳遞。
前面說(shuō)過(guò)caddy是基于http.FileServer的靜態(tài)文件Web Server,,F(xiàn)ileServer總會(huì)作為middleware chain的最后一環(huán),,如果沒(méi)有配置任何middleware,那你的server就是一個(gè)靜態(tài)文件server,。
三,、caddy典型應(yīng)用
【靜態(tài)文件Server】
caddy的最基礎(chǔ)應(yīng)用實(shí)際就是一個(gè)靜態(tài)文件Server,底層由http.FileServer承載,,當(dāng)然caddy封裝了http.FileServer,,做了一些攔截處理,最后將w, r傳遞給http.ServeContent去處理文件數(shù)據(jù),。
第一次執(zhí)行./caddy,,實(shí)際上就啟動(dòng)了一個(gè)靜態(tài)文件Server,。但這個(gè)server不默認(rèn)支持你navigate directory。如果你知道website root目錄(如果沒(méi)有指定root,,則caddy執(zhí)行的當(dāng)前路徑會(huì)作為website的root路徑)下的文件名,,比如foo.txt,你可以在瀏覽器 中輸入:localhost:2015/foo.txt,,caddy會(huì)執(zhí)行正確的服務(wù),,瀏覽器也會(huì)顯示foo.txt的全文。
對(duì)于靜態(tài)文件Server,,caddy支持在website的root路徑下首先查找是否有如下四個(gè)文件:
//caddy/middleware/browse/browse.go
var IndexPages = []string{
"index.html",
"index.htm",
"default.html",
"default.htm",
}
如果查到有其中一個(gè),,則優(yōu)先返回這個(gè)文件內(nèi)容,這就是靜態(tài)站點(diǎn)的首頁(yè),。
如果要支持目錄文件列表瀏覽,,則需要為website配置browse middleware,這樣對(duì)于無(wú)index file的目錄,,我們可以看到目錄文件列表,。
localhost:2015 {
browse
}
【反向代理】
caddy支持基本的反向代理功能。反向代理配置通過(guò)proxy middleware實(shí)現(xiàn),。
localhost:2015 {
log ./2015.log
proxy /foo localhost:9001
proxy /bar localhost:9002
}
當(dāng)你訪問(wèn)localhost:2015/foo時(shí),,實(shí)際上訪問(wèn)的是9001端口的服務(wù)程序;
當(dāng)你訪問(wèn)localhost:2015/bar時(shí),,實(shí)際上訪問(wèn)的是9002端口的服務(wù)程序,。
【負(fù)載均衡】
Caddy支持負(fù)載均衡配置,并支持三種負(fù)載均衡算法:random(隨機(jī)),、least_conn(最少連接)以及round_robin(輪詢調(diào)度),。
負(fù)載均衡同樣是通過(guò)proxy middleware實(shí)現(xiàn)的。
localhost:2015 {
log ./2015.log
proxy / localhost:9001 localhost:9003 {
policy round_robin
}
proxy /bar localhost:9002 localhost:9004 {
policy least_conn
}
}
【支持fastcgi代理】
mac os上自帶了php-fpm,,一個(gè)實(shí)現(xiàn)了fastcgi的php cgi進(jìn)程管理器。caddy將請(qǐng)求轉(zhuǎn)發(fā)給php-fpm監(jiān)聽(tīng)的端口,,后者會(huì)啟動(dòng)php-cgi解釋器,,解釋index.php,并將結(jié)果返回給caddy,。
mac os上的php-fpm默認(rèn)沒(méi)有隨機(jī)啟動(dòng),。我們需要簡(jiǎn)單配置一下:
$mkdir phptest
$mkdir -p phptest/etc
$mkdir -p phptest/log
$cd phptest
$sudo cp /private/etc/php-fpm.conf.default ./etc
$cd ./etc
$sudo chown tony php-fpm.conf.default
$mv php-fpm.conf.default php-fpm.conf
編輯php-fpm.conf,保證下面兩項(xiàng)是非注釋狀態(tài)的:
error_log = log/php-fpm.log
listen = 127.0.0.1:9000
我們通過(guò)network socket進(jìn)行fastcgi通信,。
回到phptest目錄下,,執(zhí)行:
php-fpm -p ~/test/go/caddy/phptest
執(zhí)行后,php-fpm就會(huì)轉(zhuǎn)入后臺(tái)執(zhí)行了,。
接下來(lái)我們來(lái)配置Caddyfile:
localhost:2015 {
fastcgi / 127.0.0.1:9000 php
log ./2015.log
}
這里配置的含義是:將全部請(qǐng)求轉(zhuǎn)發(fā)到9000端口,,這里的php是一個(gè)preset(預(yù)配置集合),相當(dāng)于:
ext .php
split .php
index index.php
我們?cè)趐hptest目錄下創(chuàng)建一個(gè)index.php文件,,內(nèi)容如下:
<?php echo "Hello World\n"; ?>
好了,,現(xiàn)在啟動(dòng)caddy,并使用瀏覽器訪問(wèn)localhost:2015試試,。你會(huì)看到"Hello World"呈現(xiàn)在瀏覽器中,。
【git push發(fā)布】
對(duì)于一些靜態(tài)站點(diǎn),caddy支持git directive,,實(shí)現(xiàn)在server啟動(dòng)以及運(yùn)行時(shí)定期git pull你的項(xiàng)目庫(kù),,將最新更新pull到server上。
caddy文檔中給出兩個(gè)例子:
第一個(gè)是一個(gè)php站點(diǎn),,定期pull項(xiàng)目庫(kù),,實(shí)現(xiàn)server更新:
git git@github.com:user/myphpsite {
key /home/user/.ssh/id_rsa
}
fastcgi / 127.0.0.1:9000 php
第二個(gè)是一個(gè)hugo支撐的靜態(tài)站點(diǎn),每次pull后,,執(zhí)行hugo命令生成新的靜態(tài)頁(yè)面:
git github.com/user/site {
path ../
then hugo –destination=/home/user/hugosite/public
}
注意:git directive并非middleware,,而是一個(gè)單獨(dú)的goroutine實(shí)現(xiàn)的。
四,、小結(jié)
caddy的功能不局限于上面的幾個(gè)例子,,上面只是幾個(gè)最為常見(jiàn)的場(chǎng)景而已。caddy目前還很年輕,,應(yīng)用不多,,但知名golang網(wǎng)站 gopheracademy.com(GopherCon組織方)是由Caddy support的。caddy還在積極進(jìn)化,,有興趣的Gopher可持續(xù)關(guān)注,。
© 2015, bigwhite. 版權(quán)所有.