php7嵌入式开发–重新创建cli

php7嵌入式开发–环境配置基础上,我们新建个embed1.c,代码如下:

#include <stdio.h>
#include <sapi/embed/php_embed.h>
int main(int argc, char *argv[]) {
    zend_file_handle    script;
    if ( argc <= 1 ) {
        fprintf(stderr, "Usage: %s <filename.php> <arguments>\n", argv[0]);
        return -1;
    }
    

    script.type             = ZEND_HANDLE_FP;  
    script.filename         = argv[1];  
    script.opened_path      = NULL;  
    script.free_filename    = 0;  

    if ( !(script.handle.fp = fopen(script.filename, "rb")) ) {
        fprintf(stderr, "Unable to open: %s\n", argv[1]);
        return -1;
    }
    argc --;
    argv ++;
    PHP_EMBED_START_BLOCK(argc, argv)
        php_execute_script(&script TSRMLS_CC);
    PHP_EMBED_END_BLOCK()
    return 0;
}

编译命令如下:

gcc -I /usr/local/include/php/ -I /usr/local/include/php/main/ -I /usr/local/include/php/Zend/ -I /usr/local/include/php/TSRM/ -lphp7 -o embed1 embed1.c

执行如下测试

这样,我们就可以通过embed1执行PHP源代码了,相当于php下的cli.

发表在 好文推荐, 网站开发, 网站架构 | 标签为 , | 23 条评论

php7嵌入式开发–环境配置

1:我们先生成libphp7.so库.在编译的时候加上–enable-embed

wget http://cn2.php.net/distributions/php-7.0.11.tar.gz
tar -zxvf php-7.0.11.tar.gz
cd php-7.0.11
configure --enable-embed
make
make install

生成的libphp7.so文件应该在/usr/local/lib/目录下,需要的头文件应该在 /usr/local/include/php目录下.

2:编写C文件embed.c

#include "sapi/embed/php_embed.h"
 
int main(int argc, char * argv[]){
    PHP_EMBED_START_BLOCK(argc,argv);
    char * script = " print 'Hello World!';";
    zend_eval_string(script, NULL,"Simple Hello World App" TSRMLS_CC);
    PHP_EMBED_END_BLOCK();
    return 0;
}

3:编译生成目标程序

gcc -I /usr/local/include/php/ -I /usr/local/include/php/main/ -I /usr/local/include/php/Zend/ -I /usr/local/include/php/TSRM/ -lphp7 -o embed embed.c

4:运行程序

现在我们大概可以了解php7的嵌入式开发了

发表在 好文推荐, 网站架构 | 标签为 , | 19 条评论

lsof 一切皆文件

lsof(list open files)是一个查看当前系统文件的工具。在linux环境下,任何事物都以文件的形式存在,通过文件不仅仅可以访问常规数据,还可以访问网络连接和硬件。如传输控制协议 (TCP) 和用户数据报协议 (UDP) 套接字等,系统在后台都为该应用程序分配了一个文件描述符,该文件描述符提供了大量关于这个应用程序本身的信息.

lsof打开的文件可以是:
    普通文件
    目录
    网络文件系统的文件
    字符或设备文件
    (函数)共享库
    管道,命名管道
    符号链接
    网络文件(例如:NFS file、网络socket,unix域名socket)
    还有其它类型的文件,等等

命令示例

查找打开某个文件的进程
sof /usr/local/php7/var/log/php-fpm.log

列出某个用户打开的文件信息
lsof -u nginx

列出某个程序进程所打开的文件信息
lsof -c php

通过某个进程号显示该进程打开的文件
lsof -p 1234

列出所有的网络连接
lsof -i

列出所有tcp 网络连接信息
lsof -i tcp

列出谁在使用某个端口
lsof -i :3306

列出某个用户的所有活跃的网络端口
lsof -a -u nginx -i

记录到此,方便以后查询!

发表在 网站开发, 网站架构 | 标签为 , | 15 条评论

lex和yacc学习1

