<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          在 5 分鐘之內(nèi)部署一個 Go 應(yīng)用

          共 6147字,需瀏覽 13分鐘

           ·

          2021-07-10 09:29

          點擊上方“Go編程時光”,選擇“加為星標(biāo)

          第一時間關(guān)注Go技術(shù)干貨!





          在有些程序人寫完了他們的 Go 應(yīng)用之后,這總會成為一個大問題——“我剛寫的這個 Go 應(yīng)用,當(dāng)它崩潰的時候我要怎么重啟?”,因為你沒法用 go run main.go 或者 ./main 這樣的命令讓它持續(xù)運行,并且當(dāng)程序崩潰的時候能夠重啟。

          一個普通使用的好辦法是使用 Docker。但是,設(shè)置 Docker 以及為容器配置你的應(yīng)用需要花費時間,當(dāng)你的程序需要和 MySQL、Redis 這樣的服務(wù)器/進程交互時更是如此。對于一個大型或長期項目來說,毋庸置疑這是一個正確的選擇。但是如果在你手上的是個小應(yīng)用,你想要快速部署并且實時地服務(wù)器上查看狀態(tài),那么你可能需要考慮別的選擇。

          另一個選擇就是在你的 Linux 服務(wù)器上創(chuàng)建一個守護進程,然后讓它作為一個服務(wù)運行,但是這需要花費一些額外的工夫。而且,如果你并不具備 Linux 系統(tǒng)和服務(wù)相關(guān)的知識的話,這就不是一件簡單的事情了。所以,這里有一個最簡單的解決方案——使用 Supervisor[1] 來部署你的 Go 應(yīng)用,然后它會為你處理好其余的工作。它是一個能夠幫你監(jiān)控你的應(yīng)用程序并在其崩潰時進行重啟的工具。

          本文是 Go語言中文網(wǎng)組織的 GCTT 翻譯,發(fā)布在 Go語言中文網(wǎng)公眾號,轉(zhuǎn)載請聯(lián)系我們授權(quán)。

          安裝

          安裝 Supervisor 相當(dāng)簡單,在 Ubuntu 上這條命令就會在你的系統(tǒng)上安裝 Supervisor。

          sudo apt install supervisor

          然后你需要將 Supervisor 添加到系統(tǒng)的用戶組中:

          sudo addgroup --system supervisor

          現(xiàn)在,在創(chuàng)建 Supervisor 的配置文件之前,我們先寫一個簡單的 Go 程序。這個程序?qū)x取 .env 文件中的配置項,然后和 MySQL 數(shù)據(jù)庫進行交互。代碼如下:

          (為了方便演示,我們會讓代碼簡單些)

          package main

          import (
           "database/sql"
           "encoding/json"
           "fmt"
           "log"
           "net/http"
           "os"

           _ "github.com/go-sql-driver/mysql"
           "github.com/gorilla/mux"
           "github.com/joho/godotenv"
          )

          type User struct {
           Email    string `json:"email"`
           Password string `json:"password"`
          }

          var db *sql.DB

          func init() {
           var err error
           err = godotenv.Load()
           if err != nil {
            log.Println("Error readin .env: ", err)
            os.Exit(1)
           }

           dbUserName := os.Getenv("DB_USERNAME")
           dbPassword := os.Getenv("DB_PASSWORD")
           dbNAME := os.Getenv("DB_NAME")

           dsn := dbUserName + ":" + dbPassword + "@/" + dbNAME

           db, err = sql.Open("mysql", dsn)

           if err != nil {
            log.Println(err)
            os.Exit(1)
           }

          }

          func main() {

           r := mux.NewRouter()

           r.Use(middleware)

           r.HandleFunc("/", rootHandler)
           r.HandleFunc("/user", createUserHandler).Methods("POST")

           fmt.Println("Listening on :8070")
           if err := http.ListenAndServe(":8070", r); err != nil {
            // 退出程序
            log.Println("Failed starting server ", err)
            os.Exit(1)
           }

          }

          func rootHandler(w http.ResponseWriter, r *http.Request) {
           fmt.Fprintf(w, "This is root handler")
          }

          func createUserHandler(w http.ResponseWriter, r *http.Request) {

           user := &User{}

           err := json.NewDecoder(r.Body).Decode(user)

              // 對請求響應(yīng) JSON 數(shù)據(jù)
              // 在實際應(yīng)用中你可能想要創(chuàng)建一個進行錯誤處理的函數(shù)
           if err != nil {
            // 我們也可以這么做
            // errREsp := `"error": "Invalid input", "status": 400`
            // w.Header().Set("Content-Type", "application/json")
            // w.WriteHeader(400)
                  // w.Write([]byte(errREsp))

                  // 然而我們會讓服務(wù)器崩潰
            log.Fatal(err)

            return
           }

              // 在實際應(yīng)用中必須對密碼進行哈希,可以使用 bcrypt 算法
           _, err = db.Exec("INSERT INTO users(email, password) VALUES(?,?)", user.Email, user.Password)

           if err != nil {
            log.Println(err)
                  // 簡單起見,發(fā)送明文字符串
                  // 創(chuàng)建一個有效的 JSON 響應(yīng)
            errREsp := `"error": "Internal error", "status": 500` // 返回 500 狀態(tài)碼,因為這是我們而非用戶的問題
            w.Header().Set("Content-Type""application/json")
            w.WriteHeader(500)
            w.Write([]byte(errREsp))

            return
           }

          }

          // 一個簡單的中間件,只用來記錄請求的 URI
          var middleware = func(next http.Handler) http.Handler {

           return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

            requestPath := r.URL.Path
            log.Println(requestPath)
            next.ServeHTTP(w, r) // 在中間件調(diào)用鏈中進行處理!

           })
          }

          現(xiàn)在,如果我們想要用 Supervisor 來運行這個程序,我們需要構(gòu)建程序的二進制文件。同時在項目的根目錄下創(chuàng)建一個 .env 文件 —— 如果你想把配置文件和項目放在一起的話,在這個文件中寫上 MySQL 數(shù)據(jù)庫需要的變量。

          將這個倉庫克隆到你想要運行的服務(wù)器上。確保你遵循了 Go 目錄路徑的慣例:

          $ Go build .

          Go 的這個命令最終會創(chuàng)建一個以項目根目錄命名的二進制文件,所以如果項目的根目錄是 myapp,那么文件的名稱就是 myapp

          現(xiàn)在,在服務(wù)器上創(chuàng)建 Supervisor 的配置文件 /etc/supervisor/conf.d

          #/etc/supervisor/conf.d/myapp.conf

          [program:myapp]
          directory=/root/gocode/src/github.com/monirz/myapp
          command=/root/gocode/src/github.com/monirz/myapp/myapp
          autostart=true
          autorestart=true
          stderr_logfile=/var/log/myapp.err
          stdout_logfile=/var/log/myapp.log
          environment=CODENATION_ENV=prod
          environment=GOPATH="/root/gocode"

          這里的 directory 和 command 變量很重要。directory 變量應(yīng)該設(shè)置為項目的根目錄,因為程序?qū)L試在 directory 指定的路徑下讀取 .env 文件或是其他需要的配置文件。autorestart 變量設(shè)置為 true,這樣當(dāng)程序崩潰時就會重啟。

          現(xiàn)在通過下面的命令重新加載 Supervisor:

          $ sudo supervisorctl reload

          來檢查下它的狀態(tài)。

          $ sudo supervisorctl status

          一切都正確配置的話,你應(yīng)該會看到類似下面的輸出內(nèi)容:

          myapp     RUNNING   pid 2023, uptime 0:00:03

          我們名為 myapp 的 Go 服務(wù)端程序正在后臺運行。

          現(xiàn)在向我們剛寫的 API 發(fā)起一些請求。首先檢查 rootHandler 是否正在工作。然后向 /user 結(jié)點發(fā)送一個包含無效 JSON 格式數(shù)據(jù)的請求。這應(yīng)當(dāng)會讓服務(wù)器崩潰。但是服務(wù)器上沒有存儲任何日志,不是嗎?因為我們還沒有實現(xiàn)日志功能?

          等等,Supervisor 實際上已經(jīng)為我們處理了日志。如果你到 /var/log 目錄下查看 myapp.log 文件,你就會看到它記錄著已經(jīng)向服務(wù)器發(fā)起過的請求的 URI 路徑。

          $ cat /var/log/myapp.log

          錯誤日志也是如此。好了,我們的服務(wù)器程序已經(jīng)運行了——崩潰的話會重啟,還會記錄每個請求和錯誤信息。我覺得我們應(yīng)該是在大約 5 分鐘以內(nèi)做完了這些事吧?(大概是吧,誰在乎呢。)但關(guān)鍵是,用 Supervisor 來部署和監(jiān)控你的 Go 應(yīng)用程序時十分簡單的。

          你覺得呢?毫不猶豫地回復(fù)我吧。周末愉快。


          via: https://medium.com/@monirz/deploy-golang-app-in-5-minutes-ff354954fa8e

          作者:Monir Zaman[2]譯者:maxwellhertz[3]校對:polaris1119[4]

          本文由 GCTT[5] 原創(chuàng)編譯,Go 中文網(wǎng)[6] 榮譽推出,發(fā)布在 Go語言中文網(wǎng)公眾號,轉(zhuǎn)載請前往聯(lián)系授權(quán)。

             


          喜歡明哥文章的同學(xué)
          歡迎長按下圖訂閱!

          ???

          瀏覽 56
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲欧美操逼网 | 日本xxxx视频 | 91无码精品国产 | 国产精品成人娱乐网 | 久久夜色精品国产免费观看 |