skynet设计原理

skynet是一个轻量级的游戏服务器框架;实现了actor的并发模型;可以基于skynet框架去实现业务。

多核并发编程

多线程

在一个进程中开启多线程,为了充分利用多核,一般设置工作线程的个数为 cpu 的核心数;

memcached 就是采用这种方式;

多线程在一个进程当中,所以数据共享来自进程当中的内存;这里会涉及到很多临界资源的访问,所以需要考虑加锁;

多进程

在一台机器当中,开启多个进程充分利用多核,一般设置工作进程的个数为 cpu 的核心数;

nginx 就是采用这种方式;

CSP

以 go 语言为代表,并发实体是协程(用户态线程、轻量级线程);内部也是采用多少个核心开启多少个内核线程来充分利用多核;

actor

erlang 从语言层面支持 actor 并发模型,并发实体是 actor(用户态进程);skynet采用 c + lua来实现 actor 并发模型, skynet中的actor也叫服务;底层也是通过采用多少个核心开启多少个内核线程来充分利用多核;

总结

不要通过共享内存来通信,而应该通过通信来共享内存。

actor定义

为什么要抽象进程?

每一个进程就是一个运行实体,这些运行实体提供了相互隔离的运行环境。

// 服务的定义,就是actor
struct skynet_context {
	void * instance; // 隔离的运行环境,一块内存或者lua虚拟机
	struct skynet_module * mod; // 服务的启动文件
	void * cb_ud;
	skynet_cb cb; // callback,通过调用cb来运行actor
	struct message_queue *queue; // 消息队列,按照消息到达的先后顺序执行它
	ATOM_POINTER logfile;
	uint64_t cpu_cost;	// in microsec
	uint64_t cpu_start;	// in microsec
	char result[32];
	uint32_t handle; // actor的id
	int session_id;
	ATOM_INT ref; // 引用计数
	int message_count; // actor处理了多少个消息
	bool init; // 是否初始化
	bool endless; // 是否陷入死循环
	bool profile; // 是否调试状态

	CHECKCALLING_DECL
};

actor组成部分

  1. 隔离环境,主要是lua虚拟机,也有可能是一块内存
  2. 回调函数,帮助我们运行actor;在C语言层面,只有一个回调函数;在lua层面,有多个回调函数,不同类型的消息调用不同的回调函数,比如网络消息、actor之间的消息;
  3. 消息队列

actor消息

消息的类型:actor之间的消息,网络消息、定时消息。

网络消息的事件如何与actor绑定?

在这里插入图片描述
在这里插入图片描述
skynet以消息的方式运行,所有的任务都以消息的方式进行传递。

skynet 通过 socket.start(fd, func) 来完成 actor 与 fd 的绑定;

底层epoll中,是通过epoll_ctl设置struct epoll_event中 data.ptr = (struct socket *)ud; 来完成fd 与 actor绑定;

actor之间的消息

消息怎么一一对应上?

一个actor要往另一个actor中发消息,只是将消息放入目的actor的消息队列中,是一个move的操作。

定时消息

skynet中有一个单独线程来处理定时任务,使用时间轮算法。

当定时任务被触发,会将定时任务包装成消息,传递给产生这个任务的actor,从而驱动actor的运行。

actor的调度

actor的运行是由线程池驱动的。

线程池的调度

在这里插入图片描述
线程调度方式,是指队列中的任务从无到有,是怎么处理的, 消费线程如何调度? 需要怎么唤醒消费者线程?如果任务队列中的任务从有到无,需要怎么让消费者线程休眠?
在这里插入图片描述
消息生产者:网络线程、定时线程、工作线程

消息消费者:工作线程

工作线程

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
memcached使用的自旋锁

nginx解决惊群,使用自旋锁(cas实现自旋锁),锁存放在共享内存中。
在这里插入图片描述
在这里插入图片描述
线程调度方式
在这里插入图片描述
什么时候调用pthread_cond_signal?

有消息产生的时候,需要signal。

什么时候调用pthread_cond_broadcast?

线程池要退出的时候,需要broadcast。

signal在哪里调用?
在这里插入图片描述
网络线程产生消息,需要wakeup
在这里插入图片描述
定时线程产生消息,需要wakeup
在这里插入图片描述
actor之间的消息,不需要wakeup
在这里插入图片描述

actor的调度

在这里插入图片描述
worker线程池针对的是全局消息队列,全局消息队列组织的是活跃的actor的消息队列;活跃的actor是指actor的消息队列中有消息
在这里插入图片描述
在这里插入图片描述

协程

为什么要引入协程?

需要用协程来消除回调。

skynet,一个消息对应一个协程;openresty,一个请求对应一个协程。

原文地址

LinuxC/C++服务器开发/高级架构师 学习公开课

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值