A Preliminary Study of PHP socket-Firmly Continue libevent (2)

  Asynchronous, libevent, Non-blocking, php, socket

[Original Address:https://blog.ti-node.com/blog …]

In fact, there is an instruction manual for the event extension in php.net, but it is of little use to beginners, because there are not too many strong case codes and User Contributed Notes, so the possible result is that they cannot understand it at all.

This is the event document.click hereYou can feel it. From the document, the Event extension has implemented several basic classes as shown in the following figure. among them, the most commonly used and important classes are event, EventBase and EventConfig. therefore, we will first work around these three classes.

Considering that most of you, me and other unknown species who are reading this article may not be veterans of C language, I have to try to introduce these concepts with some cases and metaphors that may not be appropriate.

Five letters in libevent are event, which actually means “event is king”.

The Event class is a generator that generates various types of events, such as timer events, reading and writing events, etc. In order to enhance national honor, we compare these various events to various fighter planes: J-10, J-15 and J-20.

The EventBase class is relatively easy to get involved. This thing is obviously an aircraft carrier. In order to enhance national honor, we regard the EventBase class as a Liaoning ship. All kinds of Event must rely on EventBase to make a living. This is the same reason that fighter planes have the strength to fly higher and farther with the Liaoning warship. There must be an aircraft carrier (EventBase) first, followed by a fighter (Event) hanging on the aircraft carrier (EventBase).

EventConfig is a configuration class, and the instantiated object can be passed to the EventBase class as a parameter, so different EventBase instances will be initialized according to this configuration when initializing the EventBase class. In analogy, this class is somewhat similar to the ship island of the Liaoning ship, and can be configured to command the entire Liaoning ship. The development trend of aircraft carriers does not require ship islands. Similarly, when instantiating the EventBase class, it is also possible to directly instantiate without passing in the EventConfig object.

Let’s start writing a php timer to step into the rhythm of the code. Timer is a commonly used tool. When phper talks about timer, the first thing in my mind is crontab in Linux. Is it true that phper can’t get along without crontab? Yes, it’s really a shame. The reality tells us that this is the way it is. They can’t get along without crontab. So, it’s time to make a wave of timers through pure php!

Note that it is really pure php, which doesn’t even need the Event extension.

// 给当前php进程安装一个alarm信号处理器
// 当进程收到alarm时钟信号后会作出动作
pcntl_signal( SIGALRM, function(){
  echo "tick.".PHP_EOL;
} );
// 定义一个时钟间隔时间,1秒钟吧
$tick = 1;
while( true ){
  // 当过了tick时间后,向进程发送一个alarm信号
  pcntl_alarm( $tick );
  // 分发信号,呼唤起安装好的各种信号处理器
  // 睡个1秒钟,继续
  sleep( $tick );

The code is saved as timer.php, and then PHP timer.php runs it. If there is no problem, it should be able to run. However, there is a problem with this code.

  • First, the performance is average (however, it is much better than using declare(ticks=1)
  • The second is that the amount of code is really short, which makes people doubt: can this special thing work?
  • Finally, even if I use it crustily, it can only be accurate to the second level. Are you kidding me?

Therefore, in order to solve the above problems, it is time to operate a wave of Event extensions!

// 初始化一个EventConfig(舰岛),虽然是个仅用于演示的空配置
$eventConfig = new EventConfig();
// 根据EventConfig初始化一个EventBase(辽宁舰,根据舰岛配置下辽宁舰)
$eventBase = new EventBase( $eventConfig );
// 初始化一个定时器event(歼15,然后放到辽宁舰机库中)
$timer = new Event( $eventBase, -1, Event::TIMEOUT | Event::PERSIST, function(){
  echo microtime( true )." : 歼15,滑跃,起飞!".PHP_EOL;
} );
// tick间隔为0.05秒钟,我们还可以改成0.5秒钟甚至0.001秒,也就是毫秒级定时器
$tick = 0.05;
// 将定时器event添加(将歼15拖到甲板加上弹射器)
$timer->add( $tick );
// eventBase进入loop状态(辽宁舰!走你!)

Save the code as tick.php, and then PHP tick.php will execute it, as shown in the following figure:

This timer is a persistent timer (it must be executed every x times). If you want a one-time timer (it will be executed every x times and will not be executed again after execution), then you can modify “Event::TIMEOUT | Event::PERSIST” in the above code to “Event::TIMEOUT”.

If you have some custom user data passed to the callback function, you can use the fifth parameter of new Event (), which can be used by the callback function as follows:

$timer = new Event( $eventBase, -1, Event::TIMEOUT | Event::PERSIST, function() use( &$custom ){
  //echo microtime( true )." : 歼15,滑跃,起飞!".PHP_EOL;
  print_r( $custom );
}, $custom = array(
  'name' => 'woshishui',
) );

What needs to be emphasized is that the new Event () line of code, I posted the prototype to show you:

public Event::__construct ( EventBase $base , mixed $fd , int $what , callable $cb [, mixed $arg = NULL ] )
  • The first parameter is an eventBase object
  • The second parameter is a file descriptor, which can be a listening socket, a connection socket, a fopen open file or a stream stream, etc. If it is clock time, then -1 is passed in. If it is other signal events, use corresponding signal constants, such as SIGHUP, SIGTERM, etc.
  • The third parameter represents the event type, followed by Event::READ, Event::WRITE, Event::SIGNAL, Event::TIMEOUT. Among them, the addition of Event::PERSIST means that it is permanent, rather than just once and never respond. For example, event:: read | event:: persistent means that a file description occurs once when it is first readable, and it will continue to occur again if it is readable again later.
  • The fourth parameter is very familiar, that is, event callback, which means what should be done after an event occurs
  • The fifth parameter is custom data, which will be passed to the callback function of the fourth parameter. This data can be used in the callback function.

The above case codes can summarize the daily process:

  1. Create EventConfig (not required)
  2. Create EventBase
  3. Create Event
  4. Suspending the Event means that the add method of the Event object is executed. if the add method is not executed, the event object cannot be suspended and will not be executed.
  5. Executes EventBase into a loop, which is the loop method.

After clarifying the timer code, we tried to solve the problem of a signal. For example, our process is daemon of memory resident, and will make corresponding actions after receiving a certain signal. For example, after receiving the term signal, the process will exit, and after receiving the usr1 signal, it will reload, etc.

// 依然是照例行事,尽管暂时没什么实际意义上的配置
$eventConfig = new EventConfig();
// 初始化eventBase
$eventBase = new EventBase( $eventConfig );
// 初始化event
$event = new Event( $eventBase, SIGTERM, Event::SIGNAL, function(){
  echo "signal term.".PHP_EOL;
} );
// 挂起event对象
// 进入循环
echo "进入循环".PHP_EOL;

Save the code as a tick.php, and then execute php tick.php. The code has entered a loop. Then we open another terminal, enter Psaux | GreTick to view the pid process number of a PHP process, and send a term signal to this process, as shown in the following figure:

It is strange to see from the first picture that the term signal has been received, but it is strange why the php process has exited. Because Event::PERSIST was not added, the following code was modified:

$event = new Event( $eventBase, SIGTERM, Event::SIGNAL | Event::PERSIST, function(){
  echo "signal term.".PHP_EOL;
} );

Some of them are very clever. IO multiplexing methods include three select, poll and EPOLL (called kqueue under MAC). Which method is used for our current event extension? Then, perform another wave:

// 查看当前系统平台支持的IO多路复用的方法都有哪些?
$method = Event::getSupportedMethods();
print_r( $method );
// 查看当前用的方法是哪一个?
$eventBase = new EventBase();
echo "当前event的方法是:".$eventBase->getMethod().PHP_EOL;
// 跑了许久龙套的config这次也得真的露露手脚了
$eventConfig = new EventConfig;
// 避免使用方法kqueue
// 利用config初始化event base
$eventBase = new EventBase( $eventConfig );
echo "当前event的方法是:".$eventBase->getMethod().PHP_EOL;

After saving the code and executing it, you can see the results as shown in the following figure:

Then, there are still some more chicken thieves who continue to ask questions. How to confirm the aforementioned edge trigger and horizontal trigger? Since both epoll and kqueue are used, edge triggering must be used.

$base = new EventBase();
echo "特性:".PHP_EOL;
$features = $base->getFeatures();
// 看不到这个判断条件的,请反思自己“位运算”相关欠缺
if( $features & EventConfig::FEATURE_ET ){
  echo "边缘触发".PHP_EOL;
if( $features & EventConfig::FEATURE_O1 ){
  echo "O1添加删除事件".PHP_EOL;
if( $features & EventConfig::FEATURE_FDS ){
  echo "任意文件描述符,不光socket".PHP_EOL;

The operation results are shown in the following figure:

To sum up, today’s content is about the three basic categories of event, and the next chapter is still about the combination of these three guys and IO operations.

[Original Address:https://blog.ti-node.com/blog …]


[Original Address:https://blog.ti-node.com/blog …]