PHP interview encountered the interviewer's swoole cooperation process three times in a row. I'm crying!

What is a process?

A process is a startup instance of an application. Independent file resources, data resources and memory space.

What is a thread?

A thread belongs to a process and is the executor of a program. A process contains at least one main thread and can also have more sub threads. Threads have two scheduling strategies: time-sharing scheduling and preemptive scheduling.

My official Penguin Group

What is collaborative process?

A coroutine is a lightweight thread. A coroutine also belongs to a thread. A coroutine is executed in a thread. The scheduling of collaborative process is manually switched by the user, so it is also called user space thread. The creation, switching, suspension and destruction of the collaboration are all memory operations, and the consumption is very low. The scheduling strategy of collaborative process is cooperative scheduling.

Principle of Swoole synergy

  • Because Swoole4 is a single thread and multi process, only one co process is running in the same process at the same time.

  • The spoole server receives data and triggers the onReceive callback in the worker process to generate a Ctrip. Swoole creates a corresponding Ctrip for each request. Sub processes can also be created in a process.

  • The underlying implementation of a collaborative process is single threaded, so only one collaborative process is working at the same time, and the execution of the collaborative process is serial.

  • Therefore, when multitasking and multi cooperation processes are executed, when one cooperation process is running, other cooperation processes will stop working. When the current coroutine performs blocking IO operation, it will hang, and the underlying scheduler will enter the event loop. When there is an IO completion event, the underlying scheduler resumes the execution of the collaboration corresponding to the event.. Therefore, there is no IO time-consuming in the collaborative process, which is very suitable for high concurrent IO scenarios. (as shown below)

Collaborative execution process of Swoole

  • There is no IO waiting for the normal execution of PHP code in the collaboration process, and there will be no execution process switching

  • When the collaboration encounters IO, wait and switch the control right immediately. After the IO is completed, switch the execution flow back to the point cut out by the original collaboration

  • The coprocess is executed in parallel, and the same logic as above

  • The nested execution process of the collaboration process enters layer by layer from outside to inside until IO occurs, and then switches to the outer collaboration. The parent collaboration will not wait for the end of the child collaboration

Execution sequence of collaborative process

Let's take a look at the basic example:

go(function () {
    echo "hello go1 \n";
});

echo "hello main \n";

go(function () {
    echo "hello go2 \n";
});

go() is the abbreviation of \ Co::create(). It is used to create a collaboration. It accepts callback as a parameter. The code in callback will be executed in this new collaboration

Note: \ spool \ coroutine can be abbreviated as \ Co

The above code execution result:

root@b98940b00a9b /v/w/c/p/swoole# php co.php
hello go1
hello main
hello go2

There seems to be no difference between the execution result and the order in which we usually write code Actual execution process:

  • Run this code and the system starts a new process

  • Encountering go(), a collaboration process is generated in the current process. heelo go1 is output in the collaboration process, and the collaboration process exits

  • The process continues to execute the code downward and outputs hello main

  • Regenerate into a collaborative process, in which heelo go2 is output, and the collaborative process exits

Run this code, the system starts a new process If you don't understand this sentence, you can use the following code:

// co.php
<?php

sleep(100);

Execute and use ps aux to view the processes in the system:

root@b98940b00a9b /v/w/c/p/swoole# php co.php &
⏎
root@b98940b00a9b /v/w/c/p/swoole# ps aux
PID   USER     TIME   COMMAND
    1 root       0:00 php -a
   10 root       0:00 sh
   19 root       0:01 fish
  749 root       0:00 php co.php
  760 root       0:00 ps aux
⏎

Let's make a little change and experience the scheduling of collaborative process:

use Co;

go(function () {
    Co::sleep(1); // Only one line of code has been added
    echo "hello go1 \n";
});

echo "hello main \n";

go(function () {
    echo "hello go2 \n";
});

\The function of Co::sleep() is similar to sleep(), but it simulates IO waiting (IO will be described in detail later) The results of the implementation are as follows:

root@b98940b00a9b /v/w/c/p/swoole# php co.php
hello main
hello go2
hello go1

Why is it not executed in sequence? Actual execution process:

  • Run this code and the system starts a new process
  • go() encountered. A coroutine is generated in the current process
  • In case of IO blocking in the process (here is the IO waiting simulated by Co::sleep()), the process gives up control and enters the process scheduling queue
  • The process continues to execute downward and outputs hello main
  • Execute the next collaboration and output hello go2
  • The previous collaboration is ready. Continue to execute and output hello go1