Lex(Lexical Analyzar 词法分析生成器),Yacc(Yet Another Compiler Compiler编译器代码生成器)是Unix下十分重要的词法分析,语法分析的工具。经常用于语言分析,公式编译等广泛领域,详细内容可参考http://dinosaur.compilertools.net/.
先来看最简单的例子(xtgxiso.l)

%{
#include "stdio.h"
%}
%%
[/n]                  ;
[0-9]+                printf("Int:%s/n",yytext);
.                     printf("Unknown:%c/n",yytext[0]);
%%

执行如下命令

到此我们会对lex有个直观的用法了解
1.定义Lex描述文件
2.通过lex工具解析成lex.yy.c文件
3.使用cc编译lex.yy.c生成可执行程序

现在我们来写一个相对比较完整的例子

%{
#include "stdio.h"
%}
%%
[/n]                  ;
[0-9]+                printf("Int:%s/n",yytext);
.                     printf("Unknown:%c/n",yytext[0]);
%%

int main()
{
    yylex();
    return 0;
}

int yywrap()
{
    return 1;
}

执行如下命令

这次编译没有加ll选项,那是因为加了main函数.

现在我们来大致了解lex的描述文件结构,
一般可以分为<定义部分><规则部><用户子程序部分>。其中规则部分是必须的,定义和用户子程序部分是任选的

1:定义部分起始于 %{ 符号,终止于 %} 符号,其间可以是包括include语句、声明语句在内的C语句。这部分跟普通C程序开头没什么区别

2:规则部分起始于”%%”符号,终止于”%%”符号,其间则是词法规则。词法规则由模式和动作两部分组成。模式部分可以由任意的正则表达式组成,动作部分是由C语言语句组成,这些语句用来对所匹配的模式进行相应处理。需要注意的是,lex将识别出来的单词存放在yytext[]字符数据中,因此该数组的内容就代表了所识别出来的单词的内容.

3:用户子程序部分,可以包含用C语言编写的子程序,而这些子程序可以用在前面的动作中,这样就可以达到简化编程的目的

Lex内部变量和函数

yytext char * 当前匹配的字符串

yyleng int 当前匹配的字符串长度

yyin FILE * lex当前的解析文件,默认为标准输出

yyout FILE * lex解析后的输出文件,默认为标准输入

yylineno int 当前的行数信息

ECHO #define ECHO fwrite(yytext, yyleng, 1, yyout) 也是未匹配字符的默认动作

int yylex(void) 调用Lex进行词法分析

int yywrap(void) 在文件(或输入)的末尾调用。如果函数的返回值是1,就停止解析

Lex其实就是词法分析器,通过配置文件*.l,依据正则表达式逐字符去顺序解析文件,并动态更新内存的数据解析状态。不过Lex只有状态和状态转换能力。因为它没有堆栈,它不适合用于剖析外壳结构。而yacc增加了一个堆栈,并且能够轻易处理像括号这样的结构。Lex善长于模式匹配,如果有更多的运算要求就需要yacc了.

发表在 好文推荐, 网站架构 | 标签为 , | 26 条评论

swoole2.0试用说明

    Swoole在2.0开始内置协程的能力,提供了具备协程能力IO接口.最好的好处是开发者可以以同步编码的方式达到异步IO的效果.

    Swoole2.0安装要求

  •      php版本要求:>= 5.5, 暂不支持PHP7
  •      基于swoole_server或者swoole_http_server进行开发,目前支持在onRequet, onReceive, onConnect回调中使用协程

    安装过程

  1. 先安装第三方的异步Redis库https://github.com/redis/hiredis   以支持redis操作
  2. 启用–enable-async-redis –enable-coroutine    

    安装常见问题

      1.找不到libhiredis.so.0.13 ,确保安装了hiredis前提下.增加/etc/ld.so.conf.d/lib.conf文件。内容”/usr/local/lib”.然后运行ldconfig命令

      2.gcc 4.4下如果在编译swoole的时候(即make阶段),出现gcc warning

        dereferencing pointer ‘v.327’ does break strict-aliasing rules、dereferencing type-punned pointer will break strict-aliasing rules

        请手动编辑Makefile,将CFLAGS = -Wall -pthread -g -O2替换为CFLAGS = -Wall -pthread -g -O2 -fno-strict-aliasing,

        然后重新编译make clean;make;make install

    安装完成后,请用php -m 来确认swoole是否安装成功.

    第一个代码示例.

