tcpdump分析php curl

所用工具

  • tcpdump:linux下分析网络的一个好用的终端工具
  • psysh:php的一个终端执行工具,可以很方便的在终端执行php代码
  • curl:php的curl库

tcpdump报文中Flags代表报文类型:
S (SYN), F (FIN), P (PUSH), R (RST), U (URG), W (ECN CWR), E (ECN-Echo) or ‘.’ (ACK), or ‘none’ if no flags are set.

分析背景

最近在了解swoole,因为公司的技术栈是php,所以之前用的java那一套在普通的php开发上完全用不了。为了让php有更好的性能,更广泛的使用场景,找了找相关资料,了解到swoole这个库。决定使用之后,项目中有个地方需要使用到http连接池,所以需要使用curl,但是curl虽然强大,但是参数实在是太多了,里面的实现细节也只能查看c的源代码才知道。所以只能曲线救国,通过tcpdump了解curl在http请求上的工作机制

tcpdump的使用方法是:

案例分析

案例一

只执行一次exec,等待一段时间,强制关闭终端进程

<?php
context = curl_init();
curl_setopt(context,CURLOPT_URL,"http://www.izuqun.com");
curl_exec($context);

整个过程的报文如下

    `192.168.1.107.44664 > 116.62.25.128.http`: Flags [S], cksum 0x4bdf (correct), seq 3687066847, win 29200, options [mss 1460,sackOK,TS val 5931640 ecr 0,nop,wscale 7], length 0
16:28:54.014280 IP (tos 0x20, ttl 52, id 0, offset 0, flags [DF], proto TCP (6), length 60)

    `116.62.25.128.http > 192.168.1.107.44664`: Flags [S.], cksum 0xbcb5 (correct), seq 4174849842, ack 3687066848, win 28960, options [mss 1460,sackOK,TS val 3844377305 ecr 5931640,nop,wscale 7], length 0
16:28:54.014304 IP (tos 0x0, ttl 64, id 44712, offset 0, flags [DF], proto TCP (6), length 52)

    `192.168.1.107.44664 > 116.62.25.128.http`: Flags [.], cksum 0x5bba (correct), ack 1, win 229, options [nop,nop,TS val 5931643 ecr 3844377305], length 0
16:28:54.014334 IP (tos 0x0, ttl 64, id 44713, offset 0, flags [DF], proto TCP (6), length 105)

    `192.168.1.107.44664 > 116.62.25.128.http`: Flags [P.], cksum 0xee60 (correct), seq 1:54, ack 1, win 229, options [nop,nop,TS val 5931643 ecr 3844377305], length 53: HTTP, length: 53
        GET / HTTP/1.1
        Host: www.izuqun.com
        Accept: *\* 
16:28:54.023036 IP (tos 0x20, ttl 52, id 44520, offset 0, flags [DF], proto TCP (6), length 52)

    `116.62.25.128.http > 192.168.1.107.44664`: Flags [.], cksum 0x5b85 (correct), ack 54, win 227, options [nop,nop,TS val 3844377307 ecr 5931643], length 0
16:28:54.023061 IP (tos 0x20, ttl 52, id 44521, offset 0, flags [DF], proto TCP (6), length 314)
    `116.62.25.128.http > 192.168.1.107.44664`: Flags [P.], cksum 0xba4d (correct), seq 1:263, ack 54, win 227, options [nop,nop,TS val 3844377307 ecr 5931643], length 262: HTTP, length: 262
        HTTP/1.1 200 OK
        Server: nginx/1.12.2
        Date: Mon, 11 Dec 2017 08:28:54 GMT
        Content-Type: text/html
        Content-Length: 1243
        Last-Modified: Mon, 11 Dec 2017 02:35:33 GMT
        Connection: keep-alive
        Vary: Accept-Encoding
        ETag: "5a2deef5-4db"
        Accept-Ranges: bytes
16:28:54.023076 IP (tos 0x0, ttl 64, id 44714, offset 0, flags [DF], proto TCP (6), length 52)

    `192.168.1.107.44664 > 116.62.25.128.http`: Flags [.], cksum 0x5a73 (correct), ack 263, win 237, options [nop,nop,TS val 5931645 ecr 3844377307], length 0
