跟著Caddy源碼學(xué)習(xí)Golang(一)

按:
源碼地址:https://github.com/mholt/caddy
對(duì)Golang不是很熟悉单旁,算是初學(xué)疾渣,並沒(méi)有什麼好的方式進(jìn)行深入的學(xué)習(xí)漾肮,故選此項(xiàng)目研究清焕。
假定我們都是初學(xué)Golang,或者是熟悉其他語(yǔ)言蝠猬,但Golang不瞭解切蟋,那麼我們下面就從0開(kāi)始來(lái)看看,寫(xiě)Golang到底是啥樣榆芦!
分析不當(dāng)柄粹,望指正!

Caddy

0. 項(xiàng)目介紹

Caddy是一款輕量級(jí)通用性很強(qiáng)的web服務(wù)器匆绣,支持在Windows驻右,Mac,Linux崎淳,BSD堪夭,以及Android多種平臺(tái)。她完全能夠替代其他主流的網(wǎng)絡(luò)服務(wù)器拣凹。

1. 項(xiàng)目結(jié)構(gòu)

  • 雲(yún)端構(gòu)建
  • appveyor
  • 依賴(lài)
    • golang.org/x/tools/cmd/vet
Caddy /

main_test.go爲(wèi)測(cè)試文件

go test

完成測(cè)試操作森爽。

簡(jiǎn)單看下main_test.go文件,

package main

import (
    "runtime" // runtime包嚣镜,運(yùn)行時(shí)的系統(tǒng)交互操作
    "testing" // testing包爬迟,自動(dòng)化測(cè)試
)

func TestSetCPU(t *testing.T) {
    // 測(cè)試設(shè)置併發(fā)數(shù)
    currentCPU := runtime.GOMAXPROCS(-1) // 設(shè)置最大併發(fā)數(shù),參數(shù)小於1時(shí)保持當(dāng)前設(shè)置
    maxCPU := runtime.NumCPU() // 獲取機(jī)器上的邏輯cpu數(shù)
    halfCPU := int(0.5 * float32(maxCPU))
    if halfCPU < 1 {
        halfCPU = 1
    }
    /*
    * 下面這段簡(jiǎn)化爲(wèi)
    */
    for i, test := range []struct {
        input string
        output int
        shouldErr bool
    }{
        {"1", 1, false},
        {"-1", currentCPU, true},
        {"0", currentCPU, true},
        {"100%", maxCPU, false},
        {"50%", halfCPU, false},
        {"110%", currentCPU, true},
        {"-10%", currentCPU, true},
        {"invalid input", currentCPU, true},
        {"invalid input%", currentCPU, true},
        {"9999", maxCPU, false}, // over available CPU
    } {
        err := setCPU(test.input)
        if test.shouldErr && err == nil {
          t.Errorf("Test %d: Expected error, but there wasn't any", i)
        }
        if !test.shouldErr && err != nil {
            t.Errorf("Test %d: Expected no error, but there was one: %v", i, err)
        }
        if actual, expected := runtime.GOMAXPROCS(-1), test.output; actual != expected {
            t.Errorf("Test %d: GOMAXPROCS was %d but expected %d", i, actual, expected)
        }
        // teardown
        runtime.GOMAXPROCS(currentCPU)
    }
}

2. 讀源碼

/main.go

1. 包名及導(dǎo)入相關(guān)包

package main

import (
    "errors" // 實(shí)現(xiàn)操作錯(cuò)誤的函數(shù)菊匿,類(lèi)似於Python中的Expection,但用法存在很大差異付呕,後面再介紹
    "flag" // 解析命令行輸入?yún)?shù)
    "fmt"  // 格式化工具
    "io/ioutil" // IO操作
    "log" // 日誌
    "os" // 操作系統(tǒng)
    "runtime" // 運(yùn)行時(shí)環(huán)境
    "strconv" // 字符串於一些基本類(lèi)型的轉(zhuǎn)換
    "strings" // 處理UTF-8編碼的字符串
    "time" // 時(shí)間
    "github.com/mholt/caddy/caddy" // 主包
    "github.com/mholt/caddy/caddy/letsencrypt" // Let's Encrypt TLS
)