<?php

$serv = new swoole\http\server("0.0.0.0", 1215);

$serv->set([
        'worker_num' => 1,
]);

$serv->on('Request', function($request, $response) {
    $response->header("X-Server", "Swoole");
    $cli = new Swoole\Coroutine\Http\Client('127.0.0.1', 80);
    $cli->setHeaders([
        'Host' => "test.xtgxiso.cn",
        "User-Agent" => 'Chrome/49.0.2587.3',
        'Accept' => 'text/html,application/xhtml+xml,application/xml',
        'Accept-Encoding' => 'gzip',
    ]);
    $cli->set([ 'timeout' => 2]);
    $cli->get('/sleep1.php');
    $str =  $cli->body;
    $cli->close();
 
    $response->end("<h1>Hello Swoole!</h1>".$str);
});

$serv->start();

    代码大致的功能是,以一个进程启动一个http server,访问一个url.这个url会sleep(1)

 模拟并发

<script src="http://123.56.145.253:1215/?id=1"></script>
<script src="http://123.56.145.253:1215/?id=2"></script>
<script src="http://123.56.145.253:1215/?id=3"></script>
<script src="http://123.56.145.253:1215/?id=4"></script>
<script src="http://123.56.145.253:1215/?id=5"></script>
<?php
echo "123";

   访问的效果

通过这个示例,我们已经可以明白swoole2.0的好处了。原来为了实现高并发,用的是IO复用,是各种回调。现在内置了协程,实现了高性能的同时代码看起来也是同步的!期待swoole2.0越来越强大.

   

发表在 好文推荐, 网站开发 | 标签为 | 25 条评论

用go实现redis服务端

上一篇文章我们用PHP来实现的redis服务端,这次我们用go来实现下,看看会有什么区别。

代码如下:

 // redis_server project main.go
package main

import (
	"strings"
	"bufio"
	"fmt"
	"net"
	"strconv"
)

var kv_data map[string]string

func main() {

	kv_data = make(map[string]string)

	//建立socket,监听端口
	netListen, _ := net.Listen("tcp", ":1215")

	defer netListen.Close()

	fmt.Println("Waiting for clients")

	for {
		conn, err := netListen.Accept()
		if err != nil {
			continue
		}
		fmt.Println("Accept a client")
		go handleConnection(conn)
	}
}

//处理连接
func handleConnection(conn net.Conn) {
	for {
		str := parseRESP(conn)
		switch value := str.(type) {
            case string:
                if len(value) == 0 {
					goto end
				}
				conn.Write([]byte(value))
			case []string:
				if ( value[0] == "SET" ) {
		            key := string(value[1])
		            val := string(value[2])
					kv_data[key] = val
		       		conn.Write([]byte("+OK\r\n"))
		        }else if ( value[0] == "GET" ){
		            key := string(value[1])
					val := string(kv_data[key])
					val_len := strconv.Itoa(len(val))
					str := "$"+val_len+"\r\n"+val+"\r\n"
		            conn.Write([]byte(str))
		        }else{
		            conn.Write([]byte("+OK\r\n"))
		        }
				break
            default:

		}
	}
	end:
	conn.Close()
}