16:28:54.024214 IP (tos 0x20, ttl 52, id 44522, offset 0, flags [DF], proto TCP (6), length 1295)

    `116.62.25.128.http > 192.168.1.107.44664`: Flags [P.], cksum 0x870b (correct), seq 263:1506, ack 54, win 227, options [nop,nop,TS val 3844377307 ecr 5931643], length 1243: HTTP
16:28:54.024236 IP (tos 0x0, ttl 64, id 44715, offset 0, flags [DF], proto TCP (6), length 52)

    `192.168.1.107.44664 > 116.62.25.128.http`: Flags [.], cksum 0x5585 (correct), ack 1506, win 256, options [nop,nop,TS val 5931645 ecr 3844377307], length 0

1~3报文:tcp的三次握手,
4~5报文:curl发起的get请求、服务器的ack回应
6~9报文:服务器对get请求的返回、我的机器的内核的ack,因为报文长度是超出了最大的限制,所以分成两次发送

稍等一会儿,又有新的报文

    `116.62.25.128.http > 192.168.1.107.44664`: Flags [F.], cksum 0x161d (correct), seq 1506, ack 54, win 227, options [nop,nop,TS val 3844393567 ecr 5931645], length 0
16:29:59.097967 IP (tos 0x0, ttl 64, id 44716, offset 0, flags [DF], proto TCP (6), length 52)
    `192.168.1.107.44664 > 116.62.25.128.http`: Flags [.], cksum 0xd672 (correct), ack 1507, win 256, options [nop,nop,TS val 5947914 ecr 3844393567], length 0

这是服务器发起的tcp连接关闭的报文,我的机器响应了,但是没有主动再次发送Fin

主动关闭psysh进程,出现大量报文

    192.168.1.107.44664 > 116.62.25.128.http: Flags [F.], cksum 0x8765 (correct), seq 54, ack 1507, win 256, options [nop,nop,TS val 5968150 ecr 3844393567], length 0
16:31:20.253999 IP (tos 0x0, ttl 64, id 44718, offset 0, flags [DF], proto TCP (6), length 52)
    192.168.1.107.44664 > 116.62.25.128.http: Flags [F.], cksum 0x8730 (correct), seq 54, ack 1507, win 256, options [nop,nop,TS val 5968203 ecr 3844393567], length 0
16:31:20.465940 IP (tos 0x0, ttl 64, id 44719, offset 0, flags [DF], proto TCP (6), length 52)
    192.168.1.107.44664 > 116.62.25.128.http: Flags [F.], cksum 0x86fb (correct), seq 54, ack 1507, win 256, options [nop,nop,TS val 5968256 ecr 3844393567], length 0
16:31:20.890008 IP (tos 0x0, ttl 64, id 44720, offset 0, flags [DF], proto TCP (6), length 52)
    192.168.1.107.44664 > 116.62.25.128.http: Flags [F.], cksum 0x8691 (correct), seq 54, ack 1507, win 256, options [nop,nop,TS val 5968362 ecr 3844393567], length 0
16:31:21.737935 IP (tos 0x0, ttl 64, id 44721, offset 0, flags [DF], proto TCP (6), length 52)
    192.168.1.107.44664 > 116.62.25.128.http: Flags [F.], cksum 0x85bd (correct), seq 54, ack 1507, win 256, options [nop,nop,TS val 5968574 ecr 3844393567], length 0
16:31:23.437992 IP (tos 0x0, ttl 64, id 44722, offset 0, flags [DF], proto TCP (6), length 52)
    192.168.1.107.44664 > 116.62.25.128.http: Flags [F.], cksum 0x8414 (correct), seq 54, ack 1507, win 256, options [nop,nop,TS val 5968999 ecr 3844393567], length 0
16:31:26.833967 IP (tos 0x0, ttl 64, id 44723, offset 0, flags [DF], proto TCP (6), length 52)
    192.168.1.107.44664 > 116.62.25.128.http: Flags [F.], cksum 0x80c3 (correct), seq 54, ack 1507, win 256, options [nop,nop,TS val 5969848 ecr 3844393567], length 0
