Skip to content

09 Zinx Async

刘丹冰 edited this page May 12, 2023 · 3 revisions

Case Source Code : https://github.com/aceld/zinx-usage/tree/main/zinx_async_op

1. Why are messages processed asynchronously?

Zinx's business threads are designed as a Worker Pool, where the number of workers handling business logic is controlled by WorkerPoolSize, which can be understood as the number of threads. Now, if at a certain point in time, one worker's logic contains a blocking operation, it will cause a delay in processing subsequent request messages by that worker. If all workers have long blocking logic, the server will struggle to handle client request messages in a timely manner. Therefore, it is recommended that the game business logic processed in the Worker Pool is generally non-blocking and operates purely in memory. If there are blocking operations, Zinx provides an asynchronous message processing module called Zasync_op.

2. Zasync_op

After Zasync_op processes asynchronous messages, they are returned to the original worker handling the business logic. This design avoids potential issues of dirty reads and writes in the business logic.

1111222

3. Usage Steps

3.1 Call Async Process

func AsyncUserSaveData(request ziface.IRequest) *zasync_op.AsyncOpResult {

    opId := 1 // Player's unique identifier Id
    asyncResult := zasync_op.NewAsyncOpResult(request.GetConnection())

    zasync_op.Process(
        int(opId),
        func() {
            // Perform db operation
            user := db_model.SaveUserData()

            // Set the asynchronous return result
            asyncResult.SetReturnedObj(user)
        },
    )

    return asyncResult
}

3.2 Set Asynchronous Return Result SetReturnedObj (optional)

Set the asynchronous return result asyncResult.SetReturnedObj in the asynchronous processing function zasync_op.Process.

3.3 Register the Async Callback Function OnComplete (optional)

// Async callback
asyncResult.OnComplete(func() {
    zlog.Debug("OnComplete IN===>333")
    returnedObj := asyncResult.GetReturnedObj()
    if returnedObj == nil {
        zlog.Debug("The asynchronous result has not been set yet when registering the callback function")
        return
    }

    user := returnedObj.(*db_model.UserModel)

    userLoginRsp := &msg_struct.MsgLoginResponse{
        UserId:    user.UserId,
        UserName:  user.Name,
        ErrorCode: 0,
    }

    marshalData, marErr := json.Marshal(userLoginRsp)
    if marErr != nil {
        zlog.Error("LoginRouter marErr", marErr.Error())
        return
    }

    // Send response to the client
    conn := request.GetConnection()
    if sendErr := conn.SendMsg(1, marshalData); sendErr != nil {
        zlog.Error("LoginRouter sendErr", sendErr.Error())
        return
    }
    zlog.Debug("OnComplete OUT===>333")
})

The async callback function asyncResult.OnComplete and the asynchronous return result asyncResult.SetReturnedObj are only necessary if there are additional business logic to be processed after the async operation. If you simply want the logic to execute asynchronously, calling zasync_op.Process is sufficient.