func parseRESP(conn net.Conn) interface{} {
	r := bufio.NewReader(conn)
	line,err := r.ReadString('\n')
	if err != nil {
		return ""
	}
	cmd_type := string(line[0])
    cmd_txt := strings.Trim(string(line[1:]),"\r\n")

	switch cmd_type {
    	case "*":
 			count,_ := strconv.Atoi(cmd_txt)
            var data []string
			//"*3\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$7\r\nmyvalue\r\n"
            for i := 0; i < count; i++ {
				line,_ := r.ReadString('\n')
    			cmd_txt := strings.Trim(string(line[1:]),"\r\n")
				c,_ := strconv.Atoi(cmd_txt)
				length := c + 2
				str := ""
	            for length > 0 {
	                block,_ := r.Peek(length)
	                if  length != len(block) {

	                }
					r.Discard(length)
	                str += string(block)
	                length -= len(block)
	            }

            	data = append(data,strings.Trim(str,"\r\n"))
            }
			return data
    	default:
        	return cmd_txt
	}
 }

感兴趣的同学可以压测试对比下和PHP的区别

发表在 网站开发, 网站架构 | 标签为 , | 20 条评论

用php实现redis的服务端

大家都知道redis是用C来实现的,现在我用php来实现一个简单的仅支持SET和GET命令的redis服务端,主要是为了更好的了解redis的服务端和php的网络编程.

代码如下:

<?php
/**
 * 多进程阻塞式
 */
class Xtgxiso_server
{
    private $socket = false;
    private $process_num = 100;
    public $redis_kv_data = array();
    public $onMessage = null;

    function __construct($host="0.0.0.0",$port=1215)
    {
        $this->socket = stream_socket_server("tcp://".$host.":".$port,$errno, $errstr);
        if (!$this->socket) die($errstr."--".$errno);
        echo "listen $host $port \r\n";
        ini_set("memory_limit", "128M");
    }

    private function parseRESP(&$conn){
        $line = fgets($conn);
        if($line === '' || $line === false)
        {
            return null;
        }
        $type = $line[0];
        $line = mb_substr($line,1,-2);
        switch ( $type ){
            case "*":
                $count = (int) $line;
                $data = array();
                for ($i = 1; $i <= $count; $i++) {
                    $data[] = $this->parseRESP($conn);
                }
                return $data;
            case "$":
                if ($line == '-1') {
                    return null;
                }
                $length = $line + 2;
                $data = '';
                while ($length > 0) {
                    $block = fread($conn, $length);
                    if ($length !== strlen($block)) {
                        throw new Exception('RECEIVING');
                    }
                    $data .= $block;
                    $length -= mb_strlen($block);
                }
                return mb_substr($data, 0, -2);
        }
        return $line;
    }

    private function start_worker_process(){
        $pid = pcntl_fork();
        switch ($pid) {
            case -1:
                echo "fork error : {$i} \r\n";
                exit;
            case 0:
                while ( 1 ) {
                    echo  "waiting...\n";
                    $conn = stream_socket_accept($this->socket, -1);
                    if ( !$conn ){
                        continue;
                    }
                    //"*3\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$7\r\nmyvalue\r\n"
                    while(1){
                        $arr = $this->parseRESP($conn);
                        if ( is_array($arr) ) {
                            if ($this->onMessage) {
                                call_user_func($this->onMessage, $conn, $arr);
                            }
                        }else if ( $arr ){
                            if ($this->onMessage) {
                                call_user_func($this->onMessage, $conn, $arr);
                            }
                        }else{
                            fclose($conn);
                            break;
                        }
                    }
                }
            default:
                $this->pids[$pid] = $pid;
                break;
        }
    }

    public function run(){
        for($i = 1; $i <= $this->process_num; $i++){
            $this->start_worker_process();
        }

        while(1){
            foreach ($this->pids as $i => $pid) {
                if($pid) {
                    $res = pcntl_waitpid($pid, $status,WNOHANG);

                    if ( $res == -1 || $res > 0 ){
                        $this->start_worker_process();
                        unset($this->pids[$pid]);
                    }
                }
            }
            sleep(1);
        }
    }

}
$server =  new Xtgxiso_server();


