本文原载于SegmentFault专栏“Swoole”
作者:韩天峰
整理编辑:SegmentFault
在4.4
之前的版本中,Swoole
一直不支持CURL
协程化,在代码中无法使用curl
。由于curl
使用了libcurl
库实现,无法直接hook
它的socket
,4.4
版本使用Swoole\Coroutine\Http\Client
模拟实现了curl
的API
,并在底层替换了curl_init
等函数的C Handler
。
提示
CURL Hook
的特性尚处于试验阶段,请勿在生产环境中直接使用暂不支持文件上传、
CURL Multi
仍然需要依赖
curl
,请务必安装curl
扩展
支持的特性列表
GET/POST
Header
Cookie
Https
经过验证
Guzzle CURL
完全可以使用
开启
使用Runtime::enableCoroutine
来开启CURL Hook
。
默认不开启
CURL Hook
Swoole\Runtime::enableCoroutine(SWOOLE_HOOK_ALL);Swoole\Runtime::enableCoroutine(SWOOLE_HOOK_CURL);
使用
$n = 10;while($n--) {
go(function () {
$ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "http://www.xinhuanet.com/"); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HEADER, 0); $output = curl_exec($ch); if ($output === FALSE) {
echo "CURL Error:" . curl_error($ch); } curl_close($ch); echo strlen($output) . " bytes\n"; });}
要将上面两段代码合并到一个文件中执行
运行结果
htf@LAPTOP-0K15EFQI:~/swoole-src/examples$ time php curl.php177173 bytes177173 bytes177173 bytes177173 bytes177173 bytes177173 bytes177173 bytes177173 bytes177173 bytes177173 bytesreal 0m0.534suser 0m0.031ssys 0m0.297s
可以看到整个程序是并行的,进程没有任何阻塞。
strace 跟踪
使用strace
跟踪发现,所有系统调用均变成epoll
+socket
的异步非阻塞调用了。
epoll_create(512) = 3mmap(NULL, 258048, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc038a50000mmap(NULL, 2101248, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc028910000socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC, IPPROTO_IP) = 4fcntl(4, F_GETFL) = 0x80002 (flags O_RDWR|O_CLOEXEC)fcntl(4, F_SETFL, O_RDWR|O_NONBLOCK|O_CLOEXEC) = 0setsockopt(4, SOL_TCP, TCP_NODELAY, [1], 4) = 0pipe([5, 6]) = 0fcntl(5, F_GETFL) = 0 (flags O_RDONLY)fcntl(5, F_SETFL, O_RDONLY|O_NONBLOCK) = 0fcntl(6, F_GETFL) = 0x1 (flags O_WRONLY)fcntl(6, F_SETFL, O_WRONLY|O_NONBLOCK) = 0epoll_ctl(3, EPOLL_CTL_ADD, 5, {EPOLLIN, {u32=5, u64=34359738373}}) = 0mmap(NULL, 8392704, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7fc028100000mprotect(0x7fc028101000, 8388608, PROT_READ|PROT_WRITE) = 0clone(child_stack=0x7fc0288ffb70, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7fc0289009d0, tls=0x7fc028900700, child_tidptr=0x7fc0289009d0) = 55mmap(NULL, 8392704, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7fc0237f0000mprotect(0x7fc0237f1000, 8388608, PROT_READ|PROT_WRITE) = 0clone(child_stack=0x7fc023fefb70, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7fc023ff09d0, tls=0x7fc023ff0700, child_tidptr=0x7fc023ff09d0) = 56mmap(NULL, 8392704, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7fc022fe0000mprotect(0x7fc022fe1000, 8388608, PROT_READ|PROT_WRITE) = 0clone(child_stack=0x7fc0237dfb70, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7fc0237e09d0, tls=0x7fc0237e0700, child_tidptr=0x7fc0237e09d0) = 57mmap(NULL, 8392704, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7fc0227d0000mprotect(0x7fc0227d1000, 8388608, PROT_READ|PROT_WRITE) = 0clone(child_stack=0x7fc022fcfb70, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7fc022fd09d0, tls=0x7fc022fd0700, child_tidptr=0x7fc022fd09d0) = 58futex(0x7fffd9e01ce0, FUTEX_WAKE_PRIVATE, 1) = 1clock_gettime(CLOCK_MONOTONIC, {tv_sec=232, tv_nsec=513190000}) = 0clock_gettime(CLOCK_MONOTONIC, {tv_sec=232, tv_nsec=513408000}) = 0mmap(NULL, 2101248, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc0225c0000socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC, IPPROTO_IP) = 8fcntl(8, F_GETFL) = 0x80002 (flags O_RDWR|O_CLOEXEC)fcntl(8, F_SETFL, O_RDWR|O_NONBLOCK|O_CLOEXEC) = 0setsockopt(8, SOL_TCP, TCP_NODELAY, [1], 4) = 0futex(0x7fffd9e01ce0, FUTEX_WAKE_PRIVATE, 1) = 1clock_gettime(CLOCK_MONOTONIC, {tv_sec=232, tv_nsec=514359000}) = 0mmap(NULL, 2101248, P