源码地址: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 之间通信的数据结构,避免了线程之间的竞争和锁的使用,进一步提高了系统的并发性和性能。