golang web 方案

来源:https://www.cnblogs.com/wang_yb/archive/2019/05/14/10859900.html
-Advertisement-
Play Games

"概要" "開發" "web 框架" "資料庫" "認證" "日誌" "配置" "靜態文件服務" "上傳/下載" "發佈" "docker 打包" "部署中遇到的問題" "時區問題" 概要 輕量的基於 golang 的 web 開發實踐. golang 上手簡單, 第三方庫豐富, 對於業務沒那麼複雜 ...


概要

輕量的基於 golang 的 web 開發實踐.

golang 上手簡單, 第三方庫豐富, 對於業務沒那麼複雜的項目, 作為 API 的後端也是不錯的選擇. 下麵是對 golang 作為 API 後端的 web 開發實踐總結.

開發

API 後端的功能模塊基本已經固定, 基於自己的項目, 主要使用了以下模塊:

  1. web 框架: 整個方案的核心
  2. 資料庫: orm 框架
  3. 認證: 訪問的安全
  4. 日誌: 輔助調試和運維
  5. 配置: 提高服務的靈活性
  6. 靜態文件服務: 部署打包後的前端
  7. 上傳/下載: 其實也是 web 框架提供的功能, 單獨提出來是因為和一般的 JSON API 不太一樣

web 框架

golang 的 API 框架有很多, 我在項目中選擇了 gin 框架. 當時是出於以下幾點考慮:

  1. 成熟度: gin 早就進入 v1 穩定版, 使用的項目也很多, 成熟度沒有問題
  2. 性能: gin 的性能在眾多 golang web 框架中不是最好的, 但也不差, 具體可以參見 gin 的 README
  3. 活躍度: github 上的 commit 可以看出, gin 雖然很穩定, 更新頻率還可以
  4. 周邊支持: gin 的插件非常多, 還有個 contrib 項目, 常用的各種插件基本都有, 另外, gin 的插件寫起來也很簡單

雖然選擇了 gin, 但是本文中使用的各個模塊都不是強依賴 gin 的, 替換任何一個模塊的代價都不會太大.

gin 的使用很簡單, 主要代碼如下:

r := gin.Default()
if gin.Mode() == "debug" {
  r.Use(cors.Default())  // 在 debug 模式下, 允許跨域訪問
}

// ... 設置路由的代碼

if err := r.Run(":" + strconv.Itoa(port)); err != nil {
  log.Fatal(err)
}

資料庫

資料庫這層, 選用了 beego ORM 框架, 它的文檔比較好, 對主流的幾種關係資料庫也都支持. 表結構的定義:

type User struct {
  Id       string     `orm:"pk" json:"id"`
  UserName string     `orm:"unique" json:"username"`
  Password string     `json:"password"`

  CreateAt time.Time `orm:"auto_now_add"`
  UpdateAt time.Time `orm:"auto_now"`
}

func init() {
  orm.RegisterModel(new(User))
}

資料庫的初始化:

// mysql 配置, postgresql 或者 sqlite 使用其他驅動
orm.RegisterDriver("default", orm.DRMySQL) // 註冊驅動
var conStr = fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8&loc=Local",
  c.DB.UserName, c.DB.Password, c.DB.Host, c.DB.Port, c.DB.DBName)
orm.RegisterDataBase("default", "mysql", conStr)

// sync database
orm.RunSyncdb("default", false, false)

認證

認證採用 jwt token, 使用了 gin-jwt 中間件. 加了認證中間件之後, 可以配置路由是否需要認證:

authMiddleware := controller.JwtMiddleware()

// *不需要* 認證的路由
r.POST("/register", controller.Register)
r.POST("/login", authMiddleware.LoginHandler)

// *需要* 認證的路由
authRoute := r.Group("/auth")
authRoute.Use(authMiddleware.MiddlewareFunc())
{
  authRoute.GET("/test", func(c *gin.Context) { fmt.Println("hello") })
}

日誌

項目不是很複雜, 日誌採用了文件的方式, 選擇了 beego logs 模塊. 雖然使用了 beego logs, 但是為了方便以後替換 logs 模塊, 在 beego logs 又封裝了一層.