16:31:33.633977 IP (tos 0x0, ttl 64, id 44724, offset 0, flags [DF], proto TCP (6), length 52)
    192.168.1.107.44664 > 116.62.25.128.http: Flags [F.], cksum 0x7a1f (correct), seq 54, ack 1507, win 256, options [nop,nop,TS val 5971548 ecr 3844393567], length 0
16:31:47.218007 IP (tos 0x0, ttl 64, id 44725, offset 0, flags [DF], proto TCP (6), length 52)
    192.168.1.107.44664 > 116.62.25.128.http: Flags [F.], cksum 0x6cdb (correct), seq 54, ack 1507, win 256, options [nop,nop,TS val 5974944 ecr 3844393567], length 0
16:32:14.417998 IP (tos 0x0, ttl 64, id 44726, offset 0, flags [DF], proto TCP (6), length 52)
    192.168.1.107.44664 > 116.62.25.128.http: Flags [F.], cksum 0x524b (correct), seq 54, ack 1507, win 256, options [nop,nop,TS val 5981744 ecr 3844393567], length 0

这些报文都是我的机器主动发送的关闭报文,但是服务器也不再响应

案例二

完整的使用curl_init和curl_close,显式关闭连接

<?php
context = curl_init();
curl_setopt(context,CURLOPT_URL,"http://www.izuqun.com");
curl_exec(context);
curl_close(context);

报文如下

    192.168.1.107.44684 > 116.62.25.128.http: Flags [S], cksum 0x469f (correct), seq 2378041097, win 29200, options [mss 1460,sackOK,TS val 6028159 ecr 0,nop,wscale 7], length 0
16:35:20.090006 IP (tos 0x20, ttl 52, id 0, offset 0, flags [DF], proto TCP (6), length 60)
    116.62.25.128.http > 192.168.1.107.44684: Flags [S.], cksum 0xd6a7 (correct), seq 3548165201, ack 2378041098, win 28960, options [mss 1460,sackOK,TS val 3844473825 ecr 6028159,nop,wscale 7], length 0
16:35:20.090050 IP (tos 0x0, ttl 64, id 64631, offset 0, flags [DF], proto TCP (6), length 52)
    192.168.1.107.44684 > 116.62.25.128.http: Flags [.], cksum 0x75ac (correct), ack 1, win 229, options [nop,nop,TS val 6028162 ecr 3844473825], length 0
