Golang implementation of the horizontal scale-up leaderboard service paradigm based on Redis and consul

Source: Internet
Author: User
This is a creation in Article, where the information may have evolved or changed.

For the complete code of this article see Https://github.com/changjixiong/goNotes/tree/master/redisnote, https://github.com/changjixiong/goNotes/ Tree/master/utils and Https://github.com/changjixiong/goNotes/tree/master/reflectinvoke. If the link is not displayed in the text, the link is killed when it is forwarded, please search to find the original reading.

Overview

Leaderboards are widely available in a variety of Internet applications. This article will use an example to illustrate how Redis and consul can be used to achieve horizontally scaled leaderboard services.

Use of Redis

There are 2 places to use Redis for the leaderboard:

1. Store the player's ranking information, which is used sorted sets, the code is as follows

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

Where Lvscorewithtime based on the player level and the time of arrival calculation score for ranking, the same level of the situation, the first to reach the level of the calculation score is greater than after.

2. Store the player's own information (name, ID, etc.) for display in the leaderboard, after all, only the rank of the ID is not enough. Here the hashset is used, the code is as follows

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

Server-side

To initialize a Redis connection first

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

Increase the initial player information (slightly).

Register the server interface, which is detailed in this section, refer to "Go" using JSON string to invoke the specified method of the struct and return the JSON result through reflection http://changjixiong.com/ reflect-invoke-method-of-struct-and-get-json-format-result/

reflectinvoke.RegisterMethod(rankservice.DefaultRankService)

To register the service with Consul, please refer to "Go using service discovery System Consul" in this section for more information http://changjixiong.com/use-consul-in-go/

go registerServer()

Open service on port 9528 for structuring client requests and returning results

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)}

The interface for adding player experience and setting the player's leaderboard data is as follows

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}

Gets the interface for the specified rank of player information

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}

Client

Connect to consul and find the address of the leaderboard service, connect and send requests

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"])    }}

Operating conditions

Consul registered 2 Custom services, one is the Echo service named Servernode (source "Go using service discovery System Consul") and the other is the leaderboard service Ranknode of this article.

Request fragments received by the server

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]}

The client finds the service in consul and connects to the 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}

Response fragments received by the client

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}

A little explanation

Why is it a level-scalable leaderboard service? It has been seen that there are currently 2 custom services registered on Consul, the client chose Ranknode_1, then if more than one ranknode is registered, the client can choose other available nodes to get the service when some of them are unavailable. And when the unavailable node is re-usable, you can continue to register to consul to provide the service.

661 Reads

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

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.