$server->onMessage = function($conn,$info) use($server){
    if ( is_array($info) ){
        if ( $info["0"] == "SET" ) {
            $key = $info[1];
            $val = $info[2];
            $server->redis_kv_data[$key] = $val;
            fwrite($conn, "+OK\r\n");
        }else if ( $info["0"] == "GET" ){
            $key = $info[1];
            fwrite($conn, "$".strlen($server->redis_kv_data[$key])."\r\n".$server->redis_kv_data[$key]."\r\n");
        }else{
            fwrite($conn,"+OK\r\n");
        }
    }else{
        fwrite($conn,"+OK\r\n");
    }
};

$server->run();

通过如下命令来测试PHP实现的性能:

redis-benchmark -h 10.170.233.221 -p 1215 -t set -n 80000 -q

看来还是不错的,大家有兴趣可以再实现其他命令!

发表在 好文推荐, 网站架构 | 标签为 , | 21 条评论

用php语言实现redis的客户端

为了更好的了解redis协议,我们用php来实现一个支持大部份命令的客户端类.

redis的协议可参考这个文章http://redis.cn/topics/protocol.html

代码如下:

<?php
namespace xtgxiso;
class Redis {
    private $redis_socket = false;
    private $cmd = '';
    public function __construct($host='127.0.0.1',$port=6379,$timeout = 3) {
        $this->redis_socket = stream_socket_client("tcp://".$host.":".$port, $errno, $errstr,  $timeout);
        if ( !$this->redis_socket) {
            throw new Exception("{$errno} - {$errstr}");
        }
    }
    public function __destruct() {
        fclose($this->redis_socket);
    }
    public function __call($name, $args) {
        $crlf = "\r\n";
        array_unshift($args,$name);
        $command = '*' . count($args) . $crlf;
        foreach ($args as $arg) {
            $command .= '$' . strlen($arg) . $crlf . $arg . $crlf;
        }
        $fwrite = fwrite($this->redis_socket,$command);
        if ($fwrite === FALSE || $fwrite <= 0) {
            throw new Exception('Failed to write entire command to stream');
        }
        return $this->readResponse();
    }
    private function readResponse() {
        $reply = trim(fgets($this->redis_socket, 1024));
        switch (substr($reply, 0, 1)) {
            case '-':
                throw new Exception(trim(substr($reply, 4)));
                break;
            case '+':
                $response = substr(trim($reply), 1);
                if ($response === 'OK') {
                    $response = TRUE;
                }
                break;
            case '$':
                $response = NULL;
                if ($reply == '$-1') {
                    break;
                }
                $read = 0;
                $size = intval(substr($reply, 1));
                if ($size > 0) {
                    do {
                        $block_size = ($size - $read) > 1024 ? 1024 : ($size - $read);
                        $r = fread($this->redis_socket, $block_size);
                        if ($r === FALSE) {
                            throw new Exception('Failed to read response from stream');
                        } else {
                            $read += strlen($r);
                            $response .= $r;
                        }
                    } while ($read < $size);
                }
                fread($this->redis_socket, 2); /* discard crlf */
                break;
            /* Multi-bulk reply */
            case '*':
                $count = intval(substr($reply, 1));
                if ($count == '-1') {
                    return NULL;
                }
                $response = array();
                for ($i = 0; $i < $count; $i++) {
                    $response[] = $this->readResponse();
                }
                break;
            /* Integer reply */
            case ':':
                $response = intval(substr(trim($reply), 1));
                break;
            default:
                throw new RedisException("Unknown response: {$reply}");
                break;
        }
        return $response;
    }
}
/*
$redis = new Client_test();
var_dump($redis->auth("123456"));
var_dump($redis->set("xtgxiso",'abc'));
var_dump($redis->get("xtgxiso"));
*/

通过实现,我们基本了解redis的协议。

发表在 好文推荐, 网站开发, 网站架构 | 标签为 , | 23 条评论

Nginx的upstream连接池

