深入研究 PHP 的 SESSION 阻塞问题

最近在一个基于 Web 的 IM 项目中,我采用异步向服务器发起请求拉取最新的聊天内容,服务器端通过 PHP 处理拉取请求,拉取过程是用 10 次循环查询数据库是否有最新的聊天内容。如发现新内容,则立即向浏览器输出,并结束掉本次请求的进程。在这 10 次的循环中,每次查询数据库后,均通过 Sleep 函数让进程暂停 1 秒,那么这个 PHP 进程可能会在服务器端保持 10 秒。

在测试过程中,我发现当这个拉取请求运行期间,其他向服务器端 PHP 发起的请求,均受到影响,响应变的非常慢。

经过一系列的排查,问题始终得不到解决,但当把代码中涉及到 SESSION 的部分全部跳过时,情况发生了变化,所有 PHP 进程都恢复正常的响应速度了。由此,联想到问题可能出在了 SESSION 阻塞机制上了。

阅读更多

冒泡排序法 for Golang 写法

代码如下:

// main
package main

import (
	"fmt"
)

func main() {
	val := []int{7, 3, 8, 1, 0, 2, 5, 9, 6, 4}
	fmt.Println(val)

	// 冒泡排序
	for i := 0; i < len(val)-1; i++ {
		for j := i + 1; j < len(val); j++ {
			if val[i] > val[j] {
				val[i], val[j] = val[j], val[i]
			}
		}
	}

	fmt.Println(val)
}

阳光部落原创,更多内容请访问http://www.sunbloger.com/

用 MiniFramework 实现文件上传功能

MiniFramework 是一款遵循 Apache2 开源协议发布的,支持 MVC 和 RESTful 的超轻量级 PHP 开发框架。

在 MiniFramework 最新的版本中,提供了上传文件的特性,下面我们来演示一下实现过程。

首先,新建一个名为 Upload 的 Controller,并在其中新建一个名为 index 的 Action,代码如下:

<?php
namespace App\Controller;

use Mini\Upload;

class Upload extends Action
{
    function indexAction()
    {
        if (! empty($_FILES)) {
            
            // 实例化 Upload 类
            $upload = new Upload();
            
            $res = $upload->save($_FILES['f']);
            if (! $res) {
                $errmsg = $upload->getErrorMsg();
                echo $errmsg;
            } else {
                dump($res);
            }
        }
        
        $this->view->display();
    }
}

上边的代码中,通过 use Mini\Upload; 引入了 MiniFramework 框架所提供的一个用于文件上传的类库。

在实例化 Upload 类后,通过调用 save 方法,并将 PHP 的 $_FILES 传入,来实现上传文件的保存。

在调用 save 方法后,若文件保存失败,可以通过 getErrorMsg 方法来获取错误信息。

如果文件保存成功,那么 save 方法会返回一个数组,其中包含有文件保存的路径和文件名,上边的示例代码中使用了 MiniFramework 内置的全局函数 dump() 来输出 save 所返回的数组。

另外,在实例化 Upload 类时,可传入一个数组类型的参数,对文件保存路径、大小和类型进行设定,例如:

// 配置数组
$config = array(

    // 文件保存的根目录
    'rootPath'  => PUBLIC_PATH . '/uploads',

    // 文件的大小限制(单位:Byte)
    'maxPath'   => 512000,

    // 允许的类型
    'allowType' => 'bmp,gif,jpg,jpeg,png'

);

// 实例化 Upload 类时,将配置数组作为参数传入
$upload = new Upload($config);

上边的代码中,常量 PUBLIC_PATH 是 MiniFramework 内置用于定义站点根目录所对应的路径,默认对应的是 Public 目录所在的路径

接下来创建视图,在 View 目录中创建一个名为 upload 的目录,将视图文件保存到 View/upload/index.php,代码如下:

<h1>Upload</h1>
<form method="post" enctype="multipart/form-data">
  <input type="file" name="f" >
  <input type="submit" value="upload" /></p>
</form>

完成上述步骤后,便可以访问类似下面的地址来测试了
http://你的域名/index.php?c=upload&a=index

如果你的站点开启了伪静态,那么访问地址可以是下面这样:
http://你的域名/upload/index

上边的 URL 中,最后的 index 可以省略,例如:
http://你的域名/upload

阳光部落原创,更多内容请访问http://www.sunbloger.com/

解决 PHP 设置 open_basedir 与 eAccelerator 冲突问题

为 PHP 设置了 open_basedir 后,发现使用 require 和 include 始终会报出下面这样的 Warning 信息:

