用 PHP 编写 http 服务器

  概述

众所周知,咱们通常使用 PHP 开发Web程序时须要使用到好比Apache或Nginx等Web服务器来支持,那么有没有办法直接使用PHP开发HTTP服务器,答案固然是能够的,最近看了一遍Workerman框架的源码,因而本身仿照写了一个简易的HTTP服务器,学习为主。本文涉及到知识点包括:php

  • PHP Socket编程
  • 网络 IO 模型
  • PHP libevent
  • PHP 多进程
  • PHP 扩展信号

  如何编写 HTTP 服务器

下面是一个简易版HTTP服务器,HTTP 是应用层,其实底层用的是 TCP,在TCP 的基础上包了一层 HTTP的协议。代码以下:git

require_once 'Http.php';
$socket = stream_socket_server("0.0.0.0:2345", $errno, $errstr);

if (!$socket) {
    echo "$errstr ($errno)<br />\n";
} else {
    while (true) {
        $conn = @stream_socket_accept($socket);
        if ($conn)
        {
            $data = Http::encode('Hi world');
            fwrite($conn, $data);
            fclose($conn);
        } else {
            echo "no newSocket\n";
        }
    }
}

几行代码就能够实现一个简单的 web 服务器,在 shell 下面执行下面命令,在浏览器输入:http://127.0.0.1:2345/ 便可看到 Hi world。github

php  simple_http_server.php

上面那那种构架,阻塞模式,要等前一个处理完了,才能处理下一个。因此流量稍微大一点,就会处理不过来。那咱们能够改进一下,变成多进程模式。web

require_once 'Http.php';
$socket = stream_socket_server("0.0.0.0:2345", $errno, $errstr);

if (!$socket) {
    echo "$errstr ($errno)<br />\n";
} else {
    while (true) {
       
       if (pcntl_fork() == 0)
       {
            $conn = @stream_socket_accept($socket);
            
            if ($conn)
            {
                $data = Http::encode('Hi world');
                fwrite($conn, $data);
                fclose($conn);
            } else {
                echo "no newSocket\n";
            }
       }
    }
}

这种模式最大的问题是,进程/线程建立和销毁的开销很大。因此上面的模式没办法应用于很是繁忙的服务器程序shell

  高性能的服务器

其实IO复用的历史和多进程同样长,Linux很早就提供了 select 系统调用,能够在一个进程内维持1024个链接。后来又加入了poll系统调用,poll作了一些改进,解决了 1024 限制的问题,能够维持任意数量的链接。但select/poll还有一个问题就是,它须要循环检测链接是否有事件。这样问题就来了,若是服务器有100万个链接,在某一时间只有一个链接向服务器发送了数据,select/poll须要作循环100万次,其中只有1次是命中的,剩下的99万9999次都是无效的,白白浪费了CPU资源。编程

直到Linux 2.6内核提供了新的epoll系统调用,能够维持无限数量的链接,并且无需轮询,这才真正解决了 C10K 问题。如今各类高并发异步IO的服务器程序都是基于epoll实现的,好比Nginx、Node.js、Erlang、Golang。像 Node.js 这样单进程单线程的程序,均可以维持超过1百万TCP链接,所有归功于epoll技术。浏览器

libevent是一个轻量级的基于事件驱动的高性能的开源网络库,而且支持多个平台,依据系统提供的select,poll和epoll方法来进行I/O复用,可是针对于多个系统平台上的不一样的I/O复用实现方式,libevent进行了从新的封装,并提供了统一的API接口。libevent在实现上使用了事件驱动这种机制。服务器

咱们经过 多进程 + libevent 来构架 web 服务器,结构图以下:yii2

具体的代码能够到   demo, 执行 php demo.php start 便可。网络

  压力测试

 硬件是本身 Mac pro,依据 1000 并发重复 100次进行测试:

 先测试一个 Nginx + fpm ,siege -c 1000 -r 100 http://yii2.localhost/  结果以下:

再测试本身写的服务器 siege -c 1000 -r 100 http://127.0.0.0:2345

本身写的服务器成功率几乎是 Nginx + fpm 的 2 倍 。

相关文章
相关标签/搜索