Here, we can see the relationship between processes and processes in swoole and the scheduling of processes. Let's change the program just now:

go(function () {
    Co::sleep(1);
    echo "hello go1 \n";
});

echo "hello main \n";

go(function () {
    Co::sleep(1);
    echo "hello go2 \n";
});

I think you already know what the output looks like:

root@b98940b00a9b /v/w/c/p/swoole# php co.php
hello main
hello go1
hello go2
⏎

Where is Xie Cheng? Reduce performance loss due to IO blocking

You may hear that the most reason for using collaborative process is that it is fast Why should the code that looks similar to what you usually write be faster? One common reason to create a collaboration is to perform it quickly This statement is correct, but it still stays on the surface

First, general computer tasks are divided into two types:

  • CPU intensive, such as scientific computing such as addition, subtraction, multiplication and division
  • IO intensive, such as network request, file reading and writing, etc

Secondly, there are two concepts related to high performance:

  • Parallel: the same CPU can only execute the same task at the same time. To execute multiple tasks at the same time, you need multiple CPUs
  • Concurrency: since CPU switching tasks are very fast, it is fast to the limit that human beings can perceive, there will be the illusion that many tasks will be executed at the same time

After understanding these, let's look at the cooperative process. The cooperative process is suitable for IO intensive applications, because the cooperative process will be automatically scheduled in case of IO blocking, so as to reduce the time loss caused by IO blocking

We can compare the following three codes:

  • Normal version: perform 4 tasks
$n = 4;
for ($i = 0; $i < $n; $i++) {
    sleep(1);
    echo microtime(true) . ": hello $i \n";
};
echo "hello main \n";
root@b98940b00a9b /v/w/c/p/swoole# time php co.php
1528965075.4608: hello 0
1528965076.461: hello 1
1528965077.4613: hello 2
1528965078.4616: hello 3
hello main
real    0m 4.02s
user    0m 0.01s
sys     0m 0.00s
⏎
  • Single collaborative version:
$n = 4;
go(function () use ($n) {
    for ($i = 0; $i < $n; $i++) {
        Co::sleep(1);
        echo microtime(true) . ": hello $i \n";
    };
});
echo "hello main \n";
root@b98940b00a9b /v/w/c/p/swoole# time php co.php
hello main
1528965150.4834: hello 0
1528965151.4846: hello 1
1528965152.4859: hello 2
1528965153.4872: hello 3
real    0m 4.03s
user    0m 0.00s
sys     0m 0.02s
⏎
  • Multi process version: a moment to witness miracles
$n = 4;
for ($i = 0; $i < $n; $i++) {
    go(function () use ($i) {
        Co::sleep(1);
        echo microtime(true) . ": hello $i \n";
    });
};
echo "hello main \n";
root@b98940b00a9b /v/w/c/p/swoole# time php co.php
hello main
1528965245.5491: hello 0
1528965245.5498: hello 3
1528965245.5502: hello 2
1528965245.5506: hello 1
real    0m 1.02s
user    0m 0.01s
sys     0m 0.00s
⏎

Why is there such a big difference in time:

  • In normal writing, performance loss caused by IO blocking will be encountered

  • Single co process: Although IO blocking causes co process scheduling, there is only one co process at present, and the current co process will be executed after scheduling

  • Multi process coordination: it really gives full play to the advantages of collaborative process. Scheduling occurs when IO is blocked, and operation resumes when IO is ready

We will slightly modify the multi process version:

  • Multi process version 2: CPU intensive
$n = 4;
for ($i = 0; $i < $n; $i++) {
    go(function () use ($i) {
        // Co::sleep(1);
        sleep(1);
        echo microtime(true) . ": hello $i \n";
    });
};
echo "hello main \n";
root@b98940b00a9b /v/w/c/p/swoole# time php co.php
1528965743.4327: hello 0
1528965744.4331: hello 1
1528965745.4337: hello 2
1528965746.4342: hello 3
hello main
real    0m 4.02s
user    0m 0.01s
sys     0m 0.00s
⏎

