Beego簡介
beego是一個使用Go語言來開發WEB引用的GoWeb框架,該框架起始於2012年,由一位中國的程序員編寫並進行公開,其目的就是為大家提供一個高效率的web應用開發框架。該框架采用模塊封裝,使用簡單,容易學習。方便技術開發者快速學習並進行實際開發。對程序員來說,beego掌握起來非常簡單,只需要關注業務邏輯實現即可,框架自動為項目需求提供不同的模塊功能。
在對beego框架有了一個簡單基本的了解之后,我們給大家介紹一下beego的一些特性。
Beego框架的主要特性
1)簡單化:RESTful支持,MVC模型;可以使用bee工具來提高開發效率,比如監控代碼修改進行熱編譯,自動化測試代碼,以及自動化打包部署等豐富的開發調試功能。
2)智能化:beego框架封裝了路由模塊,支持智能路由,智能監控,並可以監控內存消耗,CPU使用以及goroutine的運行狀況,方便開發者對線上應用進行監控分析。
3)模塊化:beego根據功能對代碼進行節耦封裝,形成了Session,Cache,Log,配置解析,性能監控,上下文操作,ORM等獨立的模塊,方便開發者進行使用。
4)高性能:beego采用Go原生的http請求,goroutine的並發效率應付大流量的Web應用和API應用。
beego的幾個特性
一方面在面試過程中,如果有面試官問起大家關於goweb開發的相關知識的時候,有可能問beego框架有優勢或者有什么特點,這個時候需要大家能夠對beego框架這些特點做一個介紹;
另一方面,咱們在后面實際的學習和編碼過程中,大家感受一下這些特性在實際項目中為我們帶來的便利和高效。
beego安裝
我們使用go get命令來進行beego的安裝。
注意
在安裝的時候,有一點大家要注意,beego框架要求Go 語言版本1.1+以上,這個我們大家的版本應該至少也是1.9的吧,應該都沒有。或者大家現在在命令行中,執行go version查看一下自己的go語言版本,如下所示:
添加環境變量
cat .bash_profile
export GOROOT=/usr/local/go
export GOARCH=amd64
export GOOS=darwin
export GOPATH=/Users/youmen/Go_wunai
export GOBIN=$GOPATH/bin
export PATH=$PATH:$GOPATH/bin
安裝beego
export GO111MODULE=on
export GOPROXY=https://goproxy.cn
ulimit -n 5000
go get -u github.com/astaxie/beego
go get -u github.com/beego/bee
bee new beego
cd ${GOPATH}/src
// cd進入項目目錄
// 輸入go mod init
bee run
// 如果想編譯成linux可以直接執行的二進制文件
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build hello.go
命令行工具Bee
現在,嘗試來進行對代碼進行修改,就簡單的添加一行打印日志:beego.Info("代碼修改")。這個時候,如果我們要想讓代碼生效,必須把原來正在執行的程序停止,然后重新執行編碼和運行命令,我們來實際嘗試一下。
經過我們的嘗試,我們可以發現,確實第二次的重新啟動的程序打印出了兩條后台日志,這說明我們修改的代碼生效了。
bee簡介
bee是一個開發工具,是協助Beego框架開發項目時進行創建項目,運行項目,熱部署等相關的項目管理的工具。beego是源碼,負責開發,bee是工具,負責構建和管理項目。介紹beego的特性的時候說過其中一點beego支持代碼熱部署吧。所謂熱部署就是,當修改代碼的時候,可以不用停止服務重新啟動,內置的beego就能夠實時感知源代碼程序編碼,並進行實時生效,這就是熱部署,這樣就大大方便了開發人員和維護人員要頻繁的進行服務停止,服務重啟的過程。
Bee安裝
如同beego一樣,如果要使用bee,首先要進行安裝,同樣的道理,先使用go get命令安裝bee工具。bee安裝命令:
go get github.com/beego/bee
Bee功能命令
安裝結束以后,進入到GOPATH對應的目錄中,然后進入到bin目錄下,可以看到有bee這個可執行文件,這就是安裝的bee工具的可執行文件,在該目錄中,我們在終端中執行以下bee命令,然后回車,會發現列出很多關於bee的用法,如下圖:
new命令
// bee new ProjectName
該命令表示新建一個全新的web項目,有一點需要注意:該命令必須在src目錄下執行,才能生效,自動生成web項目的目錄結構。如果在其他目錄下面執行bee new命令,也同樣會是在src目錄下面生成對應新項目的目錄結構,這是bee工具在構建項目的時候默認尋找創建的目錄。
api命令
// bee api ProjectNames
該命令表示用來創建開發API應用。很多用戶寫前端的后台接口也是通過go來寫,因此bee專門提供了一個寫api接口應用的命令。通過目錄結構可以看到,和Web項目相比,API項目少了static和views目錄,多了一個test目錄,test是用來進行寫測試用例代碼的。
run命令
// bee run
命令用來運行項目,並且能夠通過監控文件系統,實時進行代碼的熱部署更新。也就是,我們代碼邏輯進行了修改,不用停止應用,在前端頁面就能看到改變。
pack命令
// bee pack
pack命令用來發布應用的時候的打包操作,該命令會把項目大包成zip包,然后我們就可以在部署的時候直接把大包后的項目上傳到服務器,然后解壓進行部署。
version命令
// bee version
Version命令來查看當前bee,beego,go的版本。
使用Bee工具
常見的幾個bee工具命令就是上面的幾個,立即來練習使用一下。打開命令行終端,進入到GOPATH所對應的目錄,然后進入到src目錄中。
創建項目
使用bee run命令來新建一個案例項目,比如我們新建一個BeegoDemo2的項目,我們執行命令:bee new BeegoDemo2,命令執行效果如下:
bee new test
______
| ___ \
| |_/ / ___ ___
| ___ \ / _ \ / _ \
| |_/ /| __/| __/
\____/ \___| \___| v1.11.0
2020/07/25 16:35:17 WARN ▶ 0001 You current workdir is not inside $GOPATH/src.
2020/07/25 16:35:17 INFO ▶ 0002 Creating application...
create /Users/youmen/Go_wunai/src/test/
create /Users/youmen/Go_wunai/src/test/conf/
create /Users/youmen/Go_wunai/src/test/controllers/
create /Users/youmen/Go_wunai/src/test/models/
create /Users/youmen/Go_wunai/src/test/routers/
create /Users/youmen/Go_wunai/src/test/tests/
create /Users/youmen/Go_wunai/src/test/static/
create /Users/youmen/Go_wunai/src/test/static/js/
create /Users/youmen/Go_wunai/src/test/static/css/
create /Users/youmen/Go_wunai/src/test/static/img/
create /Users/youmen/Go_wunai/src/test/views/
create /Users/youmen/Go_wunai/src/test/conf/app.conf
create /Users/youmen/Go_wunai/src/test/controllers/default.go
create /Users/youmen/Go_wunai/src/test/views/index.tpl
create /Users/youmen/Go_wunai/src/test/routers/router.go
create /Users/youmen/Go_wunai/src/test/tests/default_test.go
create /Users/youmen/Go_wunai/src/test/main.go
2020/07/25 16:35:17 SUCCESS ▶ 0003 New application successfully created!
項目結構
cd ${GOPATH}/src
tree test
go mod init
test
├── conf
│ └── app.conf
├── controllers
│ └── default.go
├── main.go
├── models
├── routers
│ └── router.go
├── static
│ ├── css
│ ├── img
│ └── js
│ └── reload.min.js
├── tests
│ └── default_test.go
└── views
└── index.tpl
運行項目
bee run
______
| ___ \
| |_/ / ___ ___
| ___ \ / _ \ / _ \
| |_/ /| __/| __/
\____/ \___| \___| v1.11.0
2020/07/25 16:37:40 INFO ▶ 0001 Using 'test' as 'appname'
2020/07/25 16:37:40 INFO ▶ 0002 Initializing watcher...
go: finding module for package github.com/astaxie/beego
go: found github.com/astaxie/beego in github.com/astaxie/beego v1.12.2
test/controllers
test/routers
2020/07/25 16:37:43 SUCCESS ▶ 0003 Built Successfully!
2020/07/25 16:37:43 INFO ▶ 0004 Restarting 'test'...
2020/07/25 16:37:43 SUCCESS ▶ 0005 './test' is running...
2020/07/25 16:37:44.611 [I] [asm_amd64.s:1373] http server Running on http://:8080
手動創建項目
創建main.go
package main
import (
"fmt"
// 導入beego包
"github.com/astaxie/beego"
"github.com/astaxie/beego/context"
)
func main() {
// 路由 url => controller
// 處理器函數 處理器 函數
// 函數, 結構體
// beego/context/Context
// 綁定函數
// 以GET方式請求/通過綁定函數處理
// 固定路由
beego.Get("/",func(ctx *context.Context){
// 用戶數據的獲取
name := ctx.Input.Query("name")
// 給用戶響應數據
ctx.Output.Context.WriteString(fmt.Sprintf("您輸入的名字是: %s",name))
})
// Post
beego.Post("/", func(ctx *context.Context) {
name := ctx.Input.Query("name")
ctx.Output.Context.WriteString(fmt.Sprintf("(Post)您輸入的名字是: %s",name))
})
// 任意請求
beego.Any("/any", func(ctx *context.Context) {
name := ctx.Input.Query("name")
ctx.Output.Context.WriteString(fmt.Sprintf("(%s)您輸入的名字是: %s",ctx.Input.Method(),name))
})
// 啟動Beego程序
beego.Run("127.0.0.1")
}
youmen@youmendeMacBook-Pro elk % curl http://127.0.0.1:8080/
您輸入的名字是: %
// 可以使用Postman
執行流程分析
Go語言執行的時候是執行main包下面的init函數,main函數依次執行。因此,先找到main.go文件。
app.conf
appname = cmdb
httpaddr = "127.0.0.1"
httpport = 8080
runmode = dev
main.go
package main
import (
_ "beego_demo1/routers"
"github.com/astaxie/beego"
)
func main() {
beego.Info("Beego_Demo1")
beego.Run("127.0.0.1:8080")
}
上就是main.go文件內容,那么代碼如何執行呢?首先,import導入了兩個包,一個是routers,一個是beego。而在routers包前面,可以看到有一個“_”,這表明是引入routers包,並執行init方法。這里涉及一個知識點,就是Go語言的執行過程,這里給大家一張程序執行流程圖
根據上圖,可以得知程序首先到routers包下執行init方法。到router.go里面看一下具體的代碼:
可以看到在router.go文件中有一個init方法,看到beego.Router()這句代碼。router表示的是路由的意思,這個函數的功能是映射 URL 到 controller,第一個參數是 URL (用戶請求的地址),這里注冊的是 /,也就是訪問的不帶任何參數的 URL,第二個參數是對應的 Controller,即將把請求分發到那個控制器來執行相應的邏輯,現在去這里設置的MainController中去看一下有什么方法:
package controllers
import (
"github.com/astaxie/beego"
)
type MainController struct {
beego.Controller
}
func (c *MainController) Get() {
c.Data["Website"] = "youmen.me"
c.Data["Email"] = "163@.com"
c.TplName = "index.tpl"
}
MainController結構體及函數聲明在default.go文件中。而這里就看到一個Get方法,方法中有三行代碼。
上文在瀏覽器中訪問的是:http://localhost:8080,這是一個get請求,請求到了后台以后,什么請求參數都沒有,因此,就會被“/”攔截,執行到MainController中的代碼,因為是get請求,所以這里自動找到Get函數並進行執行。
在get函數里面,有三句代碼,前兩句c.Data[]= ""表示設置返回的數據字段及內容,最后一句c.TplName表示設置處理該請求指向某個模板文件,這里指向了index.tpl,那么index.tpl文件在哪里呢?可以查看項目目錄結構,在views下面,views下面存放一些模板文件。
模板文件
簡單解釋一下,通常的頁面都是使用靜態的html+css+js等這些靜態代碼來進行頁面的布局,頁面效果控制等,而把頁面的數據使用變量表示,這樣,在進行頁面展示的時候,就能夠自動的填充頁面里面的變量的值;這些靜態的代碼文件統稱為模板文件。每個模板文件就是像一個模板一樣,樣式效果都固定,只是根據數據不一樣進行渲染和展示。
服務器處理客戶端過程
// 客戶端請求: http協議 => url,method,params
// 服務器處理客戶端過程: url => handler => params => db => templates => html
// 定義處理器
// params
// 處理器調用數據庫對數據進行處理(增/刪/改/查)
// 處理器調用模板基礎去渲染頁面
// 定義url處理器管理
// 客戶端渲染: http渲染頁面
Beego.Run()邏輯
init方法分析完畢后,程序會繼續往下執行,就到了main函數,在main函數中執行:beego.Run()代碼。分析一下Run代碼的邏輯,在Run方法內部,主要做了幾件事:
// 1)解析配置文件,也就是我們的app.conf文件,比如端口,應用名稱等信息。
// 2)檢查是否開啟session,如果開啟session,就會初始化一個session對象。
// 3)是否編譯模板,beego框架會在項目啟動的時候根據配置把views目錄下的所有模板進行預編譯,
// 然后存放在map中,這樣可以有效的提高模板運行的效率,不需要進行多次編譯。
// 4)監聽服務端口。根據app.conf文件中的端口配置,啟動監聽。
官網Beego請求流程
通過文字來描述如下:
- 在監聽的端口接收數據,默認監聽在 8080 端口。
- 用戶請求到達 8080 端口之后進入 beego 的處理邏輯。
- 初始化 Context 對象,根據請求判斷是否為 WebSocket 請求,如果是的話設置 Input,同時判斷請求的方法是否在標准請求方法中(GET、POST、PUT、DELETE、PATCH、OPTIONS、HEAD),防止用戶的惡意偽造請求攻擊造成不必要的影響。
- 執行 BeforeRouter 過濾器,當然在 beego 里面有開關設置。如果用戶設置了過濾器,那么該開關打開,這樣可以提高在沒有開啟過濾器的情況下提高執行效率。如果在執行過濾器過程中,responseWriter 已經有數據輸出了,那么就提前結束該請求,直接跳轉到監控判斷。
- 開始執行靜態文件的處理,查看用戶的請求 URL 是否和注冊在靜態文件處理 StaticDir 中的 prefix 是否匹配。如果匹配的話,采用
http
包中默認的 ServeFile 來處理靜態文件。 - 如果不是靜態文件開始初始化 session 模塊(如果開啟 session 的話),這個里面大家需要注意,如果你的 BeforeRouter 過濾器用到了 session 就會報錯,你應該把它加入到 AfterStatic 過濾器中。
- 開始執行 AfterStatic 過濾器,如果在執行過濾器過程中,responseWriter 已經有數據輸出了,那么就提前結束該請求,直接跳轉到監控判斷。
- 執行過過濾器之后,開始從固定的路由規則中查找和請求 URL 相匹配的對象。這個匹配是全匹配規則,即如果用戶請求的 URL 是
/hello/world
,那么固定規則中/hello
是不會匹配的,只有完全匹配才算匹配。如果匹配的話就進入邏輯執行,如果不匹配進入下一環節的正則匹配。 - 正則匹配是進行正則的全匹配,這個正則是按照用戶添加 beego 路由順序來進行匹配的,也就是說,如果你在添加路由的時候你的順序影響你的匹配。和固定匹配一樣,如果匹配的話就進行邏輯執行,如果不匹配進入 Auto 匹配。
- 如果用戶注冊了 AutoRouter,那么會通過
controller/method
這樣的方式去查找對應的 Controller 和他內置的方法,如果找到就開始執行邏輯,如果找不到就跳轉到監控判斷。 - 如果找到 Controller 的話,那么就開始執行邏輯,首先執行 BeforeExec 過濾器,如果在執行過濾器過程中,responseWriter 已經有數據輸出了,那么就提前結束該請求,直接跳轉到監控判斷。
- Controller 開始執行 Init 函數,初始化基本的一些信息,這個函數一般都是 beego.Controller 的初始化,不建議用戶繼承的時候修改該函數。
- 是否開啟了 XSRF,開啟的話就調用 Controller 的 XsrfToken,然后如果是 POST 請求就調用 CheckXsrfCookie 方法。
- 繼續執行 Controller 的 Prepare 函數,這個函數一般是預留給用戶的,用來做 Controller 里面的一些參數初始化之類的工作。如果在初始化中 responseWriter 有輸出,那么就直接進入 Finish 函數邏輯。
- 如果沒有輸出的話,那么根據用戶注冊的方法執行相應的邏輯,如果用戶沒有注冊,那么就調用 http.Method 對應的方法(Get/Post 等)。執行相應的邏輯,例如數據讀取,數據賦值,模板顯示之類的,或者直接輸出 JSON 或者 XML。
- 如果 responseWriter 沒有輸出,那么就調用 Render 函數進行模板輸出。
- 執行 Controller 的 Finish 函數,這個函數是預留給用戶用來重寫的,用於釋放一些資源。釋放在 Init 中初始化的信息數據。
- 執行 AfterExec 過濾器,如果有輸出的話就跳轉到監控判斷邏輯。
- 執行 Controller 的 Destructor,用於釋放 Init 中初始化的一些數據。
- 如果這一路執行下來都沒有找到路由,那么會調用 404 顯示找不到該頁面。
- 最后所有的邏輯都匯聚到了監控判斷,如果用戶開啟了監控模塊(默認是開啟一個 8088 端口用於進程內監控),這樣就會把訪問的請求鏈接扔給監控程序去記錄當前訪問的 QPS,對應的鏈接訪問的執行時間,請求鏈接等。