skynet学习笔记(一)理解Service

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

是什么

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

为何

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

好处

这样作的好处是,在服务的层面实际上是感受不到线程的,你能够认为你就是线程,你也不用关心和其它服务的多线程复杂度问题,不一样服务间经过消息通讯。你能够用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个玩家要等上几乎同样多的时间。缘由并不难理解,但这个表现也挺让人吃惊。要避免这个问题,就必须限制正在加载中的玩家数,抑制极端状况的出现。框架