Swoole实际上并不单是PHP中的一个扩展,从它的定位中,我们可以看出它实际上是可以直接替代服务层的。
从官方文档:https://wiki.swoole.com/wiki/page/326.html
中就可以看出,Swoole实际上可以代替nginx,作为代理http的服务应用。
下面我们就来学习下如何使用Swoole/Http/Server
,搭建HTTP-API服务。
目录结构:
server.php 服务启动脚步
vendor 公共文件类
app 接口文件地址 ,规则为:版本号/业务名称/接口文件(类同名)
server.php
代码
<?php
// +----------------------------------------------------------------------
// Swoole - HTTP-SERVER-API
// +----------------------------------------------------------------------
// Copyright (c) 2018 https://xiuxian.junphp.com All rights reserved.
// +----------------------------------------------------------------------
// Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// Author: 小黄牛 <1731223728@qq.com>
// +----------------------------------------------------------------------
ini_set('display_errors', 1);
error_reporting(E_ALL ^ E_NOTICE);
require_once dirname(__FILE__).'/vendor/api.php';
$server = new server();
$server->run();
class server{
/**
* WS的启动实例
*/
private $_ws;
/**
* host-IP,0.0.0.0表示允许接收所有请求
*/
private $_host = '0.0.0.0';
/**
* 端口号
*/
private $_port = '9501';
/**
* 这是启动服务端的入口
*/
public function run() {
$this->start();
$this->onRequest();
$this->_ws->start();
}
/**
* 启动http-server服务
*/
private function start() {
# 创建websocket服务器对象,监听0.0.0.0:9502端口
$this->_ws = new swoole_http_server($this->_host, $this->_port);
$this->_ws->set([
'worker_num' => 4,
'max_request' => 500
]);
}
/**
* 监听回调请求
*/
private function onRequest() {
$this->_ws->on('request', function ($request, $response) {
# 防止Chrome的空包
$uri = $request->server['request_uri'];
if ($uri == '/favicon.ico') {
$response->status(404);
$response->end();
} else {
# 先验证下请求头
api::check($request->header);
# 先验证下SIGN
$data = api::param($request);
$res = api::sign_vif($data);
if ($res !== true) {
$response->end($res);
} else {
# 继续向下执行
$this->follow_up_route($request, $response, $data);
}
}
});
}
/**
* 请求API检测
*/
private function follow_up_route($request, $response, $param) {
$referer = ltrim($request->server['request_uri'], '/');
# 分割路由
$array = explode('/', $referer);
if (count($array) < 2) {
$response->end(api::json('50001', '请求地址错误,格式为:版本号/业务名/接口名'));
} else {
$path = dirname(__FILE__).'/app';
$edition = $path.'/'.$array[0];
if (!file_exists($edition)) {
$response->end(api::json('50002', '暂无该版本'));
} else {
$controller = $edition.'/'.$array[1];
if (!file_exists($controller)) {
$response->end(api::json('50003', '暂无该业务'));
} else {
if (!empty($array[2])) {
$action_name = $array[2];
} else {
$action_name = 'index';
}
$action = $controller.'/'.$action_name.'.php';
if (!file_exists($action)) {
$response->end(api::json('50004', '暂无该接口'));
} else {
$this->follow_up_transmit($request, $response, $param, $action, $action_name);
}
}
}
}
}
/**
* 请求接口转发
*/
private function follow_up_transmit($request, $response, $param, $action, $action_name) {
# 引入接口文件
require_once $action;
# 实例化接口
$obj = new $action_name();
$obj->run($request, $response, $param, new api);
}
}
其中我们需要注意,request
事件在Chrome
内核的浏览器请求下,会由一次空包请求,官方解释:https://wiki.swoole.com/wiki/page/445.html
所以需要通过$request->server['request_uri']
参数来拦截第一次空包请求,防止逻辑错乱。
通过follow_up_transmit()
最后,我们加载对应的API接口文件。
下面我们来看下一个简单的app/v1/login/index.php
接口的代码:
<?php
// +----------------------------------------------------------------------
// | 登录接口
// +----------------------------------------------------------------------
// | Copyright (c) 2018 https://xiuxian.junphp.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 小黄牛 <1731223728@qq.com>
// +----------------------------------------------------------------------
class index {
/**
* 接口请求入口
* @param obj $request
* @param obj $response
* @param array $param
* @param obj $api
* @return bool
*/
public function run($request, $response, $param, $api){
$res = $this->is_type($request, $response, $api, 'get');
if ($res !== true) {
$response->end($res);
} else {
$response->end($api::json('200', '请求成功', $param));
}
}
// 判断请求类型
private function is_type($request, $response, $api, $is_type) {
# 拿出类型
$type = strtolower($request->server['request_method']);
if ($type != $is_type) {
return $api::json('50000', '请求类型错误!');
}
return true;
}
}
在linux通过php server.php
启动服务后。
我们就可以在浏览器中访问 IP:9501/v1/login/index
或者 IP:9501/v1/order/get
进行访问测试了。
进过一台4核8G 1M带宽的阿里云测试,命令为:
ab -c 700 -n 5000 -k http://你的IP:9501/v1/login/index?id=1
得出极限并发数为700
,超过这个数就会出现丢包。
因为带宽原因,没能测试极限并发量,但也明显能够看出,swoole作为http服务引擎,也是很棒的存在,而且作为一名PHP开发者,用PHP编写http-api服务,可控性也会高很多。
最后推荐大家可以用下我开源的一个基于Swoole4.5+研发的PHP框架。该框架基于注解实现了很多好玩的功能,很适合新人快速上手Swoole扩展。
SW-X框架-专注高性能便捷开发而生的PHP-SwooleX框架www.sw-x.cn