16:35:20.090105 IP (tos 0x0, ttl 64, id 64632, offset 0, flags [DF], proto TCP (6), length 105)
    192.168.1.107.44684 > 116.62.25.128.http: Flags [P.], cksum 0x0853 (correct), seq 1:54, ack 1, win 229, options [nop,nop,TS val 6028162 ecr 3844473825], length 53: HTTP, length: 53
        GET / HTTP/1.1
        Host: www.izuqun.com
        Accept: */*

16:35:20.099626 IP (tos 0x20, ttl 52, id 47030, offset 0, flags [DF], proto TCP (6), length 52)
    116.62.25.128.http > 192.168.1.107.44684: Flags [.], cksum 0x7576 (correct), ack 54, win 227, options [nop,nop,TS val 3844473828 ecr 6028162], length 0
16:35:20.099655 IP (tos 0x20, ttl 52, id 47031, offset 0, flags [DF], proto TCP (6), length 314)
    116.62.25.128.http > 192.168.1.107.44684: Flags [P.], cksum 0xda41 (correct), seq 1:263, ack 54, win 227, options [nop,nop,TS val 3844473828 ecr 6028162], length 262: HTTP, length: 262
        HTTP/1.1 200 OK
        Server: nginx/1.12.2
        Date: Mon, 11 Dec 2017 08:35:20 GMT
        Content-Type: text/html
        Content-Length: 1243
        Last-Modified: Mon, 11 Dec 2017 02:35:33 GMT
        Connection: keep-alive
        Vary: Accept-Encoding
        ETag: "5a2deef5-4db"
        Accept-Ranges: bytes

16:35:20.099670 IP (tos 0x0, ttl 64, id 64633, offset 0, flags [DF], proto TCP (6), length 52)
    192.168.1.107.44684 > 116.62.25.128.http: Flags [.], cksum 0x7464 (correct), ack 263, win 237, options [nop,nop,TS val 6028164 ecr 3844473828], length 0
16:35:20.099680 IP (tos 0x20, ttl 52, id 47032, offset 0, flags [DF], proto TCP (6), length 1295)
    116.62.25.128.http > 192.168.1.107.44684: Flags [P.], cksum 0xa0fc (correct), seq 263:1506, ack 54, win 227, options [nop,nop,TS val 3844473828 ecr 6028162], length 1243: HTTP
16:35:20.099686 IP (tos 0x0, ttl 64, id 64634, offset 0, flags [DF], proto TCP (6), length 52)
    192.168.1.107.44684 > 116.62.25.128.http: Flags [.], cksum 0x6f76 (correct), ack 1506, win 256, options [nop,nop,TS val 6028164 ecr 3844473828], length 0

这边的报文一开始和案例一完全一致,但是在我使用curl_close()显式关闭curl后,并没有发送任何关闭tcp连接的报文。查了一下资料,在php7.0中,如果$context任何有全局引用,即使使用curl_close()显式关闭,也不会关闭连接。

案例3

完整的使用curl_init和curl_close,显式关闭连接,并且多次执行exec

16:38:26.590098 IP (tos 0x0, ttl 64, id 18826, offset 0, flags [DF], proto TCP (6), length 60)
    192.168.1.107.44714 > 116.62.25.128.http: Flags [S], cksum 0x520f (correct), seq 3672832041, win 29200, options [mss 1460,sackOK,TS val 6074787 ecr 0,nop,wscale 7], length 0
16:38:26.604823 IP (tos 0x20, ttl 52, id 0, offset 0, flags [DF], proto TCP (6), length 60)
    116.62.25.128.http > 192.168.1.107.44714: Flags [S.], cksum 0xf795 (correct), seq 3884635295, ack 3672832042, win 28960, options [mss 1460,sackOK,TS val 3844520454 ecr 6074787,nop,wscale 7], length 0
16:38:26.604870 IP (tos 0x0, ttl 64, id 18827, offset 0, flags [DF], proto TCP (6), length 52)
    192.168.1.107.44714 > 116.62.25.128.http: Flags [.], cksum 0x969a (correct), ack 1, win 229, options [nop,nop,TS val 6074790 ecr 3844520454], length 0
16:38:26.604904 IP (tos 0x0, ttl 64, id 18828, offset 0, flags [DF], proto TCP (6), length 105)
    192.168.1.107.44714 > 116.62.25.128.http: Flags [P.], cksum 0x2941 (correct), seq 1:54, ack 1, win 229, options [nop,nop,TS val 6074790 ecr 3844520454], length 53: HTTP, length: 53
        GET / HTTP/1.1
        Host: www.izuqun.com
        Accept: */*

16:38:26.616651 IP (tos 0x20, ttl 52, id 28329, offset 0, flags [DF], proto TCP (6), length 52)
    116.62.25.128.http > 192.168.1.107.44714: Flags [.], cksum 0x9664 (correct), ack 54, win 227, options [nop,nop,TS val 3844520457 ecr 6074790], length 0
16:38:26.616674 IP (tos 0x20, ttl 52, id 28330, offset 0, flags [DF], proto TCP (6), length 314)
    116.62.25.128.http > 192.168.1.107.44714: Flags [P.], cksum 0xf829 (correct), seq 1:263, ack 54, win 227, options [nop,nop,TS val 3844520457 ecr 6074790], length 262: HTTP, length: 262
        HTTP/1.1 200 OK
        Server: nginx/1.12.2
        Date: Mon, 11 Dec 2017 08:38:26 GMT
        Content-Type: text/html
        Content-Length: 1243
        Last-Modified: Mon, 11 Dec 2017 02:35:33 GMT
        Connection: keep-alive
        Vary: Accept-Encoding
        ETag: "5a2deef5-4db"
        Accept-Ranges: bytes

16:38:26.616687 IP (tos 0x0, ttl 64, id 18829, offset 0, flags [DF], proto TCP (6), length 52)
    192.168.1.107.44714 > 116.62.25.128.http: Flags [.], cksum 0x9551 (correct), ack 263, win 237, options [nop,nop,TS val 6074793 ecr 3844520457], length 0
