golang 重構部落格統計服務

來源:互聯網
上載者:User

歡迎關注樓主與他的小夥伴們的小站,每周分享一些技術文章,讓我們在技術上一起成長------> 戳這裡,歡迎光臨小站 -_-

作為一個後端開發,在docker,etcd,k8s等新技術不斷湧現的今天,其背後的功臣golang在語言熱門排行榜上持續走高,因此樓主也就開了這次使用golang自己開發的基礎功能的二次裝逼之旅。

源於Spring Boot

感興趣的小夥伴可以看看樓主的上一篇,基於Spring Boot實現的功能,請移步使用Spring Boot實現部落格統計服務

實現redis儲存邏輯

選擇redis而沒選擇資料庫的原因是redis提供了豐富的資料結構與資料持久化策略,另外redis是基於記憶體的,相對於資料庫來說,快了不止一個數量級。而統計閱讀次數的情境對介面處理的速度還是有一定的要求的,因此樓主選擇了redis作為閱讀次數統計的db。
下面就是redis操作的基礎代碼,比較簡單樓主貼一下代碼,不做進一步的闡述。

  • redigo依賴下載
go get github.com/gomodule/redigo/redis
  • redis操作的工具類
func initRedisPool() {    // 建立串連池    RedisClient = &redis.Pool{        // 從設定檔擷取maxidle以及maxactive,取不到則用後面的預設值        MaxIdle:     1,        MaxActive:   10,        IdleTimeout: 180 * time.Second,        Dial: func() (redis.Conn, error) {            c, err := redis.Dial("tcp", RedisAddress)            if err != nil {                return nil, err            }            // 選擇db            c.Do("SELECT", RedisDb)            return c, nil        },    }}/** * 設定redis的對應key的value */func redisSet(key string, value string) {    c, err := RedisClient.Dial()    if err != nil {        fmt.Println("Connect to redis error", err)        return    }    _, err = c.Do("SET", key, value)    if err != nil {        fmt.Println("redis set failed:", err)    }}/** * 擷取redis的對應key的value */func redisGet(key string) (value string) {    c, err := RedisClient.Dial()    if err != nil {        fmt.Println("Connect to redis error", err)        return    }    val, err := redis.String(c.Do("GET", key))    if err != nil {        fmt.Println("redis get failed:", err)        return ""    } else {        fmt.Printf("Got value is %v \n", val)        return val    }}/** * redis使得對應的key的值自增 */func redisIncr(key string) (value string) {    c, err := RedisClient.Dial()    _, err = c.Do("INCR", key)    if err != nil {        fmt.Println("incr error", err.Error())    }    incr, err := redis.String(c.Do("GET", key))    if err == nil {        fmt.Println("redis key after incr is : ", incr)    }    return incr}

部落格閱讀次數統計介面實現

部落格閱讀次數統計的基本商務邏輯就是,對應每篇部落格的blogId作為redis的key,而訪問次數就是這個key所對應的value,每訪問一次該介面就要將對應的blogId自增一次,並返回對應的value。這裡樓主選擇的redis的資料結構是redis的Stirng,下面是樓主實現該邏輯的主要代碼:

package mainimport (    "encoding/json"    "fmt"    "github.com/garyburd/redigo/redis"    "log"    "net/http"    "time"    "strings")const RedisAddress = "127.0.0.1:6379"const RedisDb = 0const AllowRequestUrlH = "*"const  AllowRequestUrlW = "*"const  IllegalCharacters = "?"const  DefaultReadCount = "1"var (    // 定義常量    RedisClient *redis.Pool)func main() {    // 初始化redis串連池    initRedisPool()    // 啟動web服務監聽    http.HandleFunc("/*-*/*/", blogReadCountIncr)       //設定訪問的路由    err := http.ListenAndServe(":9401", nil) //設定監聽的連接埠    if err != nil {        log.Fatal("ListenAndServe: ", err)    }}func blogReadCountIncr(responseWriter http.ResponseWriter, request *http.Request) {    // 解析參數,預設不解析    request.ParseForm()    blogId := request.Form.Get("blogId")    log.Println(">>>>>> method blogReadCountIncr exec , request params is : ",blogId)    // 判斷請求參數是否為空白    if "" == blogId {        result := ResultCode{            Code: 200,            Msg:  "success",        }        ret, _ := json.Marshal(result)        fmt.Fprintf(responseWriter, string(ret)) //這個寫入到w的是輸出到用戶端的    }        readCount := redisGet(blogId)    if "" == readCount {        // 不符合規則,直接返回        flag := strings.Index(blogId, AllowRequestUrlH) != 0 ||strings.Index(blogId, AllowRequestUrlW) != 0||strings.Contains(blogId, IllegalCharacters)        if  !flag {            result := ResultCode{                Code: 200,                Msg:  "success",            }            ret, _ := json.Marshal(result)            fmt.Fprintf(responseWriter, string(ret)) //這個寫入到w的是輸出到用戶端的        }        redisSet(blogId, DefaultReadCount)        readCount = DefaultReadCount    } else {        readCount = redisIncr(blogId)    }    log.Println(">>>>>> readCount is : ",readCount)    result := ResultCode{        Code: 200,        Msg:  "success",        Data: readCount,    }    ret, _ := json.Marshal(result)    fmt.Fprintf(responseWriter, string(ret)) //這個寫入到w的是輸出到用戶端的}// 結構體定義傳回值type ResultCode struct {    Msg  string `json:"msg"`    Code int    `json:"code"`    Data string `json:"data"`}

實現過程中遇到的坑

出現的問題

使用golang原生的json工具序列化時,出現序列化失敗的問題,如下所示的結構體定義,乍一看是沒啥問題的,然而使用

ret, _ := json.Marshal(result)

序列化時,出現無法序列化成json串的問題,另外還不報錯,這讓樓主很是頭疼。

type ResultCode struct {    msg  string `json:"msg"`    code int    `json:"code"`    data string `json:"data"`}

問題解決

最終樓主通過各種姿勢的排查,發現是結構體定義有問題,當定義結構體時首字母必須大寫才能序列化成功,這個特點在golang裡面很是明顯,在函數調用時首字母小寫函數在其他檔案裡面是調不到的。下面給出正確的結構體定義

type ResultCode struct {    Msg  string `json:"msg"`    Code int    `json:"code"`    Data string `json:"data"`}

小結

目前很多大佬都寫過關於golang web的教程,如有雷同,請略過不看,本文通過自己的親身實戰以及樓主自己踩到的坑完成的,另外本文是基於go內建的net/http庫實現的web服務。

號外

樓主造了一個輪子,LIGHTCONF 是一個基於Netty實現的一個組態管理平台,其核心設計目標是“為業務提供統一的組態管理服務”,可以做到開箱即用。感興趣的給個star支援一下。

  • 基於Netty實現的輕量級分布式應用配置中心
  • 戳這裡,歡迎光臨小站 -_-
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.