11. 事件

iFun引擎是一种event-driven方式的framework。即,利用iFun引擎编写的大部分代码均作为事件来执行。

本章介绍了事件的运行方式。

11.1. 什么是事件?

事件大体上分为引擎事件和用户自定义事件。引擎事件会注册网络、定时器等发生引擎内部事件时将要执行的处理器,进行应用。(请分别参考 从会话接收数据包定时器 )用户事件使用 Event::Invoke() 来创建事件。

事件通过一个以上的事件线程进行处理。各个事件按照 事件执行顺序和事件标签 中所介绍的内容,被赋予标签 ,标签相同的事件会被依次处理,标签不同的事件则并行处理。

对在一个事件中产生的与ORM有关的object变更事项,在事件函数结束时,作为一个事务(transaction)被批处理。 (具体内容请参考 ORM Part 1: 概要事务 。)

11.2. 事件种类

11.2.1. 引擎事件

它是在引擎中发生的事件, network message handler, timer handler ,以及引擎各功能的回调(Leaderboard、Authenticator、Biller、HttpClient、Reids等)均属于引擎事件。

注册各事件的处理器或回调函数后,引擎在需要时会调用相应函数。

Important

Rpc::Call()Mariadb::ExecuteQuery()callback 不包含在事件中。

11.2.2. 用户自定义事件

必要时,可随时利用 Event::Invoke() 来创建事件。和引擎事件一样,该函数中作为因子传输的handler也通过被事件线程调用的方式运行。用户自定义事件的目的是为了便于开发,或是为了提升性能。

Event::Invoke() 함수의 인자로 이벤트로 실행할 함수를 전달하면 해당 함수는 이벤트로 실행됩니다.

#include <funapi.h>

void event_function_a() {
  LOG(INFO) << "event_function_a";
  ...
}

void event_function_b(int param1, string param2) {
  LOG(INFO) << "event_function_b";
  ...
}

void some_function() {
  ...
  // 예제 1
  Event::Invoke(&event_function_a);
  ...
  // 예제 2
  // boost::bind 를 참고 하시기 바랍니다.
  Event::Invoke(bind(&event_function_b, 1234, "hello?"));
  ...

  // 예제 3
  // C++ 1x 를 사용하면 아래처럼 lambda 함수를 이용할 수 있습니다.
  auto event_function_c = []() {
    LOG(INFO) << "event_function_c";
    ...;
  };
  Event::Invoke(event_function_c);

  // 예제 4
  // lambda 함수는 아래처럼 표현할수 도 있습니다.
  Event::Invoke([]() {
    LOG(INFO) << "event_function_d";
    ...;
  });
}

Event.Invoke() 함수의 인자로 이벤트로 실행할 함수를 전달하면 해당 함수는 이벤트로 실행됩니다.

using funapi;

...

public void EventFunctionA()
{
  Log.Info ("EventFunctionA");
}

public void EventFunctionB(int param1, string param2)
{
  Log.Info ("EventFunctionB");
}

public void SomeFunction()
{
  ...
  // 예제 1
  Event.Invoke (EventFunctionA);

  // 예제 2
  // lambda 함수를 이용하는 방법입니다.
  Event.Invoke (() => {
    int integer_value = 1;
    string hello_string = "hello";
    EventFunctionB(integer_value, hello_string);
  });

  // 예제 3
  // delegate를 이용하여 lambda 함수를 이용할 수 있습니다.
  Event.EventFunction lambda_function = () => {
    Log.Info ("lambda function");
  };
  Event.Invoke (lambda_function);
}

11.3. 事件执行顺序和事件标签

Event默认按照发生的顺序进入队列中。然而,由于负责事件的线程有1个以上,因此事件的执行是并行产生的。(对于事件线程个数的调整,请参考 事件功能设置参数 。)

它在须要保证事件处理顺序的环境中可能会较为困难。所以iFun引擎支持将须要保证处理顺序的事件通过相同标签进行捆绑的功能。若为事件指定了标签,则相同tag的event可按顺序依次执行,不同tag的event可并行执行。

例如,网络事件会自动使用不同session的独立tag,在session单元中保证了message的处理顺序,但与其他session是并行运行。

以下代码是将 event 1event 2 指定为 tag1 ,将 event 3event 4 指定为 tag2 的示例。

  • event 1 总是先于 event 2 执行。

  • event 3 总是先于 event 4 执行。

  • 但是, event 1 + event 2event 3 + event 4 是并行处理的,所以我们无法知道哪一个先执行。

