用户是如何注册的?

简介

skynet_fly_demo在账号上采用了传统的账号密码方式。

流程

  1. c2s 用户调用loginserver的注册接口
  2. s2s loginserver 调用 centerserver 注册
  3. s2s centerserver 调用 hallserver 注册
  4. s2c loginserver 返回注册结果

流程1

涉及skynet_fly技术

* http server 
* cluster rpc

涉及服务

* loginserver
* centerserver

客户端通过http方式调用user/signup接口进行注册,客户端传递账号,密码,渠道号,之后loginserver rpc 调用centerserver 进行注册,为啥不让客户端直接去centerserver直接注册,因为center server只有1个,能提供的负载能力有限,我们需要通过loginserver 做一些限流或者排队措施,防止centerserver 超负载。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
--注册
local function signup(c)
local req = c.req
local body = req.body
local account = body.account
local password = body.password
local channel = body.channel

assert(account, "not account")
assert(password, "not passwword")
assert(channel, "not channel")
assert(CHANNEL[channel], "not exists channel", channel)

local isok, errcode, errmsg = rpc_center_account.register({
account = account,
password = password,
}, channel)
if isok then
rsp_body.set_rsp(c, "success")
else
rsp_body.set_rsp(c, nil, errcode, errmsg)
end
end

流程2

涉及skynet_fly技术

* cluster rpc
* orm

涉及服务

* centerserver
* hallserver

由于centerserver只有一个,它记录了所以玩家的账号信息,所以有必要对玩家的账号信息进行分表处理,我用orm为账号分了10个表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
   -- orm
orm_table_m = {
launch_seq = 5000,
launch_num = 11,
mod_args = {
{instance_name = "account_1",orm_plug = "orm_entity.account_entity"},
{instance_name = "account_2",orm_plug = "orm_entity.account_entity"},
{instance_name = "account_3",orm_plug = "orm_entity.account_entity"},
{instance_name = "account_4",orm_plug = "orm_entity.account_entity"},
{instance_name = "account_5",orm_plug = "orm_entity.account_entity"},
{instance_name = "account_6",orm_plug = "orm_entity.account_entity"},
{instance_name = "account_7",orm_plug = "orm_entity.account_entity"},
{instance_name = "account_8",orm_plug = "orm_entity.account_entity"},
{instance_name = "account_9",orm_plug = "orm_entity.account_entity"},
{instance_name = "account_10",orm_plug = "orm_entity.account_entity"},

--自增id分配
{instance_name = "allocid", orm_plug = "orm_entity.allocid_entity"},
}
},

如何映射到表,通常账号是不支持更改的,我们直接使用账号尾部字符编码 mod 分表数量得到一个索引进行映射。

1
2
3
4
5
6
7
8
9
--注册
function M.register(account_info, channel)
local cli = frpc_client:instance("centerserver", "account_m")
cli:set_mod_num(sbyte(account_info.account, account_info.account:len()))
local ret = cli:one_mod_call("register", account_info, channel)
if not ret then return end

return table.unpack(ret.result)
end

注册到大厅服,我们需要做两件事情

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
local MAX_INCRID = 9999999
local function register(account_info, channel)
assert(channel <= 9999, "overflow channel = ",channel)
local account = account_info.account --账号
assert(account:len() >= 6, "account not long enough")
local orm_clinet = get_orm_by_account(account)
if orm_clinet:get_one_entry(account) then
return nil, CODE.EXISTS_USER, "EXISTS_USER"
end

local module_id, svr_id = rpc_hallserver_player_m.get_module_id()
assert(module_id, "register err ")

local incrid = g_alloc_client:incr(module_id)
assert(incrid <= MAX_INCRID, "incr overflow")
local player_id = player_util.builder_player_id(channel, svr_id, incrid)
local ret = rpc_hallserver_player_m.register(player_id, account)
assert(ret, "register err")

account_info.key = crypt.randomkey()
account_info.password = crypt_util.HMAC.SHA256(account_info.password, account_info.key)
account_info.key = crypt.base64encode(account_info.key)
account_info.player_id = player_id
account_info.hall_server_id = svr_id
account_info.channel = channel
account_info.create_time = time_util.time()
if orm_clinet:create_one_entry(account_info) then
return true
else
return nil
end
end
  1. 选择大厅服

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    -- 获取自增ID所属的模块ID
    function M.get_module_id()
    local cli = frpc_client:instance("hallserver", "player_m")
    local ret = cli:one_balance_call("get_module_id")
    if not ret then return end

    local cluster_name = ret.cluster_name
    local result = ret.result
    local module_id = result[1]
    local svr_id = tonumber(string_util.split(cluster_name, ':')[2])
    return module_id, svr_id
    end

    目前我才用的简单轮询负载均衡,这里也可以定制做其他负载均衡的策略。
    rpc_hallserver_player_m.get_module_id 调用实现采用了banlance简单负载均衡的方式,假如开启了hallserver1,hallserver2,调用此接口4次返回的svr_id将会是1,2,1,2。
    module_id是对应hallserver绑定的自增模块ID,分配玩家ID,为了避免ID重复我们需要一个自增ID分配器,使用orm来做就是一个非常不错的选择。自增ID的orm可以配置为永久缓存,自增的时候不需要访问到数据库,非常便捷,快速。

  2. 分配玩家ID

    由于架构的设计玩家ID会跟指定的大厅服绑定,我们可以让玩家ID携带一些额外信息,比如大厅服的ID,这样其他服想拿取玩家的数据时,通过玩家ID就能知道访问哪个服务器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
--构建玩家ID
--由于js没有long类型,最大能表示 2^53-1的整数,所以调整一下ID结构,为了尽可能的兼容所有客户端
-- 9 0071 9925 4740991 (1个能注册1千万-1个账号)
-- 预留位 渠道id 服务id 自增id

local MAX_INCRID = 9999999
local INCRID_LIMIT = MAX_INCRID + 1
local MAX_CHANELLID = 9999
local CHANELLID_LIMIT = MAX_CHANELLID + 1
local MAX_SVRID = 9999
local SVRID_LIMIT = MAX_SVRID + 1
function M.builder_player_id(channel_id, svr_id, incrid)
assert(channel_id <= MAX_CHANELLID, "channel_id overflow")
assert(svr_id <= MAX_SVRID, "svr_id overflow")
assert(incrid <= MAX_INCRID, "incr overflow")
return tonumber(string.format("1%04d%04d%07d", channel_id, svr_id, incrid))
end

流程3

涉及skynet_fly技术

* cluster rpc

涉及服务

* centerserver
* hallserver

分配创建好对应的玩家ID后,就会去对应的hallserver创建好玩家数据。rpc_hallserver_player_m.register(player_id, account)
到这RPC请求的过程已经走完了,轮到了回复。
hallserver 回复 centerserver 注册结果。
centerserver 回复 loginserver 注册结果。

流程4

涉及skynet_fly技术

* cluster rpc

loginserver 回复 客户端 注册结果。至此注册流程结束。


用户是如何注册的?
https://huahua132.github.io/2023/05/03/skynet_fly_word/word_4/B_q/
作者
huahua132
发布于
2023年5月3日
许可协议