Just changed Co::sleep() to sleep(), and the time is about the same as that of the normal version Because:

  • sleep() can be regarded as a CPU intensive task and will not cause the scheduling of collaborative processes

  • Co::sleep() simulates IO intensive tasks, which will trigger the scheduling of collaborative processes
    This is why coprocessing is suitable for IO intensive applications

Another example of comparison: using redis

// In the synchronous version, there will be IO blocking when redis is used
$cnt = 2000;
for ($i = 0; $i < $cnt; $i++) {
    $redis = new \Redis();
    $redis->connect('redis');
    $redis->auth('123');
    $key = $redis->get('key');
}

// Single co process version: there is only one co process, and co process scheduling is not used to reduce IO blocking
go(function () use ($cnt) {
    for ($i = 0; $i < $cnt; $i++) {
        $redis = new Co\Redis();
        $redis->connect('redis', 6379);
        $redis->auth('123');
        $redis->get('key');
    }
});

// The multi process version really uses the scheduling when IO blocking caused by process scheduling
for ($i = 0; $i < $cnt; $i++) {
    go(function () {
        $redis = new Co\Redis();
        $redis->connect('redis', 6379);
        $redis->auth('123');
        $redis->get('key');
    });
}

Performance comparison:

# Multi process version
root@0124f915c976 /v/w/c/p/swoole# time php co.php
real    0m 0.54s
user    0m 0.04s
sys     0m 0.23s
⏎

# Synchronous version
root@0124f915c976 /v/w/c/p/swoole# time php co.php
real    0m 1.48s
user    0m 0.17s
sys     0m 0.57s
⏎

Comparison between swoole and go: single process vs multithreading

The coder who has been in contact with the go collaboration will be a little confused in the initial collaboration with the swoole. For example, compare the following codes:

package main

import (
    "fmt"
    "time"
)

func main() {
    go func() {
        fmt.Println("hello go")
    }()

    fmt.Println("hello main")

    time.Sleep(time.Second)
}
> 14:11 src $ go run test.go
hello main
hello go

The coder who just wrote the go process will be told not to forget time when writing this code Sleep (time. Second), otherwise you can't see the output of hello go. Secondly, the order of hello go and hello main is also different from that in swoole

The reason lies in the different models of collaborative scheduling in swoole and go

The execution process of the above go Code:

  • Run the go code and the system starts a new process
  • Find package main and execute func mian() in it
  • To the scheduler
  • Continue to execute downward and output hello main
  • If you do not add time Sleep (time. Second), when the main function is executed, the program ends and the process exits, resulting in the termination of the coordination process in the scheduling

go, MPG model used:

  • M refers to Machine. An M is directly associated with a kernel thread
  • P refers to the processor, which represents the context required by M and is also the processor that handles user level code logic
  • G refers to Goroutine, which is also a lightweight thread in essence

The cooperative process scheduling in swoole uses the single process model. All cooperative processes are scheduled in the current process, and the benefits of single process are also obvious - simple / no locking / high performance

Both the MPG model of go and the single process model of swoole are the realization of CSP theory

CSP communication mode was already available in the paper in 1985. If people who do theoretical research do not have bold assumptions that can be made a few years, more than ten years or even decades in advance, it may be difficult to improve it

Pay attention and don't get lost

Well, ladies and gentlemen, the above is the whole content of this article. The people who can see here are talents. As I said before, there are many technical points in PHP. It's also because there are too many. You can't write it and you won't read it too much. So I've sorted it into PDF and documents here. If necessary, you can

Click to enter the code: PHP + "platform"

More learning content can be accessed [benchmarking factory] complete catalogue of high-quality PHP architect tutorials. As long as you can read it, you can ensure that the salary will rise to a higher level (continuously updated)

I hope to help you with the above contents. Many PHPer will encounter some problems and bottlenecks when upgrading. There is no sense of direction when writing too much business code. I don't know where to start to improve. For this, I have sorted out some materials, including but not limited to: distributed architecture, high scalability, high performance, high concurrency, server performance tuning, TP6, laravel, YII2, Redis, Swoole, Swoft, Kafka, Mysql optimization, shell script Docker, micro services, Nginx and other knowledge points. Advanced dry goods can be shared with you free of charge, and you can join me if you need PHP Technology Exchange Group

Tags: PHP

Posted by salmanshafiq on Fri, 13 May 2022 21:27:26 +0300