#include <funapi.h>

EventTag my_tag_1 = RandomGenerator::GenerateUuid();
EventTag my_tag_2 = RandomGenerator::GenerateUuid();

function<void(string)> my_event = [](string event_msg) {
  LOG(INFO) << event_msg;
};

// tag 1
Event::Invoke(bind(my_event, "event1"), my_tag_1); // event 1
Event::Invoke(bind(my_event, "event2"), my_tag_1); // event 2

// tag 2
Event::Invoke(bind(my_event, "event3"), my_tag_2); // event 3
Event::Invoke(bind(my_event, "event4"), my_tag_2); // event 4
using funapi;


public void MyEvent(string event_msg)
{
  Log.Info ("my_event called. " + event_msg);
};

public void Example()
{
  System.Guid my_tag_1 = RandomGenerator.GenerateUuid();
  System.Guid my_tag_2 = RandomGenerator.GenerateUuid();

  // tag 1
  Event.Invoke (() => { MyEvent ("event1"); }, my_tag_1);
  Event.Invoke (() => { MyEvent ("event2"); }, my_tag_1);

  // tag 2
  Event.Invoke (() => { MyEvent ("event3"); }, my_tag_2);
  Event.Invoke (() => { MyEvent ("event4"); }, my_tag_2);
}

11.3.1. 引擎事件的标签

网络: 各个网络session把 session id 值作为事件标签执行。因此,各个session是独立的,每个session中的顺序得以保证。

定时器: 均通过一个事件标签执行。所以定时器处理器保证了按事件顺序依次调用。

其他: 除此以外,其他引擎的处理器或回调均通过随机生成的标签并行执行。

11.3.2. 事件标签的自动赋予规则

在通过 Event::Invoke() 执行事件时,若省略标签因子,引擎会分配标签。此时,根据事件创建位置的不同,运用以下规则。

11.3.2.1. 在处理事件过程中创建新事件时

若在处理事件的过程中想要创建新的事件时,会继承正在执行的事件的标签。例如,在网络消息处理器中创建新的事件时,将继承网络消息处理器的标签。对于前面的网络情况,由于已将session id作为事件标签,所以session id也会成为新建事件的标签。

Important

在定时器处理器中创建新的事件时

如前所述,若在事件处理过程中想要创建新的事件,会继承正在处理的事件的标签。但定时器例外, 定时器 , 服务器管理Part 1: 添加RESTful APIs 会被随机赋予事件标签。

这是由于 定时器 , 服务器管理Part 1: 添加RESTful APIs 事件会被赋予相同的事件标签,所以当新建的事件继承标签时,即使与定时器没有相关性,也会与定时器事件serialization。

在定时器处理器中,当新建的事件为新的定时器时,该事件将被赋予定时器上的固定标签。

以下示例代码的4个事件函数均继承了网络事件的标签值,即继承了接收相应消息的session的 session id 。 所以,它保证了OnMessage -> MessageProcess1 -> MessageProcess2 -> MessageProcess3 的处理顺序。

// 아래 OnMessage 함수는 서버 Install 함수에서
// HandlerRegstiry::Register(..., OnMessage) 로 등록된 메시지 핸들러
void OnMessage(const Ptr<Session> &session, const Json &message) {
  ...
  // 이벤트 태그 인자 생략
  Event::Invoke(bind(MessageProcess1, session, message));
  Event::Invoke(bind(MessageProcess2, session, message));
}

void MessageProcess1(const Ptr<Session> &session, const Json &message) {
  // 이 이벤트의 이벤트 태그는 session->id() 와 같습니다.
  ...
  // 이벤트 태그 인자 생략
  Event::Invoke(bind(MessageProcess3, session, message));
}

void MessageProcess2(const Ptr<Session> &session, const Json &message) {
  // 이 이벤트의 이벤트 태그는 session->id() 와 같습니다.
  ...
}

void MessageProcess3(const Ptr<Session> &session, const Json &message) {
  // 이 이벤트의 이벤트 태그는 session->id() 와 같습니다.
  ...
}
// 아래 OnMessage 함수는 서버 Install 함수에서
// HandlerRegstiry.Register(..., OnMessage) 로 등록된 메시지 핸들러
public static void OnMessage(Session session, JObject message)
{
  ...
  // 이벤트 태그 인자 생략
  Event.Invoke (() => {
    MessageProcess1 (session, message);
  });
  Event.Invoke (() => {
    MessageProcess2 (session, message);
  });
}

