服务器底层系统6U,云风的 BLOG

local salt = service.call(addr ,

"login.salt" , { username = username }).salt

local response = md5(username .. ":" .. password .. ":" .. salt)

local ok = service.call(addr, "login.challenge" ,

{ username = username , response = response }).ok

print(ok)

而 service 编写是这样的:

-- login.lua

function salt(username)

local salt = gen_salt(username)

return { salt = salt }

end

function challenge(username , response)

local salt = gen_salt(username)

local password = service.call("authdb",

"query" , { username = username }).password

local result = md5(username .. ":" .. password .. ":" .. salt

return { ok = (result == response) }

end

大家需要约定中间的协议,采用 protobuf 格式描述:

package login;

message salt {

optional string username = 1;

message response {

optional bool salt = 1;

}

}

message challenge {

optional string username = 1;

optional string response = 2;

message response {

optional bool ok = 1;

}

}

btw, 我没有用 protobuf 的 serivce 特性。觉得不太方便,而且也没有太多必要引入过多特性。

当然这只是 RPC 范例代码,不设计认证协议的具体设计。实际应用中,校验过程会比这个更完备一些。

在 login 服务中,收到用户来的请求,会去向 authdb 服务索取密码信息。这个 RPC 调用是阻塞的,直到 authdb 返回结果。但是,login 服务并不被阻塞,可以接受其它请求。每个不同请求的回应对应关系,是在低一个层次上的 session id 来区分。这些隐藏在框架实现中了。

设计细节今天先不列了,也没太多难度。

最后,我对 RPC 的使用是谨慎的。必须留意 RPC 会带来的问题。这些在《Unix 编程艺术》7.3.2 有论述,我再说多少次也超不出这个范畴。

做这套东西,我和蜗牛是有一些分工的。服务器数据流框架并不是我来实现。蜗牛用 erlang 做的。

这些东西我们折腾了好久,这里面有我的很多工作失误。最终,我觉得应该把工作严格划分开,需要有一个清晰的模块需求定义。

本来是初步定好了,每个服务工作在一个独立的 Lua State 中(并不一定在独立进程/线程中),包括为每个 Client 服务的 Agent 。一开始是在 Lua 的层次来划分工作的。蜗牛写了一部分的 Lua 驱动代码。我觉得是我在分工问题上的错误。这个需求很难稳定下来。后来,我整理了一下思路,决定定义明确的 C 接口,然后我来做 Lua 封装。

服务器的大框架主要解决玩家的接入问题,各个服务间的数据流向问题,服务的启动和停止等等。我设想,每个服务都有单一的输入输出流,只面对框架。实现上可能框架并不是在做复制转发,有可能直接对接两个服务的输入输出。尤其是两个服务可能是在同一个线程中,以 coroutine 的形式工作。这个优化空间可以留下来。

我在做后面的工作之前,先写了一份文档,描述需求:

向 skynet 发送一条指令, 并立刻获得一个结果. 目前提供以下三条指令:

注册自己到 skynet , 可以给出一个 well-known 的名字 (name), 也可以不给出. skynet 返回一个 unique 名字 (uid)

向 skynet 查询系统时间

设置 timer , skynet 会在 timeout 时回调

向 skynet 发送数据, 由 skynet 决定把数据送达何处.

注册一个 callback , 当 skynet 有消息送达的时候触发.

注:skynet 是框架的开发代号。

然后是接口的 C 定义

void * skynet_context(void);

void skynet_exit(void * context);

void skynet_step(void * ud);

const char * skynet_command(void * context, const char * cmd , const char * parm);

void skynet_send(void * context, const char * addr , const char * msg, size_t sz);

typedef void (*skynet_cb)(void *context, void *ud, const char * uid , const char * msg, size_t sz);

void skynet_callback(void * context, skynet_cb cb);

注: command 我选取了最简单的字符串风格。是因为考虑到字符串的知识依赖最小,很容易 binding 到任何语言中。也最容易实现网络传输。不同 command 的协议定义也写了文档,包括文本协议的定义。这里就不一一列出了。

有了文档之后,我开始编写 skynet 的黑盒。基本上就是一个本地单一进程,用来调度同一进程下的不同服务。这个很好写,大约两三小时,不到 300 行 C 代码就搞定了。虽然限制很多,性能也很低下,但可以实现以上的接口。

之后的代码就不再是假盒子了。是把这几个 C 接口 binding 到 lua 里,然后在 lua 层面写真实的应用。工作量比较大,但也好做。一旦蜗牛那边真正的 skynet 工作正确,整合工作只是替换前面几个接口的实现库而已。

Blog 写这种系列, 不太方便整理。 这点还是 wiki 比较好。我把这个系列整理出一个列表了。

ps. 这段时间还做了许多琐碎的工作,包括帮客户端解决一些问题。中间闲了两天,想玩一下 LaTeX ,结果就写了这个:Lua 源码欣赏 。结果工作一忙就没时间写下去了。估计这个半拉子工程会这么太监掉的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值