一般常见的PHP架构是nginx+php-fpm,而这种架构影响性能的主要原因如下(引用worker的):

  1. 有一层nginx开销
  2. 有一层nginx到php-fpm的开销
  3. nginx到php-fpm默认是短连接,频繁建立连接又断开的开销
  4. 最致命的是php-fpm每次请求开始初始化一切又销毁一切的开销

那么能不能在不修改架构的情况下而优化呢?答案当然是可以的,我们可以优化第三点,将短连接修改为长连接,关键配置如下:

继续阅读

发表在 网站架构 | 标签为 | 23 条评论

socket服务的模型下的编程方式(同步和异步)

    前面我们花了一段时间来搭建高性能的socket服务,可以同时处理大量的连接,但这是在没有具体业务的情况下。

    如果我们启用了一个单进程的server,但里面的一个业务耗时1秒,那么在这1秒内是阻塞的,后续的请求会等待,如果并发三个请求,那么三个请求的执行时间会分别昌1秒,2秒,3秒.提高并发的方法有以下几种:

    1:多启动进程,提高并发数

    2:优化业务,减少耗时间相当于减少阻塞时间,提高并发数

    3:异步编程,避免阻塞,提高并发数

    这里我们重点介绍第三种方法,以访问第三方http为例。

    代码如下: 

<?php
//同步读取
function get_data_blocking(){
    $socket = stream_socket_client("tcp://test.raventech.cn:80", $errno, $errstr, 6);
    fwrite($socket, "GET /sleep1.php HTTP/1.0\r\nHost: test.raventech.cn\r\nAccept: */*\r\n\r\n");
    $str = "";
    while (!feof($socket)) {
        $str .= fgets($socket, 1024);
    }
    fclose($socket);
    return $str;
}

//异步读取
function get_data_unblocking(){
    $socket = stream_socket_client("tcp://test.raventech.cn:80", $errno, $errstr, 6);
    stream_set_blocking($socket, 0);
    fwrite($socket, "GET /sleep1.php HTTP/1.0\r\nHost: test.raventech.cn\r\nAccept: */*\r\n\r\n");
    $write  = NULL;
    $except = NULL;
    while( $socket ){
        $read   = array($socket);
        $num_changed_streams = stream_select($read, $write, $except, 0);
        if ( $num_changed_streams > 0 ) {
            foreach($read as $r){
                $str = fread($r,2048);
                fclose($socket);
                $socket = false;
                return $str;
            }
        }
        usleep(100);
    }
}

//真正的异步读取--利用server的IO复用事件来提高并发
class Get_data_event{

    public $onMessage = null;
    private $str='';

    function __construct(&$server){
        $socket = stream_socket_client("tcp://test.xtgxiso.cn:80", $errno, $errstr, 6);
        stream_set_blocking($socket, 0);
        fwrite($socket, "GET /sleep1.php HTTP/1.0\r\nHost: test.xtgxiso.cn\r\nAccept: */*\r\n\r\n");
        $server->add_socket($socket, array($this, 'read'));
    }

    public function read($socket){
        while (1) {
            $buffer = fread($socket, 1024);
            if ($buffer === '' || $buffer === false) {
                break;
            }
            $this->str .= $buffer;
        }
        if( $this->onMessage && $this->str ) {
            call_user_func($this->onMessage, $this->str);
        }
        $this->str = '';
        return false;
    }

}

/**
 * 单进程IO复用select
 */
class Xtgxiso_server
{
    public $socket = false;
    public $master = array();
    public $onConnect = null;
    public $onMessage = null;
    public $other_socket_callback = array();

    function __construct($host="0.0.0.0",$port=1215)
    {
        $this->socket = stream_socket_server("tcp://".$host.":".$port,$errno, $errstr);
        if (!$this->socket) die($errstr."--".$errno);
        stream_set_blocking($this->socket,0);
        $id = (int)$this->socket;
        $this->master[$id] = $this->socket;
    }

    public function add_socket($socket,$callback){
        $id = (int)$socket;
        $this->master[$id] = $socket;
        $this->other_socket_callback[$id] = $callback;
    }