// Logger
type Logger interface {
  Debug(format string, v ...interface{})
  Info(format string, v ...interface{})
  Warn(format string, v ...interface{})
  Error(format string, v ...interface{})
}

// 支持 console 和 file 2 種類型的 log
func InitLogger(level, logType, logFilePath string) error {
  consoleLogger = nil
  fileLogger = nil

  if logType == ConsoleLog {
    consoleLogger = NewConsoleLogger(level)  // 這裡實際是通過 beego logs 來實現功能的
  } else if logType == FileLog {
    fileLogger = NewFileLogger(logFilePath, level)  // 這裡實際是通過 beego logs 來實現功能的
  } else {
    return fmt.Errorf("Log type is not valid\n")
  }

  return nil
}

配置

配置採用 toml 格式, 配置文件中一般存放不怎麼改變的內容, 改動比較頻繁的配置還是放在資料庫比較好.

import (
  "github.com/BurntSushi/toml"
)

type Config struct {
  Server serverConfig `toml:"server"`
  DB     dbConfig     `toml:"db"`
  Logger loggerConfig `toml:"logger"`
  File   fileConfig   `toml:"file"`
}

type serverConfig struct {
  Port int `toml:"port"`
}

type dbConfig struct {
  Port     int    `toml:"port"`
  Host     string `toml:"host"`
  DBName   string `toml:"db_name"`
  UserName string `toml:"user_name"`
  Password string `toml:"password"`
}

type loggerConfig struct {
  Level   string `toml:"level"`
  Type    string `toml:"type"`
  LogPath string `toml:"logPath"`
}

type fileConfig struct {
  UploadDir   string `toml:"uploadDir"`
  DownloadDir string `toml:"downloadDir"`
}

var conf *Config

func GetConfig() *Config {
  return conf
}

func InitConfig(confPath string) error {
  _, err := toml.DecodeFile(confPath, &conf)
  return err
}

靜態文件服務

本工程中靜態文件服務的目的是為了發佈前端. 前端採用 react 開發, build 之後的代碼放在靜態服務目錄中. 使用 gin 框架的靜態服務中間件, 很容易實現此功能:

// static files
r.Use(static.Serve("/", static.LocalFile("./public", true)))

// 沒有路由匹配時, 回到首頁
r.NoRoute(func(c *gin.Context) {
  c.File("./public/index.html")
})

上傳/下載

上傳/下載 在 gin 框架中都有支持.

  • 上傳

    func UploadXls(c *gin.Context) {
      // ... 省略的處理
    
      // upload form field name: uploadXls, 這個名字和前端能對上就行
      // file 就是上傳文件的文件流
      file, header, err := c.Request.FormFile("uploadXls")
      if err != nil {
        Fail(c, "param error: "+err.Error(), nil)
        return
      }
    
      // ... 省略的處理
    }
  • 下載

    func DownloadXls(c *gin.Context) {
      // ... 省略的處理
    
      c.File(downloadPath)
    }

發佈

基於上面幾個模塊, 一般業務不是很複雜的小應用都可以勝任. 開發之後, 就是打包發佈. 因為這個方案是針對小應用的, 所以把前後端都打包到一起作為一個整體發佈.

docker 打包

之所有採用 docker 方式打包, 是因為這種方式易於分發. docker file 如下:

# 編譯前端
FROM node:10.15-alpine as front-builder

WORKDIR /user
ARG VERSION=no-version
ADD ./frontend/app-ui .
RUN yarn
RUN yarn build


# 編譯前端
FROM golang:1.12.5-alpine3.9 as back-builder

WORKDIR /go
RUN mkdir -p ./src/app-api
ADD ./backend/src/app-api ./src/app-api
RUN go install app-api


# 發佈應用 (這裡可以用個更小的 linux image)
FROM golang:1.12.5-alpine3.9

WORKDIR /app
COPY --from=front-builder /user/build ./public
COPY --from=back-builder /go/bin/app-api .
ADD ./deploy/builder/settings.toml .

CMD ["./app-api", "-f", "./settings.toml", "-prod"]

部署中遇到的問題

時區問題