public static void MessageProcess1(Session session, JObject message)
{
  // 이 이벤트의 이벤트 태그는 session.Id 와 같습니다.
  ...
  // 이벤트 태그 인자 생략
  Event.Invoke (() => {
    MessageProcess3 (session, message);
  });
}

public static void MessageProcess2(Session session, JObject message)
{
  // 이 이벤트의 이벤트 태그는 session.Id 와 같습니다.
  ...
}

public static void MessageProcess3(Session session, JObject message)
{
  // 이 이벤트의 이벤트 태그는 session.Id 와 같습니다.
  ...
}

11.3.2.2. 在非事件处理器的位置创建新事件时

此时,将被赋予随机生成的事件标签。如前所述,随机的事件标签意味着可以并行处理事件。

Tip

在非事件的情况下,有服务器的 InstallStart 函数、 Rpc::Call()Mariadb::ExecuteQuery()callback 或在用户随机创建的线程中执行的代码等。

以下示例是在非事件的位置创建100个事件,赋予随机生成的事件标签的情况。各个事件获得了随机的事件标签,所以并行处理,但不保证执行顺序。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 아래 Start 함수는 엔진이 초기화를 위해 Install 함수 다음에 불러주는
// 함수이며 이벤트가 아닙니다.
bool Start() {
  for (size_t i = 0; i < 100; ++i) {
    auto my_event = [i]() {
      LOG(INFO) << i;
    };

    Event::Invoke(my_event);
  }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
delegate void MyEvent(int idx);

...

bool Start()
{
  MyEvent my_event = (int number) => {
    Log.Info (number.ToString());
  };

  for (int i = 0; i < 100; ++i) {
    int idx = i;
    Event.Invoke (() => {
      my_event(idx);
    });
  }
}

11.3.3. (示例)应用事件标签串行化的方法

由于在拥有同一事件标签的各事件之间,顺序是被保证的,所以无需Lock,直接串行处理即可。使用事件标签,可最大程度减少lock的使用,提升服务器的性能。更详细的内容请参考 Cookbook 1: 制作基于房间的MO游戏

예제 코드는 전역 변수를 업데이트하는 코드입니다. 여러 스레드에서 하나의 값을 변경하는 것은 오류를 만들기 때문에 이벤트 태그를 이용하여 순차적으로 처리되도록 할 수 있습니다.

// 전역변수
int64_t the_counter = 0;
EventTag the_counter_tag;

// 전역변수 초기화(서버 Start 함수)
bool Start() {
  // the_counter 변수 접근을 위한 이벤트 태그를 무작위로 하나 생성
  the_counter_tag = RandomGenerator::GenerateUuid();
  return true;
}

void UpdateCounter() {
  // ++ 연산은 atomic 하지 않아 여러 스레드에서 동시에 수행되면
  // the_counter 값은 기대한 값 보다 작은 값을 갖는 문제가 발생됩니다.
  ++the_counter;
}

// OnXXX 는 어느 곳에서든 불린다고 가정
void OnXXX(...) {
  // the_counter_tag 를 이벤트 태그로 지정
  Event::Invoke(UpdateCounter, the_counter_tag);
}

예제 코드는 클래스 내의 변수를 업데이트하는 코드입니다. 여러 스레드에서 하나의 값을 변경하는 것은 오류를 만들기 때문에 이벤트 태그를 이용하여 순차적으로 처리되도록 할 수 있습니다.

public class SomeClass
{
  static int TheCounter = 0;
  static System.Guid TheCounterTag;

  bool Start()
  {
    // TheCounter 변수 접근을 위한 이벤트 태그를 무작위로 하나 생성
    SomeClass.TheCounterTag = RandomGenerator.GenerateUuid ();
    return true;
  }

  void UpdateCounter()
  {
    // ++ 연산은 atomic 하지 않아 여러 스레드에서 동시에 수행되면
    // TheCounter 값은 기대한 값 보다 작은 값을 갖는 문제가 발생됩니다.
    SomeClass.TheCounter++;
  }

  // OnXXX 는 어느 곳에서든 불린다고 가정
  void OnXXX()
  {
    // TheCounterTag 를 이벤트 태그로 지정
    Event.Invoke (UpdateCounter, SomeClass.TheCounterTag);
  }
}

11.4. 事件性能分析与调试

iFun提供了对事件进行优化和调试的便利功能。

Note

CPU 성능 분석에 대한 내용은 CPU性能分析 을 참고하시기 바랍니다.

11.4.1. 为事件赋予调试专用名称

可使用以下函数设置事件名称。为事件命名后,输出日志时,事件名称也会一同输出,从而便于调试。

Tip

对于网络事件,引擎会运用消息(数据包)的类型自动命名,因此可以省略。

SetEventName()
Event.SetEventName()

如以下示例所示,最好在作为事件执行的函数的首行命名。

// invoke 함수로 실행되는 사용자 정의 이벤트
void my_event1() {
  SetEventName("my_event1");
  ...
}

// invoke 함수로 실행되는 사용자 정의 이벤트
auto my_event2 = []() {
  SetEventName("my_event2");
  ...
};

// timer event
void OnTimer(const Timer::Id &, const WallClock::Value &) {
  SetEventName("my timer event");
  ...
};

// 또는 컴파일러 매크로를 이용하여 아래와 같이 할 수도 있습니다.
void my_event3() {
  SetEventName(__func__);
  ...
}
delegate void MyEvent();

// invoke 함수로 실행되는 사용자 정의 이벤트
void MyEvent1()
{
  Event.SetEventName ("my_event1");
  ...
}

Event.EventFunction my_event2 = () => {
  Event.SetEventName ("my_event2");
  ...
};

Important

后续介绍的功能均是为事件命名后才能使用的功能。

11.4.2. 感知瓶颈事件

在处理事件时,若所需时间超出 事件功能设置参数slow_event_log_threshold_in_ms ,将输出如下警告日志。出现该日志时,建议检查相应事件处理器的实现情况。

Slow event: event_name={이벤트이름}, event_id={이벤트아이디}, execution_time={처리시간}

11.4.3. 强制取消严重的瓶颈事件

为了预防部分事件导致整个系统变得缓慢,在处理事件时,若所需时间超出 事件功能设置参数event_timeout_in_ms ,将输出如下警告日志,除了相应事件以外,与相应事件具有相同标签的所有事件的处理均会被取消。

Event timeout: event_name={이벤트이름}, event_id={이벤트아이디}, event_tag={이벤트태그}

事件被强制取消后,如下所示,可通过处理器接收通知。

// 서버의 Install 함수
static bool Install(const ArgumentMap &arguments) {
  ...
  Event::RegisterTimeoutHandler(OnEventTimeout);

  return true;
}

void OnEventTimeout(const string &event_name, const EventId &event_id,
                    const EventTag &event_tag,
                    const Ptr<Session> &associated_session) {
  // 만약 해당 event 가 session message handler 에서 파생된 event 라면
  // associated_session 로 해당 session 이 전달됩니다.
  if (associated_session) {
    Event::Invoke(bind(OnLogout, associated_session));
  }
}

void OnLogout(const Ptr<Session> &session) {
  ...
}

C# 버전은 추후 지원됩니다.

Important

为了免受瓶颈事件的影响,超时处理器会在其他线程中处理,而不是在事件线程中处理。但是,如果有须要在事件线程中处理的事件,则须使用 Event::Invoke

Tip

若相应事件是与session有关的事件,根据事件标签赋予规则,来自于相应Session的所有消息均会受到影响。这意味着session再也不会运行,因此须要在事件超时处理器中进行强制退出处理。

11.4.4. 感知事件线程hang

若将 事件功能设置参数enable_event_thread_checker 设置为 true ,则在事件线程停止30秒以上时,无论何种停止原因,均会以1分钟为间隔输出如下日志。此时,建议查看相应事件处理器的体现内容中是否有死锁、无限循环或非正常的长时间处理作业等。

event thread hang: event_thread_index=..., event_name={이벤트이름}, event_id={이벤트아이디}, event_tag={이벤트태그}, elapsed_time_in_sec=...

11.4.5. 事件性能分析: 概要信息

Important

该功能在 事件功能设置参数enable_event_profilertrue ,且 ApiService 功能打开时运行。

iFun引擎提供为整个事件系统统计处理时间的功能。此时,因超时而停止处理的事件则除外。

调用如下方式提供的API,用于查看统计。

GET http://{ip}:{api-service-port}/v1/counters/funapi/event/profiling/summary/

若调用上述API,则返回JSON,JSON属性中 all_time 表示累积值, last1min 表示最近1分钟之前的统计。两种情况所有统计结果中包括如下项目。

关于执行所需时间:

  • count: 已执行事件的总个数

  • execution_time_mean_in_sec: 平均执行时间

  • execution_time_stdev_in_sec: 执行时间的标准偏差

  • execution_time_max_in_sec: 最长执行时间

关于I/O待机时间:

  • io_wait_time_mean_in_sec: 平均待机时间(通过DB、Zookeeper、Lock争用等)

  • io_wait_time_stdev_in_sec: 待机时间标准偏差

  • io_wait_time_max_in_sec: 最长待机时间

关于事件队列:

  • queue_time_mean_in_sec: 事件队列中停留的平均时间

  • queue_time_stdev_in_sec: 事件队列中停留时间的标准偏差

  • queue_time_max_in_sec: 事件队列中停留的最长时间

Note

execution_time = queue_time + io_wait_time + 事件处理器的处理时间

{
    "all_time": {
        "count": 8814,
        "execution_time_mean_in_sec": 0.007857,
        "execution_time_stdev_in_sec": 0.023191,
        "execution_time_max_in_sec": 0.309402,
        "io_wait_time_mean_in_sec": 0.005639,
        "io_wait_time_stdev_in_sec": 0.017964,
        "io_wait_time_max_in_sec": 0.247697,
        "queue_time_mean_in_sec": 0.000953,
        "queue_time_stdev_in_sec": 0.005887,
        "queue_time_max_in_sec": 0.106234
    },
    "last1min": {
        "count": 5882,
        "execution_time_mean_in_sec": 0.009843,
        "execution_time_stdev_in_sec": 0.028,
        "execution_time_max_in_sec": 0.309402,
        "io_wait_time_mean_in_sec": 0.007114,
        "io_wait_time_stdev_in_sec": 0.021708,
        "io_wait_time_max_in_sec": 0.247697,
        "queue_time_mean_in_sec": 0.001377,
        "queue_time_stdev_in_sec": 0.007167,
        "queue_time_max_in_sec": 0.106234
    }
}

11.4.6. 事件性能分析: 详细信息

Important

该功能在 事件功能设置参数enable_event_profilertrue ,且 ApiService 功能打开时运行。

除了整体事件的概要信息以外,还提供各个事件的处理时间及 ORM 相关统计。

调用以下URL,查看统计。

GET http://{ip}:{api-service-port}/v1/counters/funapi/event/profiling/all

JSON作为结果值返回,且JSON中包括按各个事件名称区分的统计值。包括按各个事件名称区分的 all_time 属性和 last1min 属性,它们分别表示累积统计和最近1分钟的统计。各个统计项目的涵义如下。

关于执行所需时间:

  • execution_count: 执行次数

  • rollback_count_mean: 平均回滚次数

  • rollback_count_max: 最大回滚次数

  • execution_time_mean_in_sec: 平均执行时间

  • execution_time_stdev_in_sec: 执行时间的标准偏差

  • execution_time_max_in_sec: 最长执行时间

  • timeout_count: timeout处理次数

关于I/O待机时间:

  • io_wait_time_mean_in_sec: 平均待机时间(通过DB、Zookeeper、Lock争用等)

  • io_wait_time_stdev_in_sec: 待机时间标准偏差

  • io_wait_time_max_in_sec: 最长待机时间

关于事件队列:

  • queue_time_mean_in_sec: 事件队列中停留的平均时间

  • queue_time_stdev_in_sec: 事件队列中停留时间的标准偏差

  • queue_time_max_in_sec: 事件队列中停留的最长时间

关于ORM:

  • object_create_count_mean: 创建的object平均个数

  • object_count_mean: 已fetch的object平均个数(已Fetch,但不存在的情况除外)

  • object_cache_hit_rate_mean: fetch的object未经IO直接从in-memory cache获取的比率

    该值越高越好。该值也包括已Fetch,但不存在的情况,因此可以是负数。若该值较低,须要参照 贮藏数据 的说明,使object在cache中停留更长时间。

  • object_nolock_rate_mean: 通过 kReadCopyNoLock fetch的object的比率(已Fetch但不存在的情况除外)

  • object_lease_rate_mean: 通过非DB或cache的RPC,从其他服务器借用的object的比率。

    该值越低越好。(已Fetch但不存在的情况除外)

Note

execution_time = queue_time + io_wait_time + 事件处理器的处理时间

例)