Warning: require(): open_basedir restriction in effect. File() is not within the allowed path(s)

这个 Warning 信息中,require 和 File 的括号中都是空白没有内容的,经过反复检查代码,确认所引用的路径在 open_basedir 允许的范围内。

通过去国外网站搜索资料,发现是因为 open_basedir 与 eAccelerator 冲突造成的。

需要重新编译一次 eAccelerator,编译时需要带上 –without-eaccelerator-use-inode 参数。

编译完重新启动 php-fpm 并删除之前的 eAccelerator 缓存文件即可。

阳光部落原创,更多内容请访问http://www.sunbloger.com/

MiniFramework新增了图片验证码输出和校验功能

今天为 MiniFramework 新增了一个名为 Captcha 的类,用于图片验证码的输出和校验。

具体用法如下:

用 MiniFramework 新建一个名为 Example 的控制器,例如:

<?php
namespace App\Controller;

use Mini\Action;
use Mini\Captcha;

/**
 * Example
 */
class Example extends Action
{
    function captchaAction()
    {
        if (!empty($_POST['code'])) {
            $captcha = new Captcha();
            $res = $captcha->check($_POST['code']);
            if ($res) {
                $this->view->assign('info', 'success');
            } else {
                $this->view->assign('info', 'fail');
            }
            $this->view->assign('code', $_POST['code']);
        }
        
        $this->view->display();
    }
    
    function getcaptchaAction()
    {
        $captcha = new Captcha();
        $captcha->create();
    }
}

 

同时创建视图,代码如下:

<?php if (isset($this->code)) echo '<p>input code: ' . $this->code . '</p>';?>
<?php if (isset($this->info)) echo '<p>check result: ' . $this->info . '</p>';?>
<img src="getcaptcha" onclick="this.src='getcaptcha?t='+Math.random()" />
<form method="post" action="captcha">
  <p>code:<input type="text" name="code" value="" />
  <input type="submit" value="check" /></p>
</form>

 

创建完成后,访问 http://你的域名/example/captcha 即可看到效果。

上边的代码已经包含在 MiniFramework 位于 GitHub 的 master 主线上。

GitHub源代码地址:https://github.com/jasonweicn/MiniFramework

采用http协议做IM拉取消息的流程设计

接手一个在线客服的项目,本质上就是一个 IM 项目。

第一个想到的是用 WebSocket,然而一些客观因素导致这个项目很难采用 WebSocket 来做。那么,就只好用 http 实现,并为此设计了如下流程:

如图,首先 Browser 端发起一个拉取消息的请求。Server 端收到请求后,立即查询是否有新消息,如查询到了新消息,则立即返回结果给 Browser 端。

Browser 端收到返回结果后,再次发起一个新的拉取消息的请求,如此往复进行。

若 Server 端接到拉取请求后,没有查询到新消息,则 Hold 住这个请求进程,同时设置一个时间阈值,比如5秒。

在这5秒内,Server 端反复查询是否有新消息,当发现了新消息后,则立即向 Browser 端返回结果同时结束掉进程。若始终都没有新消息,则在达到预设的时间阈值后,向 Browser 端返回一个通知,告知 Browser 端这次没有新消息。

在这个流程中,Browser 端发出请求后,可能会立即收到结果,也可能在5秒后得知没有新消息,不论是否有新消息,在得到 Server 端反馈后,都要立即再次发出请求。

阳光部落原创,更多内容请访问http://www.sunbloger.com/

利用Swoole实现服务器通过WebSocket主动向浏览器推送数据

WebSocket 协议是基于 TCP 的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信,允许服务器主动发送信息给客户端。

下面的代码演示了利用 Swoole 实现服务器向浏览器主动推送数据的例子。

Server端代码:

<?php
$server = new Swoole\Websocket\Server('192.168.1.1', 9501);

$server->on('Open', function($server, $req) {
    echo 'connection open: '.$req->fd . "\n";
    $server->push($req->fd, "welcome to www.sunbloger.com");
    for ($i=1; $i<=9; $i++) {
        $server->push($req->fd, $i);
        sleep(3);
    }
});

$server->on('Message', function($server, $req) {
    echo "client(".$req->fd."): " . $req->data . "\n";
});

$server->on('Close', function($server, $fd) {
    echo "connection close: {$fd}\n";
});

$server->on('WorkerStart', function ($server, $workerId) {
    echo "worker started: {$workerId}\n";
});

$server->start();
?>

 

浏览器端(Client)代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>WebSocket Client</title>
</head>
<body>  
  <input type="text" id="text">
  <input type="submit" value="发送数据" onclick="send()">
  <div id="msg"></div>
