golang實現基於redis和consul的可水平擴充的熱門排行榜服務範例

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

  本文的完整代碼見https://github.com/changjixiong/goNotes/tree/master/redisnote ,https://github.com/changjixiong/goNotes/tree/master/utils 及https://github.com/changjixiong/goNotes/tree/master/reflectinvoke如果文中沒有顯示連結說明連結在被轉寄的時候被幹掉了,請搜尋找到原文閱讀。

概述

  熱門排行榜在各種互連網應用中廣泛存在。本文將用一個範例說明如何利用redis和consul實現可水平擴充的等級熱門排行榜服務。

redis的使用

  實現熱門排行榜有2個地方需要用到redis:

  1.儲存玩家的排行資訊,這裡使用的是Sorted Sets,代碼如下

err := Rds.ZAdd(        PlayerLvRankKey,        redis.Z{            Score:  lvScoreWithTime(playerInfo.Lv, time.Now().Unix()),            Member: playerInfo.PlayerID,        },    ).Err()

  其中lvScoreWithTime根據玩家等級及到達的時間計算score用於排名,等級相同的情況下,先到達等級的計算分值大於後達到的。

  2.儲存玩家自身的資訊(名字,ID等),用於在熱門排行榜中顯示,畢竟僅僅只有排行的ID是不夠的。這裡採用hashset,代碼如下

// ma的類型為map[string]stringerr := Rds.HMSet(fmt.Sprintf("playerInfo:%d", playerID), ma).Err()

伺服器端

  先初始化redis串連

rdsClient := redis.NewClient(&redis.Options{        Addr:     fmt.Sprintf("%s:%d", "127.0.0.1", 6379),        Password: "123456",        DB:       0,    })    playercache.Rds = rdsClient    rankservice.Rds = rdsClient

  增加初始玩家資訊(略)。

  註冊伺服器介面,此部分詳細說明請參考《golang通過反射使用json字串調用struct的指定方法及返回json結果》http://changjixiong.com/reflect-invoke-method-of-struct-and-get-json-format-result/

reflectinvoke.RegisterMethod(rankservice.DefaultRankService)

  將服務註冊到consul,此部分詳細說明請參考《golang使用服務發現系統consul》http://changjixiong.com/use-consul-in-golang/

go registerServer()

  在連接埠9528上開啟服務用於結構client請求並返回結果

ln, err := net.Listen("tcp", "0.0.0.0:9528")    if nil != err {        panic("Error: " + err.Error())    }    for {        conn, err := ln.Accept()        // 對Accept()產生的臨時錯誤的處理,可以參考net/http/server.go中的func (srv *Server) Serve(l net.Listener)        if err != nil {            panic("Error: " + err.Error())        }        go RankServer(conn)    }

  增加玩家經驗及設定玩家的熱門排行榜資料的介面如下

func (rankService *RankService) AddPlayerExp(playerID, exp int) bool {    player := playercache.GetPlayerInfo(playerID)    if nil == player {        return false    }    player.Exp += exp    // 固定經驗升級,可以按需要修改    if player.Exp >= playercache.LvUpExp {        player.Lv += 1        player.Exp = player.Exp - playercache.LvUpExp        rankService.SetPlayerLvRank(player)    }    playercache.SetPlayerInfo(player)    return true}func (rankService *RankService) SetPlayerLvRank(playerInfo *playercache.PlayerInfo) bool {    if nil == playerInfo {        return false    }    err := Rds.ZAdd(        PlayerLvRankKey,        redis.Z{            Score:  lvScoreWithTime(playerInfo.Lv, time.Now().Unix()),            Member: playerInfo.PlayerID,        },    ).Err()    if nil != err {        log.Println("RankService: SetPlayerLvRank:", err)        return false    }    return true}

  擷取指定排行的玩家資訊的介面

func (rankService *RankService) GetPlayerByLvRank(start, count int64) []*playercache.PlayerInfo {    playerInfos := []*playercache.PlayerInfo{}    ids, err := Rds.ZRevRange(PlayerLvRankKey, start, start+count-1).Result()    if nil != err {        log.Println("RankService: GetPlayerByLvRank:", err)        return playerInfos    }    for _, idstr := range ids {        id, err := strconv.Atoi(idstr)        if nil != err {            log.Println("RankService: GetPlayerByLvRank:", err)        } else {            playerInfo := playercache.LoadPlayerInfo(id)            if nil != playerInfos {                playerInfos = append(playerInfos, playerInfo)            }        }    }    return playerInfos}

用戶端

  串連到consul並查到到熱門排行榜服務的地址,串連並發送請求

func main() {    client, err := consulapi.NewClient(consulapi.DefaultConfig())    if err != nil {        log.Fatal("consul client error : ", err)    }    for {        time.Sleep(time.Second * 3)        var services map[string]*consulapi.AgentService        var err error        services, err = client.Agent().Services()        log.Println("services", strings.Repeat("-", 80))        for _, service := range services {            log.Println(service)        }        if nil != err {            log.Println("in consual list Services:", err)            continue        }        if _, found := services["rankNode_1"]; !found {            log.Println("rankNode_1 not found")            continue        }        log.Println("choose", strings.Repeat("-", 80))        log.Println("rankNode_1", services["rankNode_1"])        sendData(services["rankNode_1"])    }}

運行情況

  consul上註冊了2個自訂的服務,一個是名為serverNode的echo服務(來源 《golang使用服務發現系統consul》),另一個是本文的熱門排行榜服務rankNode。

  伺服器接收到的請求片段

get: {"func_name":"AddPlayerExp","params":[4,41]}get: {"func_name":"AddPlayerExp","params":[2,35]}get: {"func_name":"AddPlayerExp","params":[5,27]}get: {"func_name":"GetPlayerByLvRank","params":[0,3]}

  用戶端在consul中尋找到服務並串連rankNode_1

services ----------------------------------------------------------&{consul consul [] 8300  false}&{rankNode_1 rankNode [serverNode] 9528 127.0.0.1 false}&{serverNode_1 serverNode [serverNode] 9527 127.0.0.1 false}choose ------------------------------------------------------------rankNode_1 &{rankNode_1 rankNode [serverNode] 9528 127.0.0.1 false}

  用戶端收到的回應片段

get: {"func_name":"AddPlayerExp","data":[true],"errorcode":0}get: {"func_name":"AddPlayerExp","data":[true],"errorcode":0}get: {"func_name":"AddPlayerExp","data":[true],"errorcode":0}get: {"func_name":"GetPlayerByLvRank","data":[[{"player_id":3,"player_name":"玩家3","exp":57,"lv":4,"online":true},{"player_id":2,"player_name":"玩家2","exp":31,"lv":4,"online":true},{"player_id":1,"player_name":"玩家1","exp":69,"lv":3,"online":true}]],"errorcode":0}

一點說明

  為什麼說是可水平擴充的熱門排行榜服務呢?文中已經看到,目前有2個自定的服務註冊在consul上,client選擇了rankNode_1,那麼如果註冊了多個rankNode,則可以在其中某些節點不可用時,client可以選擇其他可用的節點擷取服務,而當停用節點重新可用時,可以繼續註冊到consul以提供服務。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.