{
    "OnGameServerLogin": {
        "all_time": {
            "execution_count": 1926,
            "rollback_count_mean": 1.0,
            "rollback_count_max": 4,
            "execution_time_mean_in_sec": 0.025312,
            "execution_time_stdev_in_sec": 0.07749,
            "execution_time_max_in_sec": 0.404292,
            "io_wait_time_mean_in_sec": 0.018308,
            "io_wait_time_stdev_in_sec": 0.057154,
            "io_wait_time_max_in_sec": 0.290297,
            "queue_time_mean_in_sec": 0.005993,
            "queue_time_stdev_in_sec": 0.022505,
            "queue_time_max_in_sec": 0.156082,
            "object_count_mean": 104.0,
            "object_cache_hit_rate_mean": 0.824,
            "object_nolock_rate_mean": 0.0,
            "object_lease_rate_mean": 0.175,
            "object_create_count_mean": 0.0,
            "timeout_count": 0
        },
        "last1min": {
            "execution_count": 56,
            "rollback_count_mean": 1.0,
            "rollback_count_max": 2,
            "execution_time_mean_in_sec": 0.001889,
            "execution_time_stdev_in_sec": 0.000936,
            "execution_time_max_in_sec": 0.007396,
            "io_wait_time_mean_in_sec": 0.000792,
            "io_wait_time_stdev_in_sec": 0.00048,
            "io_wait_time_max_in_sec": 0.004086,
            "queue_time_mean_in_sec": 0.000052,
            "queue_time_stdev_in_sec": 0.00002,
            "queue_time_max_in_sec": 0.000108,
            "object_count_mean": 97.0,
            "object_cache_hit_rate_mean": 0.965,
            "object_nolock_rate_mean": 0.0,
            "object_lease_rate_mean": 0.035,
            "object_create_count_mean": 0.0
        }
    },
    "OnListFriend": {
        "all_time": {
            "execution_count": 12136,
            "rollback_count_mean": 2.0,
            "rollback_count_max": 3,
            "execution_time_mean_in_sec": 0.000958,
            "execution_time_stdev_in_sec": 0.004036,
            "execution_time_max_in_sec": 0.321312,
            "io_wait_time_mean_in_sec": 0.0003,
            "io_wait_time_stdev_in_sec": 0.003198,
            "io_wait_time_max_in_sec": 0.319272,
            "queue_time_mean_in_sec": 0.000143,
            "queue_time_stdev_in_sec": 0.002402,
            "queue_time_max_in_sec": 0.086365,
            "object_count_mean": 11.9,
            "object_cache_hit_rate_mean": 0.899,
            "object_nolock_rate_mean": 0.837,
            "object_lease_rate_mean": 0.1,
            "object_create_count_mean": 0.0,
            "timeout_count": 0
        },
        "last1min": {
            "execution_count": 408,
            "rollback_count_mean": 2.0,
            "rollback_count_max": 3,
            "execution_time_mean_in_sec": 0.00053,
            "execution_time_stdev_in_sec": 0.00019,
            "execution_time_max_in_sec": 0.002214,
            "io_wait_time_mean_in_sec": 0.0,
            "io_wait_time_stdev_in_sec": 0.0,
            "io_wait_time_max_in_sec": 0.0,
            "queue_time_mean_in_sec": 0.000043,
            "queue_time_stdev_in_sec": 0.000082,
            "queue_time_max_in_sec": 0.001559,
            "object_count_mean": 11.9,
            "object_cache_hit_rate_mean": 1.0,
            "object_nolock_rate_mean": 0.838,
            "object_lease_rate_mean": 0.0,
            "object_create_count_mean": 0.0
        }
    },
    ...
}

11.5. 事件功能设置参数

可参照以下说明和 配置文件(MANIFEST.json)配置文件(MANIFEST.json)详情 ,来设置EventDispatcher。

  • event_threads_size: 主事件线程个数。(type=uint64, default=4)

  • enable_event_profiler: 事件性能分析器是否已被激活(type=bool, default=true)

几乎不需要直接更改设置的参数

  • slow_event_log_threshold_in_ms: 是否通过日志保存缓慢的事件(type=uint64, default=300)

  • event_timeout_in_ms: 在事件超时前所花费的时间,单位为毫秒 (type=uint64, default=30000)

  • enable_inheriting_event_tag: 若在调用 Event::Invoke() 时不指定event tag,是否继承已调用的event的event tag(type=bool, default=true)

  • enable_random_event_tag: 当 enable_inherit_event_tag 也未被激活时,是否在调用不含任何event tag的 Event::Invoke() 时随机创建并赋予event tag。若为false,则赋予null event tag(type=bool, default=true)

  • enable_event_thread_checker: 若为true,则会对event thread处理时是否发生blocking进行验证,且每秒验证1次(type=bool, default=true)

  • enable_outstanding_event_profiler: 是否对当前正在执行的事件进行性能分析(type=bool, default=true)