概述

众所周知,我们一般使用 PHP 开发Web程序时需要使用到比如Apache或Nginx等Web服务器来支持,那么有没有办法直接使用PHP开发HTTP服务器,答案当然是可以的,最近看了一遍Workerman框架的源码,于是自己仿照写了一个简易的HTTP服务器,学习为主。本文涉及到知识点包括:

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

如何编写 HTTP 服务器

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

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。

php  simple_http_server.php

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

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";
}
}
}
}

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

高性能的服务器

其实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 服务器,结构图如下:

具体的代码可以到   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 倍 。

用 PHP 编写 http 服务器的更多相关文章

  1. 实战WEB 服务器(JAVA编写WEB服务器)

    实战WEB 服务器(JAVA编写WEB服务器) 标签: web服务服务器javawebsockethttp服务器 2010-04-21 17:09 11631人阅读 评论(24) 收藏 举报  分类: ...

  2. 【北航软件工程】Alpha阶段前端页面编写及服务器部署

    前端页面编写 虽然之前对html语法有过一些了解,但是完全没有编写前端页面的经验,和我合作的czy大概也是这么个情况.在Alpha阶段的前端页面编写过程中,我们是摸着石头过河,html是个入门很快专精 ...

  3. Golang 编写 Tcp 服务器

    Golang 作为广泛用于服务端和云计算领域的编程语言,tcp socket 是其中至关重要的功能.无论是 WEB 服务器还是各类中间件都离不开 tcp socket 的支持. Echo 服务器 拆包 ...

  4. 使用 acl_cpp 的 HttpServlet 类及服务器框架编写WEB服务器程序(系列文章)

    在 <用C++实现类似于JAVA HttpServlet 的编程接口 > 文章中讲了如何用 HttpServlet 等相关类编写 CGI 程序,于是有网友提出了 CGI 程序低效性,不错, ...

  5. 深入学习用 Go 编写 HTTP 服务器

    Go是一门通用的编程语言,想要学习 Go 语言的 Web 开发,就必须知道如何用 Go 启动一个 HTTP 服务器用于接收和响应来自客户端的 HTTP 请求.用 Go实现一个http server非常 ...

  6. php编写tcp服务器和客户端程序

    这是我从别的地方看到的. 1.修改php.ini,打开extension=php_sockets.dll 2.客户端程序 SocketClient.php <?php set_time_limi ...

  7. JAVA编写WEB服务器

    一.超文本传输协议  1.1 HTTP请求  1.2 HTTP应答  二.Socket类  三.ServerSocket类  四.Web服务器实例  4.1 HttpServer类  4.2 Requ ...

  8. io复用select方法编写的服务器

    摘要:io多路复用是通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般都是读就绪或者写就绪),就能通知应用程序进行相应的读写操作.select函数作为io多路复用的机制,第一个参数nfds是f ...

  9. 01-简单编写http服务器

    package com.day3; import java.io.IOException; import java.io.InputStream; import java.net.ServerSock ...

随机推荐

  1. bitnami redmine安装、配置、备份、恢复

    1. 安装时语言选择英文,不可以选择中文,否则不能正常运行,可以在账户里改为显示中文:   2. 安装完成后,最上面的标题栏字体太小,修改:   D:\BitNami\redmine-2.1.4-0\ ...

  2. JS 相关

    计算高度: var a = document.body.clientHeight/2;console.log(a) window.scrollTo(0, document.body.clientHei ...

  3. java程序运行时间

    方法一 long startTime = System.currentTimeMillis(); //获取开始时间 doSomething(); //测试的代码段 long endTime = Sys ...

  4. 【校招面试 之 C/C++】第28题 C++ 内存泄漏的检查

    1.memwatch的使用 (1)首先去官网上下载源码: http://www.linkdata.se/sourcecode/memwatch/ 解压得到memwatch.c以及memwatch.h两 ...

  5. day4:vcp考试

    Q61. Which two statements are true regarding Virtual SAN Fault Domains? (Choose two.)A. They enable ...

  6. 20-最大k乘积问题

    /*                                             最大k乘积问题        题目内容: 设I是一个n位十进制整数.如果将I划分为k段,则可得到k个整数. ...

  7. catkin-tools

    http://catkin-tools.readthedocs.io/en/latest/cheat_sheet.html 一.Initializing Workspaces初始化工作空间 初始化具有 ...

  8. Halcon的C#二次开发及经验分享

    本文涉及面较广,因此很难在所有方面都讲解得很详细,故适合具有一定Halcon开发经验的人阅读. 1.Halcon二次开发的两种方式 ① 使用C#的语法方式逐句改写Halcon代码 优点:各种变量的类型 ...

  9. Redis数据持久化

    持久化选项 Redis提供了两种不同的持久化方法来将数据存储到硬盘里面.一种方法叫快照(snapshotting),它可以将存在于某一时刻的所有数据都写入硬盘里面.另一种方法叫只追加文件(append ...

  10. Codeforces 600A. Extract Numbers 模拟

    A. Extract Numbers time limit per test: 2 seconds memory limit per test: 256 megabytes input: standa ...