歡迎關注樓主與他的小夥伴們的小站,每週分享一些技術文章,讓咱們在技術上一塊兒成長------> 戳這裏,歡迎光臨小站 -_-git
做爲一個後端開發,在docker,etcd,k8s等新技術不斷涌現的今天,其背後的功臣golang在語言排行榜上持續走高,所以樓主也就開了此次使用golang本身開發的基礎功能的二次裝逼之旅。
github
感興趣的小夥伴能夠看看樓主的上一篇,基於Spring Boot實現的功能,請移步使用Spring Boot實現博客統計服務golang
選擇redis而沒選擇數據庫的緣由是redis提供了豐富的數據結構與數據持久化策略,另外redis是基於內存的,相對於數據庫來講,快了不止一個數量級。而統計閱讀次數的場景對接口處理的速度仍是有必定的要求的,所以樓主選擇了redis做爲閱讀次數統計的db。
下面就是redis操做的基礎代碼,比較簡單樓主貼一下代碼,不作進一步的闡述。web
go get github.com/gomodule/redigo/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,下面是樓主實現該邏輯的主要代碼:redis
package main import ( "encoding/json" "fmt" "github.com/garyburd/redigo/redis" "log" "net/http" "time" "strings" ) const RedisAddress = "127.0.0.1:6379" const RedisDb = 0 const 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工具序列化時,出現序列化失敗的問題,以下所示的結構體定義,乍一看是沒啥問題的,然而使用spring
ret, _ := json.Marshal(result)
序列化時,出現沒法序列化成json串的問題,另外還不報錯,這讓樓主非常頭疼。docker
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服務。json
樓主造了一個輪子,LIGHTCONF 是一個基於Netty實現的一個配置管理平臺,其核心設計目標是「爲業務提供統一的配置管理服務」,能夠作到開箱即用。感興趣的給個star支持一下。後端