源码地址:https://github.com/cloudwu/skynet
框架结构#
skynet-src:Skynet 的核心源码,包括主体框架、服务管理、网络通信、计时器等模块。3rd:Skynet 依赖的第三方库,包括 LuaJIT、luasocket 等。config:Skynet 的配置文件目录。example:Skynet 的示例代码目录,包括 echo 服务、pingpong 示例、socket 模块示例等。luaclib:Skynet 的 C 扩展库,例如 cjson、protobuf 等。lualib:Skynet 的 Lua 扩展库,例如 skynet、socket 等。service:Skynet 的服务代码目录,包括各个服务的 Lua 源码、初始化脚本等。test:Skynet 的测试代码目录。
核心模块#
skynet_main.c:Skynet 主函数,包括参数解析、初始化、启动服务等。skynet.h:Skynet 的核心头文件,包括数据结构定义、函数声明等。service-src:Skynet 服务管理模块,包括服务注册、服务查找、消息处理等。lualib-src:Skynet Lua 扩展模块,包括 Lua 和 C 之间的交互函数等。timer.c:Skynet 计时器模块,包括定时器的设置、删除等。socket_server.c:Skynet 网络通信模块,包括 TCP/UDP 网络连接、消息处理等。logger.c:Skynet 日志模块,包括日志的记录、输出等。malloc_hook.c:Skynet 内存管理钩子,用于统计内存使用情况。
服务管理#
- 注册服务:将服务注册到框架中,使得其他服务可以调用。
- 查找服务:根据服务名查找对应的服务句柄。
- 消息处理:处理来自其他服务的消息。
Lua 扩展模块#
skynet.lua:Skynet 的核心接口,包括服务注册、消息发送、定时器等。socket.lua:网络通信模块,包括 TCP、UDP、HTTP 等协议的支持。cjson.lua:JSON 解析库。protobuf.lua:Google 的 Protobuf 库的 Lua 实现。
网络通信#
1. 网络模型
skynet_socket:Socket 的结构体,包括 Socket 的 ID、类型、地址等信息。struct skynet_context:Actor 的上下文结构体,包括 Actor 的名字、消息队列等信息。skynet_socket_poll:Socket 的轮询函数,用于处理网络事件。skynet_socket_send:向指定的 Socket 发送数据。skynet_socket_connect:向指定的地址和端口号连接 Socket。skynet_socket_bind:绑定 Socket 到指定的地址和端口号。skynet_socket_listen:开始监听 Socket。skynet_socket_close:关闭指定的 Socket。
2. 网络通信
skynet_send:向指定的 Actor 发送一条消息,该函数会查找目标 Actor 并向其消息队列中添加一条消息。skynet_socket_start:启动网络服务,并监听指定的端口号。skynet_socket_poller:网络轮询函数,用于处理网络事件。skynet_socket_udp:创建一个 UDP Socket,并绑定到指定的地址和端口号。skynet_socket_udp_connect:将 UDP Socket 连接到指定的地址和端口号。skynet_socket_udp_send:向指定的 UDP Socket 发送数据。skynet_socket_udp_address:获取 UDP Socket 的地址信息。skynet_socket_udp_close:关闭指定的 UDP Socket。
Actor 模型相关#
1. 消息队列
-
struct message:消息的结构体,包括消息源、目标、类型、数据等信息。消息队列中存储的是
message结构体,该结构体包括消息源、目标、类型、数据等信息,方便消息的传递和处理。 -
struct message_queue:消息队列的结构体,包括消息队列头、尾指针、消息数量等信息。message_queue结构体是消息队列的管理结构体,包括消息队列头、尾指针、消息数量等信息,用于管理消息队列中的消息,方便消息的添加、弹出等操作。 -
skynet_mq_push:向消息队列中添加一条消息。skynet_mq_push函数用于向消息队列中添加一条消息,将消息添加到消息队列的尾部,方便消息的处理。 -
skynet_mq_pop:从消息队列中弹出一条消息。skynet_mq_pop函数用于从消息队列中弹出一条消息,将消息从消息队列的头部弹出,方便消息的处理。 -
skynet_mq_length:获取消息队列中的消息数量。skynet_mq_length函数用于获取消息队列中的消息数量,方便统计消息的数量。 -
skynet_mq_alloc:从内存池中分配一个消息队列。skynet_mq_alloc函数用于从内存池中分配一个新的消息队列,用于消息的存储和管理。 -
skynet_mq_mark_release:标记消息队列为释放状态,当消息队列中的所有消息被处理完毕后,就可以将其释放回内存池。skynet_mq_mark_release函数用于标记消息队列为释放状态,当消息队列中的所有消息被处理完毕后,就可以将其释放回内存池,方便内存的管理。 -
skynet_mq_release:释放标记为释放状态的消息队列。skynet_mq_release函数用于释放标记为释放状态的消息队列,方便内存的管理。
2. Actor 模型通信机制
-
struct skynet_context:Actor 的上下文结构体,包括 Actor 的名字、消息队列等信息。skynet_context结构体是 Actor 的上下文结构体,包括 Actor 的名字、消息队列等信息,用于管理 Actor 的状态和各种信息。 -
skynet_context_new:创建一个新的 Actor,并初始化其上下文结构体。skynet_context_new函数用于创建一个新的 Actor,并初始化其上下文结构体,方便 Actor 的管理和使用。 -
skynet_context_release:释放 Actor 的上下文结构体。skynet_context_release函数用于释放 Actor 的上下文结构体,方便内存的管理。 -
skynet_context_push:向 Actor 的消息队列中添加一条消息。skynet_context_push函数用于向 Actor 的消息队列中添加一条消息,方便消息的传递和处理。 -
skynet_context_pop:从 Actor 的消息队列中弹出一条消息。skynet_context_pop函数用于从 Actor 的消息队列中弹出一条消息,方便消息的处理。 -
skynet_context_handle_message:处理 Actor 的消息队列中的所有消息。skynet_context_handle_message函数用于处理 Actor 的消息队列中的所有消息,方便消息的统一处理。 -
skynet_send:向指定的 Actor 发送一条消息,该函数会查找目标 Actor 并向其消息队列中添加一条消息。skynet_send函数用于向指定的 Actor 发送一条消息,该函数会查找目标 Actor 并向其消息队列中添加一条消息,方便消息的传递和处理。
3. 消息的传递过程
-
发送方使用
skynet_send向指定的 Actor 发送一条消息。 -
Skynet 根据消息的目标 Actor 查找其上下文结构体,并向其消息队列中添加一条消息。
-
接收方使用
skynet_context_pop从自己的消息队列中弹出一条消息,并进行处理。
日志模块#
- 记录各个服务的运行状态、错误信息等。
内存管理#
Skynet 的内存管理模块采用了透明的内存池实现,可以有效地减少内存分配和释放的频率,提高内存使用效率。具体分析如下:
- 内存池的结构
struct mem_data:内存池中的一个块的结构体,包括前后链接、大小等信息。struct mem_env:内存池的管理结构体,包括内存池的起始地址、结束地址、块的大小等信息。
- 内存池的创建
mem_init:初始化内存池的管理结构体,设置内存池的起始地址、结束地址、块的大小等信息。mem_alloc:从内存池中分配一定大小的内存,返回内存池中一个空闲块的指针。mem_free:将一块内存释放回内存池,将其标记为空闲块。
- 内存池的扩展
mem_newpool:在内存池管理结构体后面申请一块新的内存池,并将其加入内存池链表,以支持内存池的扩展。
- 内存池的使用
skynet_malloc:使用内存池分配一块指定大小的内存。skynet_calloc:使用内存池分配一块指定大小的内存,并将其清零。skynet_free:将一块指定的内存释放回内存池。
- 内存池的统计
mem_data_dump:输出内存池中各个块的使用情况。mem_report:输出内存池的总体情况,包括使用率、总大小、空闲块数等。
- 内存池的原理
内存池的实现原理是将一块连续的内存分成若干大小相等的块,用前后链接将这些块串联起来,空闲块用一个链表连接,已分配的块则不在链接中。内存池的管理结构体包括内存池的起始地址、结束地址、块的大小等信息。在内存池中分配和释放内存时,只需要简单地修改链表的指针即可,不需要进行内存的分配和释放操作,以达到快速的内存分配和释放效果。同时,内存池的使用还可以降低内存碎片的产生,提高内存使用效率。
高并发、高吞吐、高性能分析#
skynet_socket_poller使用了 I/O 多路复用模型,同时支持 epoll、kqueue、select 三种模式,可以有效地提高网络通信的并发处理能力。- Skynet 使用了非阻塞 I/O 模型,使用了底层操作系统提供的异步 I/O 函数,避免了 I/O 操作阻塞线程的情况,提高了系统的吞吐量和性能。
- Skynet 使用了消息队列作为 Actor 之间通信的数据结构,避免了线程之间的竞争和锁的使用,进一步提高了系统的并发性和性能。