What exactly does Yunfeng’s skynet talk about?

Yunfeng’s skynet is defined as a game server framework, implemented with c + lua based on the Actor model. The code is extremely streamlined, with only about 3,000 lines of code in part c.
The core problem to be solved by the entire skynet framework is to send a message (data packet) from one service (Actor) to another service (Actor), and receive its return. That is, a service in the same process (the author also emphasizes that it is not limited to the same process, because there may be communication between clusters) a service that calls another service in the same process through a similar rpc and receives the processing result. Skynet deals with the rules and correctness of sending data packets between these services.
The core layer of skynet is all realized by c.
When the system starts, it will get a pre-allocated node id, which we call the harbor id. This id is used by the cluster. Many skynet nodes can be started in a cluster, and each node will be assigned a unique id.
There are many services in a node (that is, a process), and services can be temporarily understood as functional modules in a narrow sense.
When a service is initialized, a skynet_context is generated as an instance of the service; a unique (even in a cluster) service handle, that is, the unique id of the service, is used to identify the service; a message queue message_queue; also Register a callback with the framework, and when the service receives a message sent, it is passed in through this method.
The code to initialize a service is as follows:

struct skynet_context *
skynet_context_new(const char * name, const char *param) {
    
    
     // 装载模块
     struct skynet_module * mod = skynet_module_query(name);

     if (mod == NULL)
          return NULL;

     void *inst = skynet_module_instance_create(mod);
     if (inst == NULL)
          return NULL;
     // 初始化skynet_context实例
     struct skynet_context * ctx = skynet_malloc(sizeof(*ctx));
     CHECKCALLING_INIT(ctx)

     ctx->mod = mod;
     ctx->instance = inst;
     ctx->ref = 2;
     ctx->cb = NULL;
     ctx->cb_ud = NULL;
     ctx->session_id = 0;
     ctx->logfile = NULL;

     ctx->init = false;
     ctx->endless = false;
     // 初始化服务handle
     // Should set to 0 first to avoid skynet_handle_retireall get an uninitialized handle
     ctx->handle = 0;    
     ctx->handle = skynet_handle_register(ctx);
     // 初始化消息队列
     struct message_queue * queue = ctx->queue = skynet_mq_create(ctx->handle);
     // init function maybe use ctx->handle, so it must init at last
     context_inc();

     CHECKCALLING_BEGIN(ctx)
     int r = skynet_module_instance_init(mod, inst, ctx, param);
     CHECKCALLING_END(ctx)
     if (r == 0) {
    
    
          struct skynet_context * ret = skynet_context_release(ctx);
          if (ret) {
    
    
               ctx->init = true;
          }
          skynet_globalmq_push(queue);
          if (ret) {
    
    
               skynet_error(ret, "LAUNCH %s %s", name, param ? param : "");
          }
          return ret;
     } else {
    
    
          skynet_error(ctx, "FAILED launch %s", name);
          uint32_t handle = ctx->handle;
          skynet_context_release(ctx);
          skynet_handle_retire(handle);
          struct drop_t d = {
    
     handle };
          skynet_mq_release(queue, drop_message, &d);
          return NULL;
     }
}
 在skynet_handle_register方法中生成一个服务handle,handle是一个32位的整数,在生成handle的时候,是把该节点的harbor id写到了handle的高8位里面,所以一个服务的handle,就可以知道这个服务是哪个节点的。
   s->handle_index = handle + 1;
      rwlock_wunlock(&s->lock);
      handle |= s->harbor;
      return handle;   

Therefore, the highest harbor id is only 256, which means that the skynet cluster can only have 256 nodes at most, and a node can only have 24 services at most, that is, 1.6M. Because a handle is a 32-bit integer, the upper 8 bits are used to store the harbor id, and only the lower 24 bits are used to allocate the handle of the node.
The message queue message_queue is used to store messages sent to the service. All messages sent to the service must first be pressed into the message queue of the service.

 服务启动起来了,来看看数据包是如何从一个服务发送给另一个服务的。
 来看看 skynet_send 和 callback 函数的定义:
int skynet_send(
  struct skynet_context * context,
  uint32_t source,
  uint32_t destination,
  int type,
  int session,
  void * msg,
  size_t sz
);

typedef int (*skynet_cb)(
  struct skynet_context * context,
  void *ud,
  int type,
  int session,
  uint32_t source ,
  const void * msg,
  size_t sz
);

The source and destination are the handles of the sender and receiver respectively.
The type is the protocol
session used by the sender and receiver to process data packets to identify the password for this call. After the sender sends a message, it retains the session so that when the response packet is received, it can identify which call it is.
msg/sz is the content and length of the data packet, used in pairs

Skynet's message dispatch

Skynet maintains a two-level message queue.

Each service entity has a private message queue, in which messages are sent to it one by one. The message consists of four parts:

struct skynet_message {
    
    
    uint32_t source;
    int session;
    void * data;
    size_t sz;
};

To send a message to a service is to press such a message body into the private message queue of the service. The value of this structure is copied into the message queue, but the message content itself is not copied.

Skynet maintains a global message queue, which contains non-empty secondary message queues.

When Skynet is started, several worker threads (the number is configurable) are established, and they continuously take out a secondary message queue from the main message queue, then take a message from the secondary queue, and call the callback function of the corresponding service Carry it out. In order to call fairness, only process one message at a time, instead of consuming all messages (although that part is more efficient because it reduces the number of query service entities and the number of times the main message queue enters and exits), so that no service will be starve.

In this way, skynet realizes that a message (data packet) is sent from one service to another.

Recommend to everyone a training camp about the actual combat of the skynet project. Registration is now equivalent to free. The main content:

Multi-core concurrent programming
message queue. Thread pool
actor message scheduling
Network module implementation
Time wheel timer implementation
lua/c/interface programming
Skynet programming essentials
demo demonstrates actor programming thinking

Address: Click to watch the video.
More skynet information plus group: 832218493 Get it for free!
Insert picture description here

Guess you like

Origin blog.csdn.net/lingshengxueyuan/article/details/112273489