簡(jiǎn)單下介紹下幾個(gè)包计福,

  • error
    實(shí)現(xiàn)操作錯(cuò)誤的函數(shù),用法是先定義一個(gè)結(jié)構(gòu)體徽职,然後給這個(gè)結(jié)構(gòu)體實(shí)現(xiàn)一個(gè)Error()『方法』棒搜。
  • flag
    flag.String(N1, N2, N3),有反回值返回值返回給字面量活箕,N1是變量參數(shù)名,N2是默認(rèn)參數(shù)可款,N3是提示字符串育韩;
    flag.VarString(N1, N2, N3),N1爲(wèi)存儲(chǔ)的指針
/*
* 定義變量
*/
var (
    conf string
    cpu string
    logfile string
    revoke string
    version bool
)

/*
* 定義常量
*/
const (
    appName = "Caddy"
    appVersion = "0.8"
)

初始化函數(shù)闺鲸,這個(gè)函數(shù)再main()函數(shù)之前執(zhí)行筋讨,

func init() {
    caddy.TrapSignals()
    /*
    * 命令行參數(shù)的初始化,再main函數(shù)中flag.Parse()就能獲取到輸入的值
    */
    flag.BoolVar(&letsencrypt.Agreed, "agree", false, "Agree to Let's Encrypt Subscriber Agreement")
    flag.StringVar(&letsencrypt.CAUrl, "ca", "https://acme-v01.api.letsencrypt.org/directory", "Certificate authority ACME server")
    .
    .
    .
}

下面看下caddy包內(nèi)TrapSignals()函數(shù)摸恍,這個(gè)函數(shù)再/caddy/sigtrap.go悉罕,

// TrapSignals create signal handlers for all applicable signals for this
// system. If your Go program uses signals, this is a rather invasive
// function; best to implement them yourself in that case. Signals are not
// required for the caddy package to function properly, but this is a
// convenient way to allow the user to control this package of your program.

func TrapSignals() {
    trapSignalsCrossPlatform()

    /*
    * 這個(gè)函數(shù)不在這個(gè)文件中,但同樣是在`caddy`包內(nèi)立镶,
    * 在/caddy/sigtrap_posix.go文件中壁袄,
    * 所以可以直接調(diào)用,這點(diǎn)類(lèi)似於Java媚媒,而不同於Python以文件來(lái)區(qū)分包
    */
    trapSignalsPosix()
}

// trapSignalsCrossPlatform captures SIGINT, which triggers forceful
// shutdown that executes shutdown callbacks first. A second interrupt
// signal will exit the process immediately.

func trapSignalsCrossPlatform() {
    go func() {
        shutdown := make(chan os.Signal, 1)
        signal.Notify(shutdown, os.Interrupt)

        for i := 0; true; i++ {
            <-shutdown

            if i > 0 {
                log.Println("[INFO] SIGINT: Force quit")
                os.Exit(1)
            }

            log.Println("[INFO] SIGINT: Shutting down")
            go os.Exit(executeShutdownCallbacks("SIGINT"))
        }
    }()
}

// executeShutdownCallbacks executes the shutdown callbacks as initiated
// by signame. It logs any errors and returns the recommended exit status.
// This function is idempotent; subsequent invocations always return 0.
func executeShutdownCallbacks(signame string) (exitCode int) {
    shutdownCallbacksOnce.Do(func() {
        serversMu.Lock()
        errs := server.ShutdownCallbacks(servers)
        serversMu.Unlock()

        if len(errs) > 0 {
            for _, err := range errs {
                log.Printf("[ERROR] %s shutdown: %v", signame, err)
            }
            exitCode = 1
        }
    })
    return
}

var shutdownCallbacksOnce sync.Once

下面嘗試分析下嗜逻,這個(gè)函數(shù)或者說(shuō)這些函數(shù)是來(lái)幹什麼的?
初看下註釋?zhuān)蟾诺囊馑际晴哉伲@是一個(gè)信號(hào)槽栈顷,不知這樣理解是否妥當(dāng)。它主要是用來(lái)做程序功能的切換嵌巷,或者說(shuō)響應(yīng)萄凤,簡(jiǎn)單點(diǎn)就是事件的處理或者分發(fā),當(dāng)然在這裏是信號(hào)搪哪。

