到目前爲止,咱們前面已經介紹瞭如何開發程序、調試程序以及測試程序,正如人們常說的:開發最後的10%須要花費90%的時間,因此這咱們將強調這最後的10%部分,要真正成爲讓人信任並使用的優秀應用,須要考慮到一些細節,以上所說的10%就是指這些小細節。html
咱們指望開發的Web應用程序可以把整個程序運行過程當中出現的各類事件一一記錄下來,Go語言中提供了一個簡易的log包,咱們使用該包能夠方便的實現日誌記錄的功能,這些日誌都是基於fmt包的打印再結合panic之類的函數來進行通常的打印、拋出錯誤處理。Go目前標準包只是包含了簡單的功能,若是咱們想把咱們的應用日誌保存到文件,而後又可以結合日誌實現不少複雜的功能(編寫過Java或者C++的讀者應該都使用過log4j和log4cpp之類的日誌工具),可使用第三方開發的日誌系統:logrus和seelog,它們實現了很強大的日誌功能,能夠結合本身項目選擇。mysql
logrus是用Go語言實現的一個日誌系統,與標準庫log徹底兼容而且核心API很穩定,是Go語言目前最活躍的日誌庫linux
seelog是用Go語言實現的一個日誌系統,它提供了一些簡單的函數來實現複雜的日誌分配、過濾和格式化。主要有以下特性:git
XML的動態配置,能夠不用從新編譯程序而動態的加載配置信息github
支持熱更新,可以動態改變配置而不須要重啓應用golang
支持多輸出流,可以同時把日誌輸出到多種流中、例如文件流、網絡流等web
支持不一樣的日誌輸出redis
對於應用日誌,每一個人的應用場景可能會各不相同,有些人利用應用日誌來作數據分析,有些人利用應用日誌來作性能分析,有些人來作用戶行爲分析,還有些就是純粹的記錄,以方便應用出現問題的時候輔助查找問題。sql
舉一個例子,咱們須要跟蹤用戶嘗試登錄系統的操做。這裏會把成功與不成功的嘗試都記錄下來。記錄成功的使用"Info"日誌級別,而不成功的使用"warn"級別。若是想查找全部不成功的登錄,咱們能夠利用linux的grep之類的命令工具,以下:shell
# cat /data/logs/roll.log | grep "failed login" 2012-12-11 11:12:00 WARN : failed login attempt from 11.22.33.44 username password
經過這種方式咱們就能夠很方便的查找相應的信息,這樣有利於咱們針對應用日誌作一些統計和分析。另外咱們還須要考慮日誌的大小,對於一個高流量的Web應用來講,日誌的增加是至關可怕的,因此咱們在seelog的配置文件裏面設置了logrotate,這樣就能保證日誌文件不會由於不斷變大而致使咱們的磁盤空間不夠引發問題。
經過上面對seelog系統及如何基於它進行自定義日誌系統的學習,如今咱們能夠很輕鬆的隨需構建一個合適的功能強大的日誌處理系統了。日誌處理系統爲數據分析提供了可靠的數據源,好比經過對日誌的分析,咱們能夠進一步優化系統,或者應用出現問題時方便查找定位問題,另外seelog也提供了日誌分級功能,經過對minlevel的配置,咱們能夠很方便的設置測試或發佈版本的輸出消息級別。
咱們的Web應用一旦上線以後,那麼各類錯誤出現的機率都有,Web應用平常運行中可能出現多種錯誤,具體以下所示:
數據庫錯誤:指與訪問數據庫服務器或數據相關的錯誤。例如,如下可能出現的一些數據庫錯誤。
應用運行時錯誤:這類錯誤範圍很廣,涵蓋了代碼中出現的幾乎全部錯誤。可能的應用錯誤的狀況以下:
HTTP錯誤:這些錯誤是根據用戶的請求出現的錯誤,最多見的就是404錯誤。雖然可能會出現不少不一樣的錯誤,但其中比較常見的錯誤還有401未受權錯誤(須要認證才能訪問的資源)、403禁止錯誤(不容許用戶訪問的資源)和503錯誤(程序內部出錯)。
操做系統出錯:這類錯誤都是因爲應用程序上的操做系統出現錯誤引發的,主要有操做系統的資源被分配完了,致使死機,還有操做系統的磁盤滿了,致使沒法寫入,這樣就會引發不少錯誤。
網絡出錯:指兩方面的錯誤,一方面是用戶請求應用程序的時候出現網絡斷開,這樣就致使鏈接中斷,這種錯誤不會形成應用程序的崩潰,可是會影響用戶訪問的效果;另外一方面是應用程序讀取其餘網絡上的數據,其餘網絡斷開會致使讀取失敗,這種須要對應用程序作有效的測試,可以避免這類問題出現的狀況下程序崩潰。
在實現錯誤處理以前,咱們必須明確錯誤處理想要達到的目標是什麼,錯誤處理系統應該完成如下工做:
咱們知道在不少其餘語言中有try...catch關鍵詞,用來捕獲異常狀況,可是其實不少錯誤都是能夠預期發生的,而不須要異常處理,應該當作錯誤來處理,這也是爲何Go語言採用了函數返回錯誤的設計,這些函數不會panic,例如若是一個文件找不到,os.Open返回一個錯誤,它不會panic;若是你向一箇中斷的網絡鏈接寫數據,net.Conn系列類型的Write函數返回一個錯誤,它們不會panic。這些狀態在這樣的程序裏都是能夠預期的。你知道這些操做可能會失敗,由於設計者已經用返回錯誤清楚地代表了這一點。這就是上面所講的能夠預期發生的錯誤。
可是還有一種狀況,有一些操做幾乎不可能失敗,並且在一些特定的狀況下也沒有辦法返回錯誤,也沒法繼續執行,這樣狀況就應該panic。舉個例子:若是一個程序計算x[j],可是j越界了,這部分代碼就會致使panic,像這樣的一個不可預期嚴重錯誤就會引發panic,在默認狀況下它會殺掉進程,它容許一個正在運行這部分代碼的goroutine從發生錯誤的panic中恢復運行,發生panic以後,這部分代碼後面的函數和代碼都不會繼續執行,這是Go特地這樣設計的,由於要區別於錯誤和異常,panic其實就是異常處理。
規則很簡單:若是你定義的函數有可能失敗,它就應該返回一個錯誤。當我調用其餘package的函數時,若是這個函數實現的很好,我不須要擔憂它會panic,除非有真正的異常狀況發生,即便那樣也不該該是我去處理它。而panic和recover是針對本身開發package裏面實現的邏輯,針對一些特殊狀況來設計。
程序開發完畢以後,咱們如今要部署Web應用程序了,可是咱們如何來部署這些應用程序呢?由於Go程序編譯以後是一個可執行文件,編寫過C程序的讀者必定知道採用daemon就能夠完美的實現程序後臺持續運行,可是目前Go還沒法完美的實現daemon,所以,針對Go的應用程序部署,咱們能夠利用第三方工具來管理,第三方的工具備不少,例如Supervisord、upstart、daemontools等,
目前Go程序還不能實現daemon,詳細的見這個Go語言的bug:http://code.google.com/p/go/issues/detail?id=227,大概的意思說很難從現有的使用的線程中fork一個出來,由於沒有一種簡單的方法來確保全部已經使用的線程的狀態一致性問題。
在大多數集羣環境下,Web應用程序基本不須要備份,由於這個其實就是一個代碼副本,咱們在本地開發環境中,或者版本控制系統中已經保持這些代碼。可是不少時候,一些開發的站點須要用戶來上傳文件,那麼咱們須要對這些用戶上傳的文件進行備份。目前其實有一種合適的作法就是把和網站相關的須要存儲的文件存儲到雲儲存,這樣即便系統崩潰,只要咱們的文件還在雲存儲上,至少數據不會丟失。
若是咱們沒有采用雲儲存的狀況下,如何作到網站的備份呢?這裏咱們介紹一個文件同步工具rsync:rsync可以實現網站的備份,不一樣系統的文件的同步,若是是windows的話,須要windows版本cwrsync。
應用數據庫目前仍是MySQL爲主流,目前MySQL的備份有兩種方式:熱備份和冷備份,熱備份目前主要是採用master/slave方式(master/slave方式的同步目前主要用於數據庫讀寫分離,也能夠用於熱備份數據),關於如何配置這方面的資料,你們能夠找到不少。冷備份的話就是數據有必定的延遲,可是能夠保證該時間段以前的數據完整,例若有些時候可能咱們的誤操做引發了數據的丟失,那麼master/slave模式是沒法找回丟失數據的,可是經過冷備份能夠部分恢復數據。
冷備份通常使用shell腳原本實現定時備份數據庫,而後經過上面的rsync同步非本地機房的一臺服務器。
前面介紹MySQL備份分爲熱備份和冷備份,熱備份主要的目的是爲了可以實時的恢復,例如應用服務器出現了硬盤故障,那麼咱們能夠經過修改配置文件把數據庫的讀取和寫入改爲slave,這樣就能夠儘可能少時間的中斷服務。
可是有時候咱們須要經過冷備份的SQL來進行數據恢復,既然有了數據庫的備份,就能夠經過命令導入:
mysql -u username -p databse < backup.sql
能夠看到,導出和導入數據庫數據都是至關簡單,不過若是還須要管理權限,或者其餘的一些字符集的設置的話,可能會稍微複雜一些,可是這些都是能夠經過一些命令來完成的。
redis是目前咱們使用最多的NoSQL,它的備份也分爲兩種:熱備份和冷備份,redis也支持master/slave模式,因此咱們的熱備份能夠經過這種方式實現,相應的配置你們能夠參考官方的文檔配置,至關的簡單。咱們這裏介紹冷備份的方式:redis其實會定時的把內存裏面的緩存數據保存到數據庫文件裏面,咱們備份只要備份相應的文件就能夠,就是利用前面介紹的rsync備份到非本地機房就能夠實現。
redis的恢復分爲熱備份恢復和冷備份恢復,熱備份恢復的目的和方法同MySQL的恢復同樣,只要修改應用的相應的數據庫鏈接便可。
可是有時候咱們須要根據冷備份來恢復數據,redis的冷備份恢復其實就是隻要把保存的數據庫文件copy到redis的工做目錄,而後啓動redis就能夠了,redis在啓動的時候會自動加載數據庫文件到內存中,啓動的速度根據數據庫的文件大小來決定。
看到這我明白了我對數據庫瞭解的仍是太少o(╥﹏╥)o
最後的小結
本章討論瞭如何部署和維護咱們開發的Web應用相關的一些話題。這些內容很是重要,要建立一個可以基於最小維護平滑運行的應用,必須考慮這些問題。
具體而言,本章討論的內容包括:
好刺激呀這一塊