</body>
<script>
var isSupport;

if (typeof WebSocket != 'undefined') {
    isSupport = true;
} else {
    isSupport = false;
}

if (isSupport == true) {
    var msg = document.getElementById("msg");
    var wsServer = 'ws://192.168.1.1:9501';
    var websocket = new WebSocket(wsServer);
    websocket.onopen = function (e) {
        console.log("websocket server open.");
    };
    
    websocket.onmessage = function (e) {
        msg.innerHTML += e.data +'<br>';
    }
    
    websocket.onclose = function (e) {
        console.log("websocket server close.");
    };
    
    
}

function send() {
    if (isSupport == true) {
        var text = document.getElementById('text').value;
        document.getElementById('text').value = ''; //向服务器发送数据
        websocket.send(text);
    } else {
        alert("浏览器不支持WebSocket!");
    }
}

</script>
</html>

 

阳光部落原创,更多内容请访问http://www.sunbloger.com/

用Python3提取网页中的超链接

最近有朋友给我指出,我此前写的博文《用Python提取网页中的超链接》(原文地址:http://www.sunbloger.com/article/442.html)中,给出的代码在Python3下运行报错。下面给出在Python3的代码写法:

import urllib.request
import re
 
url = 'http://www.sunbloger.com/'
 
req = urllib.request.urlopen(url)
doc = req.read()
doc = doc.decode('utf-8')
 
links = re.findall(r'href\=\"(http\:\/\/[a-zA-Z0-9\.\/]+)\"', doc)
for a in links:
    print(a)

 

阳光部落原创,更多内容请访问http://www.sunbloger.com/

汇总PHP最常见的四种排序算法

PHP最常见的四种排序算法分别是:冒泡排序法,选择排序法、插入排序法和快速排序法。下面我们就分别给出四种排序算法的实现代码,供大家参考。

1.冒泡排序法

<?php
function bubble_sort($array)
{
    $count = count($array);
    if ($count == 0) {
        return false;
    }
    for ($i=0; $i<$count; $i++) {
        for ($k=$count-1; $k>$i; $k--) {
            if ($array[$k] < $array[$k-1]) {
                $tmp = $array[$k];
                $array[$k] = $array[$k-1];
                $array[$k-1] = $tmp;
            }
        }
    }
    return $array;
}

$a = array(2, 5, 1, 3, 4);
$a = bubble_sort($a);
var_dump($a);
?>

 

2.选择排序法

<?php
function select_sort($array)
{
    $len = count($array);
    for($i=0; $i<$len-1; $i++) {
        $p = $i;
        for($j=$i+1; $j<$len; $j++) {
            if($array[$p] > $array[$j]) {
                $p = $j;
            }
        }
        if($p != $i) {
            $tmp = $array[$p];
            $array[$p] = $array[$i];
            $array[$i] = $tmp;
        }
    }
    
    return $array;
}

$a = array(2, 5, 1, 3, 4);
$a = select_sort($a);
var_dump($a);
?>

 

3.插入排序法

<?php
function insert_sort($array)
{
    $len = count($array);
    for($i=1; $i<$len; $i++) {
        $tmp = $array[$i];
        for($j=$i-1; $j>=0; $j--) {
            if($tmp < $array[$j]) {
                $array[$j+1] = $array[$j];
                $array[$j] = $tmp;
            } else {
                break;
            }
        }
    }
    
    return $array;
}

$a = array(2, 5, 1, 3, 4);
$a = insert_sort($a);
var_dump($a);
?>

 

4.快速排序法

<?php
function quick_sort($array)
{
    $len = count($array);
    if ($len <= 1) {
        return $array;
    }
    
    $base = $array[0];
    
    $left = array();
    $right = array();
    for ($i=1; $i<$len; $i++) {
        if($base > $array[$i]) {
            $left[] = $array[$i];
        } else {
            $right[] = $array[$i];
        }
    }
    $left = quick_sort($left);
    $right = quick_sort($right);
    
    return array_merge($left, array($base), $right);
}

$a = array(2, 5, 1, 3, 4);
$a = quick_sort($a);
var_dump($a);
?>

 

阳光部落原创,更多内容请访问http://www.sunbloger.com/

用 MiniFramework 快速构建 REST 风格的 API 接口

MiniFramework 是一款遵循 Apache2 开源协议发布的,支持 MVC 和 RESTful 的超轻量级 PHP 开发框架。

通过 MiniFramework 内置的 REST 接口功能,我们可以很轻松的快速构建起基于 REST 风格的 API 接口,特别是在开发 iOS 和 Android 手机客户端应用时,服务器端的 API 接口是否灵活高效,直接影响着手机应用的体验。

下面介绍一下如何使用 MiniFramework 开发 REST 风格的 API 接口。

第一步,我们下载最新 MiniFramework 1.0.2 版本

下载地址是:https://github.com/jasonweicn/MiniFramework/archive/1.0.2.zip

下载并解压缩后,会看到里面有 App 和 MiniFramework 两个目录,App 目录是一个附带的应用 demo,MiniFramework 目录是框架核心文件。

第二步,我们在 Apache 或 Nginx 中新建一个站点,并将站点的根目录指向 App/Public 这个目录。

设置好站点根目录后,我们可以测试一下,用浏览器访问下面的 URL 地址:

http://你的域名/index.php?c=index&a=index

如果可以显示“Hello World!”,说明站点已经部署成功了。

第三步,为了优化访问 URL 访问的便捷性,我们要给站点设置伪静态规则。

如果你用的是 Apache,那么附带的应用 demo 中已经提供了 .htaccess 文件,不需要额外的设置了。

如果你用的是 Nginx,那么需要在 nginx.conf 中,向对应站点的 server{} 段中,增加下面的代码:

location / {
    index  index.html index.php;
    if (!-e $request_filename) {
        rewrite ^/(.*)$ /index.php last;
    }
}

 

保存后,别忘记重启 Nginx 让配置生效。

经过上述配置后,用浏览器访问下面的 URL 地址:

http://你的域名/index/index

如果可以显示“Hello World!”,那就说明伪静态也配置成功了。

第四步,创建 API 程序

找到 App/Api 目录,这里是存放 API 接口的地方,在里面已经附带了一个名为 Version.php 的 demo,你可以参考这个文件的代码,编写自己的 API,比如我们创建一个名为 User.php,里面放置 get、post、put 和 delete 四个方法,分别对应 REST 中的 GET、POST、PUT 和 DELETE 请求。代码如下:

<?php
//首先要声明当前文件的命名空间为 App\Api
namespace App\Api;
 
//还要引入 Mini\Rest
use Mini\Rest;
 
class User extends Rest //必须要继承Rest类
{
    /**
     * 初始化
     */
    function _init()
    {
        //do something...
    }
    
    /**
     * GET
     */
    function get()
    {
        //我们临时构建一个用户列表数组,在你的业务代码中或许应该通过模型从数据库中读取
        $userlist = array(
            array('id'=>1, 'name'=>'user1'),
            array('id'=>2, 'name'=>'user2'),
            array('id'=>2, 'name'=>'user3')
        );
        
        //将$userlist通过responseJson()方法输出为JSON
        $this->responseJson(200, 'success', $userlist);
    }
    
    /**
     * POST
     */
    function post()
    {
        //获取POST参数
        $params = $this->params->getParams();
        
        //do something...
    }
    
    /**
     * PUT
     */
    function put()
    {
        //获取PUT参数(POST和PUT参数均可通过此方法获得)
        $params = $this->params->getParams();
        
        //do something...
    }
    
    /**
     * DELETE
     */
    function delete()
    {
        //返回HTTP状态码403(在REST中表示对于拒绝访问)
        $this->responseJson(403);
    }
}

 

创建好上边的 API 后,我们可以测试一下,用浏览器访问下面的 URL 地址:

http://你的域名/api/user

此时的访问请求属于 GET 方式,MiniFramework 会将这个请求映射到我们刚刚创建的 User 类的 get() 方法上,如果一切正常,我们会看到浏览器中显示如下内容:

{"code":200,"msg":"success","data":[{"id":1,"name":"user1"},{"id":2,"name":"user2"},{"id":2,"name":"user3"}]}

 

上边显示的内容就是通过 responseJson() 方法输出的 JSON 字符串了,其中 code 代表这次请求服务器返回的状态代码,msg 是返回的说明,data 里面就是这次请求发出后,服务器返回给客户端的数据了。

我们可以看到,上边 User 类的代码中,在最开始要通过 namespace 来声明当前文件的命名空间,在 MiniFramework 中,默认情况下 API 接口的命名空间统一为 App/Api。

再往下还要通过 use 引入 Mini\Rest,这个是 MiniFramework 的核心类之一,我们创建的 API 接口的 class 必须要继承 Rest。

再往下就是 REST 中经典的 get、post、put 和 delete 四个方法,对于客户端请求类型的映射问题,MiniFramework 会自动帮我们处理好,我们只要专注写好业务逻辑就可以了。

阳光部落原创,更多内容请访问 http://www.sunbloger.com/