    public function run(){
        $read = $this->master;
        $receive = array();
        echo  "start run...\n";
        while ( 1 ) {
            $read = $this->master;
            //echo  "waiting...\n";
            $mod_fd = @stream_select($read, $_w = NULL, $_e = NULL, 60);
            if ($mod_fd === FALSE) {
                break;
            }
            foreach ( $read as $k => $v ) {
                $id = (int)$v;
                if ( $v === $this->socket ) {
                    //echo "new conn\n";
                    $conn = stream_socket_accept($this->socket);
                    if ($this->onConnect) {
                        call_user_func($this->onConnect, $conn);
                    }
                    $id = (int)$conn;
                    $this->master[$id] = $conn;
                } else if ( @$this->other_socket_callback[$id] ){
                    call_user_func_array($this->other_socket_callback[$id], array($v));
                } else {
                    //echo "read data\n";
                    if ( !isset($receive[$k]) ){
                        $receive[$k]="";
                    }
                    $buffer = fread($v, 1024);
                    //echo $buffer."\n";
                    if ( strlen($buffer) === 0 ) {
                        if ( $this->onClose ){
                            call_user_func($this->onClose,$v);
                        }
                        fclose($v);
                        $id = (int)$v;
                        unset($this->master[$id]);
                    } else if ( $buffer === FALSE ) {
                        if ( $this->onClose ){
                            call_user_func($this->onClose, $this->master[$key_to_del]);
                        }
                        fclose($v);
                        $id = (int)$v;
                        unset($this->master[$id]);
                    } else {
                        $pos = strpos($buffer, "\r\n\r\n");
                        if ( $pos === false) {
                            $receive[$k] .= $buffer;
                            //echo "received:".$buffer.";not all package,continue recdiveing\n";
                        }else{
                            $receive[$k] .= trim(substr ($buffer,0,$pos+4));
                            $buffer = substr($buffer,$pos+4);
                            if($this->onMessage) {
                                call_user_func($this->onMessage,$v,$receive[$k]);
                            }
                            $receive[$k]='';
                        }
                    }
                }
            }
            usleep(10000);
        }
    }
}


$server =  new Xtgxiso_server();

$server->onConnect = function($conn){
    echo "onConnect -- accepted " . stream_socket_get_name($conn,true) . "\n";
};

$server->onMessage = function($conn,$msg) use ( $server ) {
    /*
    $respone ="";//响应内容
    $respone = "HTTP/1.1 200 OK\r\n";
    $respone .= "Server: openresty\r\n";
    $respone .= "Content-Type: text/html; charset=utf-8\r\n";
    $body = time().rand(111111,999999);
    $len = strlen($body);
    $respone .= "Content-Length:$len\r\n";
    $respone .= "Connection: close\r\n";
    $respone .= "\r\n$body\r\n\r\n";
    echo "onMessage --" . $msg . "\n";
    */

    //同步读取
    //$respone = get_data_blocking();
    //fwrite($conn,$respone);

    //异步读取
    //$respone = get_data_unblocking();
    //fwrite($conn,$respone);

    //真正异步
    $data = new Get_data_event($server);
    $data->onMessage = function($str) use($conn){
        fwrite($conn,$str);
    };

};

$server->onClose = function($conn){
    echo "onClose --" . "\n";
};

$server->run();

    第三方服务sleep1.php的代码比较简单

<?php
sleep(1);//模拟耗时
echo "OK";

    通过以上代码示例,我们分别注释运行 同步读取,异步读取,真正异步,来观察server的并发.测试方法可以写个test.html来模拟三个并发.

<script src="http://127.0.0.1:1215/?id=1"></script>
<script src="http://127.0.0.1:1215/?id=2"></script>
<script src="http://127.0.0.1:1215/?id=3"></script>

    通过测试发现,真正异步的是并发的,每个请求耗时1秒,这样我们总算明白什么是真正的非阻塞异步编程了,关键就在共用IO复用.

发表在 好文推荐, 网站开发, 网站架构 | 标签为 , | 25 条评论