01 . Go框架之Beego簡介部署及程序流程分析


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請求流程

通過文字來描述如下:

  1. 在監聽的端口接收數據,默認監聽在 8080 端口。
  2. 用戶請求到達 8080 端口之后進入 beego 的處理邏輯。
  3. 初始化 Context 對象,根據請求判斷是否為 WebSocket 請求,如果是的話設置 Input,同時判斷請求的方法是否在標准請求方法中(GET、POST、PUT、DELETE、PATCH、OPTIONS、HEAD),防止用戶的惡意偽造請求攻擊造成不必要的影響。
  4. 執行 BeforeRouter 過濾器,當然在 beego 里面有開關設置。如果用戶設置了過濾器,那么該開關打開,這樣可以提高在沒有開啟過濾器的情況下提高執行效率。如果在執行過濾器過程中,responseWriter 已經有數據輸出了,那么就提前結束該請求,直接跳轉到監控判斷。
  5. 開始執行靜態文件的處理,查看用戶的請求 URL 是否和注冊在靜態文件處理 StaticDir 中的 prefix 是否匹配。如果匹配的話,采用 http 包中默認的 ServeFile 來處理靜態文件。
  6. 如果不是靜態文件開始初始化 session 模塊(如果開啟 session 的話),這個里面大家需要注意,如果你的 BeforeRouter 過濾器用到了 session 就會報錯,你應該把它加入到 AfterStatic 過濾器中。
  7. 開始執行 AfterStatic 過濾器,如果在執行過濾器過程中,responseWriter 已經有數據輸出了,那么就提前結束該請求,直接跳轉到監控判斷。
  8. 執行過過濾器之后,開始從固定的路由規則中查找和請求 URL 相匹配的對象。這個匹配是全匹配規則,即如果用戶請求的 URL 是 /hello/world,那么固定規則中 /hello 是不會匹配的,只有完全匹配才算匹配。如果匹配的話就進入邏輯執行,如果不匹配進入下一環節的正則匹配。
  9. 正則匹配是進行正則的全匹配,這個正則是按照用戶添加 beego 路由順序來進行匹配的,也就是說,如果你在添加路由的時候你的順序影響你的匹配。和固定匹配一樣,如果匹配的話就進行邏輯執行,如果不匹配進入 Auto 匹配。
  10. 如果用戶注冊了 AutoRouter,那么會通過 controller/method 這樣的方式去查找對應的 Controller 和他內置的方法,如果找到就開始執行邏輯,如果找不到就跳轉到監控判斷。
  11. 如果找到 Controller 的話,那么就開始執行邏輯,首先執行 BeforeExec 過濾器,如果在執行過濾器過程中,responseWriter 已經有數據輸出了,那么就提前結束該請求,直接跳轉到監控判斷。
  12. Controller 開始執行 Init 函數,初始化基本的一些信息,這個函數一般都是 beego.Controller 的初始化,不建議用戶繼承的時候修改該函數。
  13. 是否開啟了 XSRF,開啟的話就調用 Controller 的 XsrfToken,然后如果是 POST 請求就調用 CheckXsrfCookie 方法。
  14. 繼續執行 Controller 的 Prepare 函數,這個函數一般是預留給用戶的,用來做 Controller 里面的一些參數初始化之類的工作。如果在初始化中 responseWriter 有輸出,那么就直接進入 Finish 函數邏輯。
  15. 如果沒有輸出的話,那么根據用戶注冊的方法執行相應的邏輯,如果用戶沒有注冊,那么就調用 http.Method 對應的方法(Get/Post 等)。執行相應的邏輯,例如數據讀取,數據賦值,模板顯示之類的,或者直接輸出 JSON 或者 XML。
  16. 如果 responseWriter 沒有輸出,那么就調用 Render 函數進行模板輸出。
  17. 執行 Controller 的 Finish 函數,這個函數是預留給用戶用來重寫的,用於釋放一些資源。釋放在 Init 中初始化的信息數據。
  18. 執行 AfterExec 過濾器,如果有輸出的話就跳轉到監控判斷邏輯。
  19. 執行 Controller 的 Destructor,用於釋放 Init 中初始化的一些數據。
  20. 如果這一路執行下來都沒有找到路由,那么會調用 404 顯示找不到該頁面。
  21. 最后所有的邏輯都匯聚到了監控判斷,如果用戶開啟了監控模塊(默認是開啟一個 8088 端口用於進程內監控),這樣就會把訪問的請求鏈接扔給監控程序去記錄當前訪問的 QPS,對應的鏈接訪問的執行時間,請求鏈接等。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM