Skip to content
This repository has been archived by the owner on Sep 1, 2020. It is now read-only.

Commit

Permalink
Use Chinese slash.
Browse files Browse the repository at this point in the history
  • Loading branch information
twose committed Apr 28, 2018
1 parent 7857342 commit f1fb7af
Show file tree
Hide file tree
Showing 78 changed files with 2,294 additions and 78 deletions.
156 changes: 78 additions & 78 deletions README.md

Large diffs are not rendered by default.

24 changes: 24 additions & 0 deletions doc/1.4.1 - sleep/usleep的影响.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# sleep/usleep的影响

在异步IO的程序中,__不得使用sleep/usleep/time_sleep_until/time_nanosleep__。(下文中使用`sleep`泛指所有睡眠函数)

* `sleep`函数会使进程陷入睡眠阻塞
* 直到指定的时间后操作系统才会重新唤醒当前的进程
* `sleep`过程中,只有信号可以打断
* 由于`Swoole`的信号处理是基于`signalfd`实现的,所以即使发送信号也无法中断`sleep`

`Swoole`提供的`swoole_event_add``swoole_timer_tick``swoole_timer_after``swoole_process::signal``异步swoole_client` 在进程sleep后会停止工作。`swoole_server`也无法再处理新的请求。


实例程序
----
```php
$serv = new swoole_server("127.0.0.1", 9501);
$serv->set(['worker_num' => 1]);
$serv->on('receive', function ($serv, $fd, $from_id, $data) {
sleep(100);
$serv->send($fd, 'Swoole: '.$data);
});
$serv->start();
```
`onReceive`事件中执行了`sleep`函数,server在100秒内无法再收到任何客户端请求。
26 changes: 26 additions & 0 deletions doc/1.4.2 - exit/die函数的影响.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# exit/die函数的影响

在swoole程序中禁止使用exit/die,如果PHP代码中有`exit/die`,当前工作的Worker进程、Task进程、User进程、以及`swoole_process`进程会立即退出。

建议使用`try/catch`的方式替换`exit/die`,实现中断执行跳出`PHP`函数调用栈。

```php
function swoole_exit($msg)
{
//php-fpm的环境
if (ENV=='php')
{
exit($msg);
}
//swoole的环境
else
{
throw new Swoole\ExitException($msg);
}
}
```

> 以上代码并未实现`ENV`常量和`Swoole\ExitException`,请自行实现
异常处理的方式比`exit/die`更友好,因为异常是可控的,`exit/die`不可控。在最外层进行try/catch即可捕获异常,仅终止当前的任务。Worker进程可以继续处理新的请求,而`exit/die`会导致进程直接退出,当前进程保存的所有变量和资源都会被销毁。如果进程内还有其他任务要处理,遇到`exit/die`也将全部丢弃。

107 changes: 107 additions & 0 deletions doc/10.5.3 - 使用类静态变量/全局变量保存上下文.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# 使用类静态变量/全局变量保存上下文

多个协程是并发执行的,因此不能使用类静态变量/全局变量保存协程上下文内容。使用局部变量是安全的,因为局部变量的值会自动保存在协程栈中,其他协程访问不到协程的局部变量。

实例
----
#### 错误的代码
```php
$_array = [];
$serv->on("Request", function ($req, $resp){
global $_array;
//请求 /a(协程 1 )
if ($request->server['request_uri'] == '/a') {
$_array['name'] = 'a';
co::sleep(1.0);
echo $_array['name'];
$resp->end($_array['name']);
}
//请求 /b(协程 2 )
else {
$_array['name'] = 'b';
$resp->end();
}
});
```

发起`2`个并发请求。
```shell
curl http://127.0.0.1:9501/a
curl http://127.0.0.1:9501/b
```

* 协程`1`中设置了全局变量`$_array['name']`的值为`a`
* 协程`1`调用`co::sleep`挂起
* 协程`2`执行,将`$_array['name']`的值为`b`,协程2结束
* 这时定时器返回,底层恢复协程1的运行。而协程1的逻辑中有一个上下文的依赖关系。当再次打印`$_array['name']`的值时,程序预期是`a`,但这个值已经被协程`2`所修改,实际结果却是`b`,这样就造成了逻辑错误
* 同理,使用类静态变量`Class::$array`、全局对象属性`$object->array`、其他超全局变量`$GLOBALS`等,进行上下文保存在协程程序中是非常危险的。可能会出现不符合预期的行为。

使用 Context 管理上下文
----
* 可以使用一个`Context`类来管理协程上下文,在`Context`类中,使用`Coroutine::getUid`获取了协程`ID`,然后隔离不同协程之间的全局变量
* 协程退出时清理上下文数据

#### Context
```php
use Swoole\Coroutine;

class Context
{
protected static $pool = [];

static function get($key)
{
$cid = Coroutine::getuid();
if ($cid < 0)
{
return null;
}
if(isset(self::$pool[$cid][$key])){
return self::$pool[$cid][$key];
}
return null;
}

static function put($key, $item)
{
$cid = Coroutine::getuid();
if ($cid > 0)
{
self::$pool[$cid][$key] = $item;
}

}

static function delete($key = null)
{
$cid = Coroutine::getuid();
if ($cid > 0)
{
if($key){
unset(self::$pool[$cid][$key]);
}else{
unset(self::$pool[$cid]);
}
}
}
}
```

#### 正确的代码
```php
$serv->on("Request", function ($req, $resp) {
if ($request->server['request_uri'] == '/a') {
Context::put('name', 'a');
co::sleep(1.0);
echo Context::get('name');
$resp->end(Context::get('name'));
//退出协程时清理
Context::delete('name');
} else {
Context::put('name', 'b');
$resp->end();
//退出协程时清理
Context::delete();
}
});
```
22 changes: 22 additions & 0 deletions doc/11.1 - Coroutine/Client.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Coroutine/Client

`Swoole\Coroutine\Client`提供了`TCP``UDP`传输协议`Socket`客户端的封装代码,使用时仅需`new Swoole\Coroutine\Client`即可。

* `Swoole\Coroutine\Client`底层实现协程调度,业务层无需感知
* 使用方法和`Swoole\Client`同步模式方法完全一致
* `connect`超时设置同时作用于`Connect``Recv``Send` 超时
* 除了正常的调用外,`Swoole\Coroutine\Client`还支持并行请求。
* 使用方法和`Swoole\Client`一致的此处不再列出,请参考 [Swoole\Client](/wiki/page/p-client.html),对于使用有区别的函数,此处单独说明

协程版实例
-----
```php
$client = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP);
if (!$client->connect('127.0.0.1', 9501, 0.5))
{
exit("connect failed. Error: {$client->errCode}\n");
}
$client->send("hello world\n");
echo $client->recv();
$client->close();
```
27 changes: 27 additions & 0 deletions doc/11.1.1 - Coroutine/Client->connect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Coroutine/Client->connect

连接到远程服务器,函数原型:
```php
bool $swoole_client->connect(string $host, int $port, float $timeout = 0.1)
```
connect方法接受4个参数:

* `$host`是远程服务器的地址,`2.0.12`或更高版本可直接传入域名,底层会自动进行协程切换解析域名为`IP`地址
* `$port`是远程服务器端口
* `$timeout`是网络`IO`的超时时间,包括`connect/send/recv`,单位是秒`s`,支持浮点数。默认为`0.1s`,即`100ms`,超时发生时,连接会被自动`close`
* `connect`操作会有一次协程切换开销,`connect`发起时`yield`,完成时`resume`

> 原先异步客户端不支持`recv`超时,现在协程版已经支持超时,复用上面的`$timeout`参数
-----
connect不会发生阻塞,connect事件触发后,切回PHP上下文。

```php
if ($cli->connect('127.0.0.1', 9501)) {
$cli->send("data");
} else {
echo "connect failed.";
}
```
如果连接失败,会返回false
> 超时后返回,检查$cli->errCode为110
11 changes: 11 additions & 0 deletions doc/11.1.2 - Coroutine/Client->send.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Coroutine/Client->send

发送数据,函数原型为
```php
$client->send(string $data);
```

* $data为发送的数据,必须为字符串类型,支持二进制数据
* 成功返回true,失败返回false
* send操作是立即返回的,没有协程切换

19 changes: 19 additions & 0 deletions doc/11.1.3 - Coroutine/Client->recv.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Coroutine/Client->recv

`recv`方法用于从服务器端接收数据。底层会自动`yield`,等待数据接收完成后自动切换到当前协程。

```php
function Coroutine\Client->recv(float $timeout = -1) : string;
```

* `recv`方法,不接受长度参数,当设置了通信协议后。`recv`会返回完整的数据
* 未设置通信协议返回原始的数据,需要`PHP`代码中自行实现网络协议的处理
* 服务端主动关闭连接,`recv`返回空字符串
* `recv`操作需要进行一次协程切换,在收到数据后进行`resume`
* `$timeout` 设置超时,单位为秒,浮点型,需要`2.1.2`或更高版本

超时设置
----
* 传入了`$timeout`,优先使用制定的`timeout`参数
* 未传入`$timeout`,但在`connect`时指定了超时时间,自动以`connect`超时时间作为`recv`超时时间
* 未传入`$timeout`,未设置`connect`超时,将设置为`-1`表示永不超时
10 changes: 10 additions & 0 deletions doc/11.1.4 - Coroutine/Client->close.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Coroutine/Client->close

关闭连接。`close`不存在阻塞,会立即返回。

```php
function Coroutine\Client->close() : bool;
```

* 执行成功返回true,失败返回false
* 关闭操作没有协程切换
11 changes: 11 additions & 0 deletions doc/11.1.5 - Coroutine/Client->peek.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Coroutine/Client->peek

窥视数据。`peek`方法直接操作`socket`,因此不会引起协程调度。

```php
function Coroutine\Client->peek(int $length = 65535) : string;
```