16:38:26.616701 IP (tos 0x20, ttl 52, id 28331, offset 0, flags [DF], proto TCP (6), length 1295)
    116.62.25.128.http > 192.168.1.107.44714: Flags [P.], cksum 0xc1ea (correct), seq 263:1506, ack 54, win 227, options [nop,nop,TS val 3844520457 ecr 6074790], length 1243: HTTP
16:38:26.616708 IP (tos 0x0, ttl 64, id 18830, offset 0, flags [DF], proto TCP (6), length 52)
    192.168.1.107.44714 > 116.62.25.128.http: Flags [.], cksum 0x9063 (correct), ack 1506, win 256, options [nop,nop,TS val 6074793 ecr 3844520457], length 0
16:38:28.241841 IP (tos 0x0, ttl 64, id 18831, offset 0, flags [DF], proto TCP (6), length 105)
    192.168.1.107.44714 > 116.62.25.128.http: Flags [P.], cksum 0x2174 (correct), seq 54:107, ack 1506, win 256, options [nop,nop,TS val 6075199 ecr 3844520457], length 53: HTTP, length: 53
        GET / HTTP/1.1
        Host: www.izuqun.com
        Accept: */*

16:38:28.253636 IP (tos 0x20, ttl 52, id 28332, offset 0, flags [DF], proto TCP (6), length 314)
    116.62.25.128.http > 192.168.1.107.44714: Flags [P.], cksum 0xeede (correct), seq 1506:1768, ack 107, win 227, options [nop,nop,TS val 3844520867 ecr 6075199], length 262: HTTP, length: 262
        HTTP/1.1 200 OK
        Server: nginx/1.12.2
        Date: Mon, 11 Dec 2017 08:38:28 GMT
        Content-Type: text/html
        Content-Length: 1243
        Last-Modified: Mon, 11 Dec 2017 02:35:33 GMT
        Connection: keep-alive
        Vary: Accept-Encoding
        ETag: "5a2deef5-4db"
        Accept-Ranges: bytes

16:38:28.253689 IP (tos 0x0, ttl 64, id 18832, offset 0, flags [DF], proto TCP (6), length 52)
    192.168.1.107.44714 > 116.62.25.128.http: Flags [.], cksum 0x8be1 (correct), ack 1768, win 276, options [nop,nop,TS val 6075202 ecr 3844520867], length 0
16:38:28.253704 IP (tos 0x20, ttl 52, id 28333, offset 0, flags [DF], proto TCP (6), length 1295)
    116.62.25.128.http > 192.168.1.107.44714: Flags [P.], cksum 0xb8a1 (correct), seq 1768:3011, ack 107, win 227, options [nop,nop,TS val 3844520867 ecr 6075199], length 1243: HTTP
16:38:28.253713 IP (tos 0x0, ttl 64, id 18833, offset 0, flags [DF], proto TCP (6), length 52)
    192.168.1.107.44714 > 116.62.25.128.http: Flags [.], cksum 0x86f3 (correct), ack 3011, win 295, options [nop,nop,TS val 6075202 ecr 3844520867], length 0

多次curl_exec()会复用之前的连接,但是curl_close()依然是不能关闭连接的。但当我使用unset($context)时,观察到了以下报文:

    192.168.1.107.49938 > 116.62.25.128.http: Flags [F.], cksum 0x8407 (correct), seq 54, ack 1506, win 256, options [nop,nop,TS val 274723 ecr 3860448314], length 0                                                                                                      
10:20:58.408173 IP (tos 0x20, ttl 52, id 25537, offset 0, flags [DF], proto TCP (6), length 52)                                      
    116.62.25.128.http > 192.168.1.107.49938: Flags [F.], cksum 0x5ca5 (correct), seq 1506, ack 55, win 227, options [nop,nop,TS val 3860458424 ecr 274723], length 0                                                                                                      
10:20:58.408218 IP (tos 0x0, ttl 64, id 34603, offset 0, flags [DF], proto TCP (6), length 52)                                       
    192.168.1.107.49938 > 116.62.25.128.http: Flags [.], cksum 0x5c85 (correct), ack 1507, win 256, options [nop,nop,TS val 274726 ecr 3860458424], length 0 

我的机器主动关闭连接发出Fin,服务器ack并发送Fin,我的机器也ack。两边顺利的完成了连接的关闭。

所以当前可以得出以下几点:

1.$context = curl_init(), $context是可以复用的,复用$context后,多次执行curl_exec()可以避免tcp的3次握手和4次断开的过程。

2.curl_close()在上面的试验中,并不能正常的关闭连接,这是因为在psysh的执行环境中,$context一直是在当前的作用域,会一直保持着变量的引用。而php7.0中,$context如果保持引用,curl_close则不会关闭连接,而unset($context)则会关闭。

3.服务器主动关闭连接后发出Fin,我的机器虽然响应了ack,但是没有主动发送Fin,这会不会导致服务器大量的fin_wait2?

第三点是很重要的,如果不解决会大量占用服务器资源。

所以再做一个实验,实验过程如下:

  • 我的机器初始化大量的curl_init(),然后不释放,等待服务器关闭。使用netstat查看服务器是否会出现大量的fin_wait2。

使用netstat -an|awk '/tcp/ {print $6}'|sort|uniq -c可以快速查看各种tcp连接状态的统计。
初始情况为:

20 CLOSE_WAIT
63 ESTABLISHED                                                                              
10 LISTEN

psysh中执行以下脚本

$contextArr = array();
for($i = 0; $i < 1000; $i++){
    $contextArr[$i] = curl_init();
    curl_setopt($contextArr[$i],CURLOPT_URL,"http://www.izuqun.com");
    curl_exec($contextArr[$i]);
}

脚本执行完后,服务器状态变化如下:

20 CLOSE_WAIT
1058 ESTABLISHED
5 FIN_WAIT2
10 LISTEN
2 TIME_WAIT
20 CLOSE_WAIT
58 ESTABLISHED
1001 FIN_WAIT2
10 LISTEN

20 CLOSE_WAIT
63 ESTABLISHED
10 LISTEN

可以看到,中间有1000个FIN_WAIT2状态,说明我们的猜想是正确的。

Swoole Server架构分析

一.简介

首页这里引用一下swoole的官方介绍:

swoole:面向生产环境的 PHP 异步网络通信引擎
使 PHP 开发人员可以编写高性能的异步并发 TCP、UDP、Unix Socket、HTTP,WebSocket 服务。Swoole 可以广泛应用于互联网、移动通信、企业软件、云计算、网络游戏、物联网(IOT)、车联网、智能家居等领域。 使用 PHP + Swoole 作为网络通信框架,可以使企业 IT 研发团队的效率大大提升,更加专注于开发创新产品。

通过上述的介绍,我们是可以得出几点信息的

  • 1.swoole可以投入生产环境
  • 2.使用php编写
  • 3.异步网络通信引擎,支持大量的网络协议,并且具有很高的网络性能

swoole server与传统的php运行模式是完全不同的,它是常驻内存的,省去了大量的php脚本的初始化。

二.swoole server的是怎么运行的

swoole进程/线程模型

这是一张官方的swoole运行时进程/线程模型。

1.Master进程

Master进程是一个多线程模型,其中包括Master线程,Reactor线程组,心跳检测线程,UDP收包线程。

以http server为例,Master线程负责监听(listen)端口,然后接受(accept)新的连接,然后将这个连接分配给一个Reactor线程,由这个Reactor线程监听此连接,一旦此连接可读时,读取数据,解析协议,然后将请求投递到worker进程中去执行。

Master进程是使用select/poll进行IO事件循环的,这是因为Master进程中的文件描述符只有几个(listenfd等),Reactor线程使用的是epoll,因为Reactor线程中会监听大量连接的可读事件,使用epoll可以支持大量的文件描述符。

2.Manager进程

Manager进程是专门用来管理Worker进程组和Task进程组的。它会Fork出指定数量的Worker进程和Task进程,并且有以下职能:

  • 子进程结束运行时,manager进程负责回收此子进程,避免成为僵尸进程。并创建新的子进程
  • 服务器关闭时,manager进程将发送信号给所有子进程,通知子进程关闭服务
  • 服务器reload时,manager进程会逐个关闭/重启子进程

3.Worker进程和Task进程

Worker进程接收Reactor线程投递过来的数据,执行php代码,然后生成数据交给Reactor线程,由Reactor线程通过tcp将数据返回给客户端。(如果是UDP,Worker进程直接将数据发送给客户端)。

Worker进程中执行的php代码和我们平时写php是一样的,它等同于php-fpm。但是众所周知,php-fpm下,php在处理异步操作时是很无力的,swoole提供的Task进程可以很好的解决这个问题。Worker进程可以将一些异步任务投递给Task进程,然后直接返回,处理其他的由Reactor线程投递过来的事件。

Task进程以完全同步阻塞的方式运行,一个Task进程在执行任务期间,是不接受从Worker进程投递的任务的,当Task进程执行完任务后,会异步地通知worker进程告诉它此任务已经完成。

所以介绍完上述的一些概念后,再引用一张官方的swoole执行流程图。
swoole运行流程图

这里需要注意,在文档上说的是:Workder进程组和Task进程组是由Manager进程Fork出来的,但是流程图上画的是在启动服务器时Fork出主进程和Worker进程组以及Tasker进程组。

三、使用swoole和传统php开发的优缺点

在说这个话题之前,需要先了解一下CGI,FASTCGI。

1.CGI
CGI的全称是Common Gateway Interface,通用网关接口,它使得任何一个拥有标准输入输出的程序拥有提供web server的能力。假设我们写了一个Hello World的c++程序,这个程序接受输入{text},输出{text},Hello World。

以nginx作为接受http请求为例,nginx接受一个http请求,Fork出一个进程,将http请求带来的text参数作为输入,执行完hello world程序,将输出{text},Hello World作为输出,销毁这个Fork出来的进程,由nginx返回给客户端。

这种方式虽然简单,但是要不断的Fork进程,销毁进程。

2.FASTCGI
FASTCGI,顾名思义,它是CGI的改进版,是一个常驻型的CGI服务。我们常用的php-fpm就是这种模式运行的,php-fpm负责Forl多个进程,每个进程中都运行了php的解释器。可以在终端下看一下php-fpm的进程:
php-fpm进程

一个php-fpm主进程,pid是1263,Fork出了3个子进程。在nginx+php-fpm的组合中,nginx负责接受http请求,将请求封装好交给php-fpm,php-fpm将请求按照一定的规则交给一个子进程去执行,这个子进程中的php解释器加载php代码运行。也是因为这个原因,传统的php只能作为web server。

然后我们发现,nginx+php-fpm的组合和我们Reactor+Worker子进程的运行方式非常相似。

3.swoole的运行方式
这里以swoole作为http server为例(传统php几乎都是作为web服务)。

首先swoole是实现了http server的,也就是说不需要nginx作为http服务器了,当然swoole并不是为了取代nginx,实际上swoole当前实现的http server功能有限,比如说只支持Get和Post,所有往往swoole前面还要运行一个nginx来作为前端代理服务器。

其次,swoole是内存常驻的。和php-fpm的常驻服务不同,php-fpm中常驻的是php的解释器,这个解释器会重复加载php代码,初始化环境,而swoole只在启动的时候加载,这样一来,性能就自然而然的提高了。这一点可以在开发中很明显的体现出来,php-fpm下,修改的php代码会即时生效,而使用swoole则需要重启swoole的server才能使代码生效。

通过上面的一些说明,就可以很明显的得出swoole和传统php开发的优缺点了。

swoole server优点:
– swoole性能更高
– 可以做为tcp,udp服务器
– 在高io高并发的服务器要求下,swoole的运行模式是完全可以胜任的

swoole server缺点:
– 更难上手。这要求开发人员对于多进程的运行模式有更清晰的认识
– 更容易内存泄露。在处理全局变量,静态变量的时候一定要小心,这种不会被GC清理的变量会存在整个生命周期中,如果没有正确的处理,很容易消耗完所有的内存。而以往的php-fpm下,php代码执行完内存就会被完全释放。
– 无法做密集计算。当然这一点是php甚至是所有动态语言都存在的问题。写在这里是因为防止误导读者以为使用swoole后,php可以用来做密集计算。