docker 的官方 image 基本都是 UTC 時區的, 所以插入資料庫的時間一般會慢 8 個小時. 所以, 在 docker 啟動或者打包的時候, 需要對時區做一些處理.

  1. 資料庫連接的設置

    // 連接字元串中加上: loc=Local
    var conStr = fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8&loc=Local",
      c.DB.UserName, c.DB.Password, c.DB.Host, c.DB.Port, c.DB.DBName)
  2. 資料庫鏡像的設置 (環境變數中設置時區)

    # -e TZ=Asia/Shanghai 就是設置時區
    docker run --name xxx -e TZ=Asia/Shanghai -d mysql:5.7
  3. 應用鏡像的設置 (docker-compose.yml) 在 volumes 中設置時區和主機一樣

    services:
    user:
      image: xxx:latest
      restart: always
      networks:
        - nnn
      volumes:
        - "/etc/localtime:/etc/localtime:ro"

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 概述 基於串口的SD_card系統1, 掃描文件;2, 新建文件;3, 刪除文件;4, 寫入文件;5, 讀取文件。 整個文件系統的串口通信方式都是ASC通信方式。 文件系統分為簡單實用方式和專業使用方式,簡單使用方式只需要普通的串口調試助手就可以完成。專業使用方式需要專用的調試上位機。簡單模式/專業 ...
  • 背景 很多人心裡是想一直做技術的,但是擔心隨著年紀增加,會造成在技術方面的競爭力不足。加上迫於經驗人士的壓力,就在糾結要不要轉管理崗。不是什麼權威意見,僅僅談談個人看法。 個人觀點:做自己想做的事。 優劣勢 技術和管理兩個路線各有優劣勢。用SWOT分析來展現兩個維度的關係。 如果有朋友對到底是走哪個 ...
  • 第1題:閱讀下麵的代碼,默讀出A0,A1至An的最終值。 默讀代碼類的題目,相對來說是比較簡單的。重點去研究列表解析,之後你就可以輕鬆的回答這些問題嘍~ 第2題:如何提高python的運行效率? 1. 數據結構一定要選對 能用字典就不用列表:字典在索引查找和排序方面遠遠高於列表。 2. 多用pyth ...
  • 內容簡介 本書面向所有對機器學習與數據挖掘的實踐及競賽感興趣的讀者,從零開始,以Python編程語言為基礎,在不涉及大量數學模型與複雜編程知識的前提下,逐步帶領讀者熟悉並且掌握當下最流行的機器學習、數據挖掘與自然語言處理工具,如Scikitlearn、NLTK、Pandas、gensim、XGBo ...
  • Java9的日誌級別: ALL 最低級別,系統會輸出所有的日誌信息,會生成大量的·、冗餘的日誌 TRACE 輸出系統的各種跟蹤信息,會生成大量的·、冗餘的日誌 DEBUG 輸出調試信息,會生成較多的日誌 INFO 輸出系統提示用戶的信息 WARNING 輸出警告信息 ERROR 輸出錯誤信息 OFF ...
  • 筆記 7天學完Java基礎之0/7 1.常用命令提示符(cmd) 啟動: Win+R,輸入cmd​ :twisted_rightwards_arrows: cmd 切換盤符 盤符名稱+:(冒號為英文輸入法下的冒號) 進入指定文件夾 cd +文件夾名稱 註意只能夠進入文件夾,不能夠進入文件哦 查看當前 ...
  • 國際化是指應用程式運行時,可根據客戶端OS的國家/地區、語言的不同而顯示不同的界面,比如客戶端OS的語言環境為大陸的簡體中文,程式就顯示為簡體中文,客戶端OS的語言環境為美國——英語,程式就顯示美式英語。 OS的語言環境可在控制面板中手動設置。 國際化的英文單詞是Internationalizati ...
  • Python進階之網路編程,內容包括 網路通信,socket,udp,tcp。其中,網路通信 包括 使用網路的目的,ip地址,埠;socket 包括 socket的概念,創建socket;udp 包括 udp發送數據,udp接收數據,udp接發數據總結,埠綁定的問題,單工半雙工全雙工;tcp 包... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...