* `peek`方法仅用于窥视内核`socket`缓存区中的数据,不进行偏移。使用`peek`之后,再调用`recv`仍然可以读取到这部分数据
* `peek`方法是非阻塞的,它会立即返回。当`socket`缓存区中有数据时,会返回数据内容。缓存区为空时返回`false`,并设置`$client->errCode`
* 连接已被关闭`peek`会返回空字符串
44 changes: 44 additions & 0 deletions doc/11.2 - Coroutine/Http/Client.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Coroutine/Http/Client

Swoole-2.0.0版本增加了对协程版`Http`客户端的支持。底层是用纯C编写,拥有超高的性能。

> 协程版Http客户端基于原生的AsyncIo中的异步Http客户端,基本的设置和使用方法和异步Http客户端一致,不在需要注册回调函数,只需要同步写法即可,使用方法和Swoole\Http\Client一致的此处不再列出,请参考 [swoole\AsyncIO\异步Http/WebSocket客户端](/wiki/page/p-http_client.html),对于使用有区别的函数,此处单独说明
启用协程Http客户端
----
* 需要在编译swoole时增加`--enable-coroutine`来开启此功能。
* swoole_http_client不依赖任何第三方库
* 支持`Http-Chunk``Keep-Alive`特性,暂不支持`form-data`格式
* Http协议版本为`HTTP/1.1`
* `gzip`压缩格式支持需要依赖`zlib`

构造方法
---
```php
function Swoole\Coroutine\Http\Client->__construct(string $ip, int port, bool $ssl = false);
```

* $ip 目标服务器的IP地址,可使用`swoole_async_dns_lookup_coro`查询域名对应的IP地址
* $port 目标服务器的端口,一般`http``80``https``443`
* $ssl 是否启用`SSL/TLS`隧道加密,如果目标服务器是`https`必须设置`$ssl`参数为`true`


使用实例
----
```php
$cli = new Swoole\Coroutine\Http\Client('127.0.0.1', 80);
$cli->setHeaders([
'Host' => "localhost",
"User-Agent" => 'Chrome/49.0.2587.3',
'Accept' => 'text/html,application/xhtml+xml,application/xml',
'Accept-Encoding' => 'gzip',
]);
$cli->set([ 'timeout' => 1]);
$cli->get('/index.php');
echo $cli->body;
$cli->close();
```
* 如果未设置timeout,则将底层connect和IO回包都设为默认的500ms
#### defer特性
- - -
请参考[并发Client](http://wiki.swoole.com/wiki/page/p-coroutine_multi_call.html)一节。
25 changes: 25 additions & 0 deletions doc/11.2.2 - Coroutine/Http/Client->get.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Coroutine/Http/Client->get

发起`GET`请求,函数原型:
```php
function Swoole\Coroutine\Http\Client->get(string $path);
```

* $path 设置URL路径,如`/index.html`,注意这里不能传入`http://domain`
* 使用get会忽略`setMethod`设置的请求方法,强制使用`GET`

使用实例
----
```php
$cli = new Swoole\Coroutine\Http\Client('127.0.0.1', 80);
$cli->setHeaders([
'Host' => "localhost",
"User-Agent" => 'Chrome/49.0.2587.3',
'Accept' => 'text/html,application/xhtml+xml,application/xml',
'Accept-Encoding' => 'gzip',
]);

$cli->get('/index.php');
echo $cli->body;
$cli->close();
```
19 changes: 19 additions & 0 deletions doc/11.2.3 - Coroutine/Http/Client->post.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Coroutine/Http/Client->post

发起`POST`请求,函数原型:
```php
function Swoole\Coroutine\Http\Client->post(string $path, mixed $data);
```

* $path 设置URL路径,如`/index.html`,注意这里不能传入`http://domain`
* $data 请求的包体数据,如果$data为数组底层自动会打包为`x-www-form-urlencoded`格式的POST内容,并设置`Content-Type``application/x-www-form-urlencoded`
* 使用post会忽略`setMethod`设置的请求方法,强制使用`POST`

使用实例
----
```php
$cli = new Swoole\Coroutine\Http\Client('127.0.0.1', 80);
$cli->post('/post.php', array("a" => '1234', 'b' => '456'));
echo $cli->body;
$cli->close();
```
27 changes: 27 additions & 0 deletions doc/11.2.4 - Coroutine/Http/Client->upgrade.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Coroutine/Http/Client->upgrade

升级为`WebSocket`连接。

```php
function Coroutine\Http\Client->upgrade(string $path);
```

* 失败返回`false`,成功返回`true`
* 升级成功后可以使用`push`方法向服务器端推送消息,也可以调用`recv`接收消息
* `upgrade`会产生一次协程调度

使用实例
----
```php
go(function () {
$cli = new Co\http\Client("127.0.0.1", 9501);
$ret = $cli->upgrade("/");
if ($ret) {
while(true) {
$cli->push("hello");
var_dump($cli->recv());
co::sleep(0.1);
}
}
});
```
Loading

0 comments on commit f1fb7af

Please sign in to comment.