skynet学习笔记(一)理解Service

skynet是一个开源服务框架,运用她可以用lua编写服务器程序。一开始上手有个概念容易弄混,就是‘服务’,service,以前以为服务器无非是处理一些网络协议,抱着这个想法很难理解。原来skynet的服务是一个新概念。

是什么

框架内实现了一套消息处理过程,而这个处理者就是服务。那消息是什么?消息就是一些数据包,在业务上通常是表示一些请求或者回应。那消息是如何处理的,排队一个一个处理,就是存在消息队列的设计(业务上如何处理是你编写的,不要问我)。

为什么

为什么要新创造一个概念呢?直接用系统的线程,写一个消息循环,不就好了么。按我自己的理解,我觉得,是为了让线程资源的分配可以优化。也就是说服务不会独占一个线程,在框架的层面上做服务和线程的分配。比如,为了匹配目标机器,可能会启动和核心数一致或略大于核心数的线程数,作为线程池,实际跑起来的时候服务数可能远大于或者小于配置数,为了不让任何一个服务饥饿,可能会轮着分配,一个消息一个消息处理,也可以为特定的服务设定更高的优先级,等等。

好处

这样做的好处是,在服务的层面其实是感觉不到线程的,你可以认为你就是线程,你也不用关心和其它服务的多线程复杂度问题,不同服务间通过消息通信。你可以用lua这样简法优美的语言,写着几十个线程配合工作,几千个服务协同运行的服务器程序。

不得不提的协程

lua的协程在框架里也扮演比较重要角色,它是在lua虚拟机里的类似线程的概念,它当然不是线程,也就是说所有协同程序是在单个线程里顺序执行,它们中永远只有一个在running,其它全部在yield。但是这样可以让代码看起来像是多线程,这个感觉也很不错,你可以写很多类似阻塞调用,用来代替回调。回调会让执行过程混乱不清,本人是受够了在JS里写各种回调,把代码写得像修长城一样叠好多层(写前端的同学不容易);而用协程会让原本需要回调,而且还需要状态小心处理的过程,可以轻易直接写出来,逻辑清晰易读。所以不要再问,skynet.fork里的代码会不会和外面的代码同时访问某个数据结构,这样问挺蠢的。

有一种冲突的情况

单个服务每收到一个消息,它会在一个新的协程里去执行,如果先前的处理还没有执行完的话。那微妙的事情可能会发生,尽管没有多线程的问题,但协程间还是有可能打架:
1,收到消息A,协程A在处理,IO处理中
2,收到消息B,协程B正要处理,它可能拿到同一份数据,这份数据是协程A在IO处理前状态的
3,协程A的IO处理返回,数据被更新
(它们不会同时去访问,但可能一前一后,而且数据可能会被读出来放在局部变量中,正在做某个比较‘玩家的金币是否>=100’)

解决方法有2种:1,尽量不保存数据在lua环境,skynet服务器就是个消息转发器,一个搬运工,比如就是从缓存里读点东西给客户端。2,实在没办法不保存状态和数据,而这些数据是敏感的,使用CriticalSection,其实就是将处理过程函数排队执行。

以前机器人测试时遇到过一个现象,在首次登录时加载玩家对象时会多次IO调用,假设平均需要10次,那如果同时登录多个玩家,比如1000个,那第1个玩家要等全部人都完成9次IO后,才会完成加载,如果每个玩家正常情况下需要10ms,那这时就需要9s,他和第1000个玩家要等上几乎一样多的时间。原因并不难理解,但这个表现也挺让人吃惊。要避免这个问题,就必须限制正在加载中的玩家数,抑制极端情况的出现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值