先看trapSignalsCrossPlatform()這個(gè)函數(shù)靡努,
跨平臺(tái)信號(hào)捕捉,觸發(fā)強(qiáng)制關(guān)閉回調(diào)的停止信號(hào)噩死,第二次中斷立即退出颤难。
go func(){}(),Golang的殺手鐧已维,併發(fā)行嗤,關(guān)鍵字go,來(lái)執(zhí)行這個(gè)匿名函數(shù)垛耳。make()變量的申明栅屏,只適用於通道飘千、切片字典這三種引用類(lèi)型(底層就是數(shù)組)栈雳,返回的是值本身护奈,已經(jīng)初始化(非零值);相反地哥纫,new()用於爲(wèi)值分配內(nèi)存霉旗,返回的是已經(jīng)初始化(非零值)的內(nèi)存指針。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蛀骇,一起剝皮案震驚了整個(gè)濱河市厌秒,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌擅憔,老刑警劉巖鸵闪,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異暑诸,居然都是意外死亡蚌讼,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門(mén)个榕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)篡石,“玉大人,你說(shuō)我怎么就攤上這事西采∠闹荆” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵苛让,是天一觀的道長(zhǎng)沟蔑。 經(jīng)常有香客問(wèn)我,道長(zhǎng)狱杰,這世上最難降的妖魔是什么瘦材? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮仿畸,結(jié)果婚禮上食棕,老公的妹妹穿的比我還像新娘。我一直安慰自己错沽,他們只是感情好簿晓,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著千埃,像睡著了一般憔儿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上放可,一...
    開(kāi)封第一講書(shū)人閱讀 49,741評(píng)論 1 289
  • 那天谒臼,我揣著相機(jī)與錄音朝刊,去河邊找鬼。 笑死蜈缤,一個(gè)胖子當(dāng)著我的面吹牛拾氓,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播底哥,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼咙鞍,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了趾徽?” 一聲冷哼從身側(cè)響起奶陈,我...
    開(kāi)封第一講書(shū)人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎附较,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體潦俺,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拒课,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了事示。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片早像。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖肖爵,靈堂內(nèi)的尸體忽然破棺而出卢鹦,到底是詐尸還是另有隱情,我是刑警寧澤劝堪,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布冀自,位于F島的核電站,受9級(jí)特大地震影響秒啦,放射性物質(zhì)發(fā)生泄漏熬粗。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一余境、第九天 我趴在偏房一處隱蔽的房頂上張望驻呐。 院中可真熱鬧,春花似錦芳来、人聲如沸含末。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)佣盒。三九已至,卻和暖如春顽聂,著一層夾襖步出監(jiān)牢的瞬間沼撕,已是汗流浹背宋雏。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留务豺,地道東北人磨总。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓凳鬓,卻偏偏與公主長(zhǎng)得像疏叨,于是被迫代替她去往敵國(guó)和親侈百。 傳聞我的和親對(duì)象是個(gè)殘疾皇子耳奕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 也許你還沒(méi)有理解構(gòu)造函數(shù)和原型對(duì)象的時(shí)候已經(jīng)在javascript的路上走了很久辛掠,但直到你很好的掌握它們之前你不會(huì)...
    WanLum閱讀 404評(píng)論 0 1
  • 學(xué)習(xí)如何創(chuàng)建對(duì)象時(shí)理解面向?qū)ο蟮牡谝徊角橐佟5诙繒r(shí)理解繼承敌厘。在傳統(tǒng)面向?qū)ο蟮恼Z(yǔ)言中糠亩,類(lèi)從其他類(lèi)繼承屬性汹桦。然而在jav...
    WanLum閱讀 261評(píng)論 0 0
  • 前言 什么是CSS預(yù)處理器 定義:CSS預(yù)處理器定義了一種新的語(yǔ)言鲁驶,其基本思想是,用一種專(zhuān)門(mén)的編程語(yǔ)言舞骆,為CSS增...
    SA_Arthur閱讀 3,116評(píng)論 0 18
  • 為何叫做 shell 钥弯? shell prompt(PS1) 與 Carriage Return(CR) 的關(guān)系?...
    Zero___閱讀 3,141評(píng)論 3 49
  • D3 行程 Day 3: London Pass 倫敦國(guó)會(huì)大廈和大本鐘 - 威斯敏斯特修道院 - 泰晤士河觀光游船...
    肖慶in上海閱讀 633評(píng)論 0 2