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 1
和 event 2
指定为 tag1
,将 event 3
和 event 4
指定为 tag2
的示例。
event 1
总是先于event 2
执行。event 3
总是先于event 4
执行。但是,
event 1 + event 2
和event 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
在非事件的情况下,有服务器的 Install
或 Start
函数、
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_profiler
为 true
,且 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_profiler
为 true
,且 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)