mirror of
https://gitee.com/hyperf/hyperf.git
synced 2024-11-30 02:37:58 +08:00
parent
44eb130336
commit
8024cc6ddb
22
bin/md-format
Executable file → Normal file
22
bin/md-format
Executable file → Normal file
@ -49,25 +49,11 @@ function replace($text)
|
||||
'$1$3$5',
|
||||
],
|
||||
|
||||
'cjk_hash' => [
|
||||
'([' . $cjk . '])(#(\S+))',
|
||||
'$1 $2',
|
||||
],
|
||||
|
||||
'hash_cjk' => [
|
||||
'((\S+)#)([' . $cjk . '])',
|
||||
'$1 $3',
|
||||
],
|
||||
|
||||
'cjk_operator_ans' => [
|
||||
'([' . $cjk . '])([A-Za-zΑ-Ωα-ω0-9])([\+\-\*\/=&\\|<>])',
|
||||
'$1 $2 $3',
|
||||
],
|
||||
|
||||
'bracket_cjk' => [
|
||||
'([' . $cjk . '])([\(`]+\w(.*?)\w[>\)`]+)([' . $cjk . '])',
|
||||
'$1 $2 $4',
|
||||
],
|
||||
|
||||
'ans_operator_cjk' => [
|
||||
'([\+\-\*\/=&\\|<>])([A-Za-zΑ-Ωα-ω0-9])([' . $cjk . '])',
|
||||
@ -75,18 +61,18 @@ function replace($text)
|
||||
],
|
||||
|
||||
'cjk_ans' => [
|
||||
'([' . $cjk . '])([A-Za-zΑ-Ωα-ω0-9@&%\=\$\^\\-\+\\/|\\\><])',
|
||||
'([' . $cjk . '])([A-Za-zΑ-Ωα-ω0-9@&%\=\$\^\\-\+\\\><])',
|
||||
'$1 $2',
|
||||
],
|
||||
|
||||
'ans_cjk' => [
|
||||
'([A-Za-zΑ-Ωα-ω0-9~!%&=;\|\,\.\?\$\^\\-\+\/\\\<>])([' . $cjk . '])',
|
||||
'([A-Za-zΑ-Ωα-ω0-9~!%&=;\,\.\?\$\^\\-\+\\\<>])([' . $cjk . '])',
|
||||
'$1 $2',
|
||||
],
|
||||
];
|
||||
$code = [];
|
||||
$i = 0;
|
||||
$text = preg_replace_callback('/```.*(\n|.)*?\n```/m', function ($match) use (&$code, &$i) {
|
||||
$text = preg_replace_callback('/```(\n|.)*?\n```/m', function ($match) use (&$code, &$i) {
|
||||
$code[++$i] = $match[0];
|
||||
return "__REPLACEMARK__{$i}__";
|
||||
}, $text);
|
||||
@ -97,5 +83,7 @@ function replace($text)
|
||||
return $code[$match[1]];
|
||||
}, $text);
|
||||
|
||||
echo 'markdown files formatted!'.PHP_EOL;
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
Hyperf 是基于 `Swoole 4.4+` 实现的高性能、高灵活性的 PHP 协程框架,内置协程服务器及大量常用的组件,性能较传统基于 `PHP-FPM` 的框架有质的提升,提供超高性能的同时,也保持着极其灵活的可扩展性,标准组件均基于 [PSR 标准](https://www.php-fig.org/psr) 实现,基于强大的依赖注入设计,保证了绝大部分组件或类都是 `可替换` 与 `可复用` 的。
|
||||
|
||||
框架组件库除了常见的协程版的 `MySQL 客户端`、`Redis 客户端`,还为您准备了协程版的 `Eloquent ORM`、`WebSocket 服务端及客户端`、`JSON RPC 服务端及客户端`、`GRPC 服务端及客户端`、`Zipkin/Jaeger (OpenTracing) 客户端`、`Guzzle HTTP 客户端`、`Elasticsearch 客户端`、`Consul 客户端`、`ETCD 客户端`、`AMQP 组件`、`Apollo 配置中心`、`阿里云 ACM 应用配置管理`、`ETCD 配置中心`、`基于令牌桶算法的限流器`、`通用连接池`、`熔断器`、`Swagger 文档生成`、`Swoole Tracker`、`Blade 和 Smarty 视图引擎`、`Snowflake 全局 ID 生成器` 等组件,省去了自己实现对应协程版本的麻烦。
|
||||
框架组件库除了常见的协程版的 `MySQL 客户端`、`Redis 客户端`,还为您准备了协程版的 `Eloquent ORM`、`WebSocket 服务端及客户端`、`JSON RPC 服务端及客户端`、`GRPC 服务端及客户端`、`Zipkin/Jaeger (OpenTracing) 客户端`、`Guzzle HTTP 客户端`、`Elasticsearch 客户端`、`Consul 客户端`、`ETCD 客户端`、`AMQP 组件`、`Apollo 配置中心`、`阿里云 ACM 应用配置管理`、`ETCD 配置中心`、`基于令牌桶算法的限流器`、`通用连接池`、`熔断器`、`Swagger 文档生成`、`Swoole Tracker`、`Blade 和 Smarty 视图引擎`、`Snowflake 全局ID生成器` 等组件,省去了自己实现对应协程版本的麻烦。
|
||||
|
||||
Hyperf 还提供了 `基于 PSR-11 的依赖注入容器`、`注解`、`AOP 面向切面编程`、`基于 PSR-15 的中间件`、`自定义进程`、`基于 PSR-14 的事件管理器`、`Redis/RabbitMQ 消息队列`、`自动模型缓存`、`基于 PSR-16 的缓存`、`Crontab 秒级定时任务`、`国际化`、`Validation 表单验证器` 等非常便捷的功能,满足丰富的技术场景和业务场景,开箱即用。
|
||||
|
||||
@ -21,4 +21,4 @@ Hyperf 还提供了 `基于 PSR-11 的依赖注入容器`、`注解`、`AOP 面
|
||||
|
||||
# 生产可用
|
||||
|
||||
我们为组件进行了大量的单元测试以保证逻辑的正确,同时维护了高质量的文档,在 Hyperf 正式对外开放之前,便已经过了严酷的生产环境的考验,我们才正式的对外开放该项目,至今,已有很多的大型 / 中小型互联网公司在生产环境使用 Hyperf。
|
||||
我们为组件进行了大量的单元测试以保证逻辑的正确,同时维护了高质量的文档,在 Hyperf 正式对外开放之前,便已经过了严酷的生产环境的考验,我们才正式的对外开放该项目,至今,已有很多的大型/中小型互联网公司在生产环境使用 Hyperf。
|
||||
|
@ -1,4 +1,4 @@
|
||||
# AMQP 组件
|
||||
# AMQP组件
|
||||
|
||||
[hyperf/amqp](https://github.com/hyperf/amqp) 是实现 AMQP 标准的组件,主要适用于对 RabbitMQ 的使用。
|
||||
|
||||
|
@ -19,7 +19,7 @@ Hyperf 使用了 [doctrine/annotations](https://github.com/doctrine/annotations)
|
||||
* @AnnotationClass()
|
||||
*/
|
||||
```
|
||||
在标准注释块内通过书写 `@AnnotationClass()` 这样的语法即表明对当前注释块所在位置的对象 (类、类方法、类属性) 进行了注解的定义, `AnnotationClass` 对应的是一个 `注解类` 的类名,可写全类的命名空间,亦可只写类名,但需要在当前类 `use` 该注解类以确保能够根据命名空间找到正确的注解类。
|
||||
在标准注释块内通过书写 `@AnnotationClass()` 这样的语法即表明对当前注释块所在位置的对象(类、类方法、类属性)进行了注解的定义, `AnnotationClass` 对应的是一个 `注解类` 的类名,可写全类的命名空间,亦可只写类名,但需要在当前类 `use` 该注解类以确保能够根据命名空间找到正确的注解类。
|
||||
|
||||
### 注解是如何发挥作用的?
|
||||
|
||||
@ -42,7 +42,7 @@ return [
|
||||
|
||||
## 使用注解
|
||||
|
||||
注解一共有 3 种应用对象,分别是 `类`、`类方法` 和 `类属性`。
|
||||
注解一共有3种应用对象,分别是 `类`、`类方法` 和 `类属性`。
|
||||
|
||||
### 使用类注解
|
||||
|
||||
|
@ -18,7 +18,7 @@ composer require hyperf/async-queue
|
||||
|:----------------:|:---------:|:-------------------------------------------:|:---------------------------------------:|
|
||||
| driver | string | Hyperf\AsyncQueue\Driver\RedisDriver::class | 无 |
|
||||
| channel | string | queue | 队列前缀 |
|
||||
| timeout | int | 2 | pop 消息的超时时间 |
|
||||
| timeout | int | 2 | pop消息的超时时间 |
|
||||
| retry_seconds | int,array | 5 | 失败后重新尝试间隔 |
|
||||
| handle_timeout | int | 10 | 消息处理超时时间 |
|
||||
| processes | int | 1 | 消费进程数 |
|
||||
|
@ -38,7 +38,7 @@
|
||||
## 依赖注入容器
|
||||
|
||||
- [hyperf/di](https://github.com/hyperf/di) Hyperf 官方提供的支持注解及 AOP 的依赖注入容器
|
||||
- [reasno/lazy-loader](https://github.com/Reasno/LazyLoader) 为 Hyperf DI 补充基于类型提示的懒加载注入。
|
||||
- [reasno/lazy-loader](https://github.com/Reasno/LazyLoader) 为Hyperf DI补充基于类型提示的懒加载注入。
|
||||
|
||||
## 服务
|
||||
|
||||
@ -99,14 +99,14 @@
|
||||
|
||||
- [hyperf/graphql](https://github.com/hyperf/graphql) Hyperf 官方提供的 Graphql 服务端组件 (beta)
|
||||
|
||||
## 热更新 / 热重载
|
||||
## 热更新/热重载
|
||||
|
||||
- [ha-ni-cc/hyperf-watch](https://github.com/ha-ni-cc/hyperf-watch) 一个基于 fswatch 实现的通用热更新组件
|
||||
- [mix-php/swoolefor](https://github.com/mix-php/swoolefor) 一个由 Mixphp 实现的通用热更新组件
|
||||
- [buexplain/go-watch](https://github.com/buexplain/go-watch) 一个基于 Go 语言实现的通用热更新组件
|
||||
- [remy/nodemon](https://github.com/remy/nodemon) 一个基于 node.js 实现的通用热更新组件
|
||||
|
||||
> Warning: 请勿于生产环境使用 `热更新 / 热重载` 功能
|
||||
> Warning: 请勿于生产环境使用 `热更新/热重载` 功能
|
||||
|
||||
## Swoole
|
||||
|
||||
@ -119,7 +119,7 @@
|
||||
|
||||
## 第三方 SDK
|
||||
|
||||
- [yurunsoft/pay-sdk](https://github.com/Yurunsoft/PaySDK) 支持 Swoole 协程的支付宝 / 微信支付 SDK
|
||||
- [yurunsoft/yurun-oauth-login](https://github.com/Yurunsoft/YurunOAuthLogin) 支持 Swoole 协程的第三方登录授权 SDK(QQ、微信、微博、Github、Gitee 等)
|
||||
- [yurunsoft/pay-sdk](https://github.com/Yurunsoft/PaySDK) 支持 Swoole 协程的支付宝/微信支付 SDK
|
||||
- [yurunsoft/yurun-oauth-login](https://github.com/Yurunsoft/YurunOAuthLogin) 支持 Swoole 协程的第三方登录授权 SDK(QQ、微信、微博、Github、Gitee等)
|
||||
- [overtrue/wechat](zh/sdks/wechat) EasyWeChat,一个流行的非官方微信 SDK
|
||||
- [Yurunsoft/PHPMailer-Swoole](https://github.com/Yurunsoft/PHPMailer-Swoole) Swoole 协程环境下的可用的 PHPMailer
|
||||
|
@ -11,7 +11,7 @@ composer require hyperf/cache
|
||||
|
||||
| 配置 | 默认值 | 备注 |
|
||||
|:------:|:----------------------------------------:|:---------------------:|
|
||||
| driver | Hyperf\Cache\Driver\RedisDriver | 缓存驱动,默认为 Redis |
|
||||
| driver | Hyperf\Cache\Driver\RedisDriver | 缓存驱动,默认为Redis |
|
||||
| packer | Hyperf\Utils\Packer\PhpSerializer | 打包器 |
|
||||
| prefix | c: | 缓存前缀 |
|
||||
|
||||
@ -42,7 +42,7 @@ $cache = $container->get(\Psr\SimpleCache\CacheInterface::class);
|
||||
### 注解方式
|
||||
|
||||
组件提供 `Hyperf\Cache\Annotation\Cacheable` 注解,作用于类方法,可以配置对应的缓存前缀、失效时间、监听器和缓存组。
|
||||
例如,UserService 提供一个 user 方法,可以查询对应 id 的用户信息。当加上 `Hyperf\Cache\Annotation\Cacheable` 注解后,会自动生成对应的 Redis 缓存,key 值为`user:id`,超时时间为 `9000` 秒。首次查询时,会从数据库中查,后面查询时,会从缓存中查。
|
||||
例如,UserService 提供一个 user 方法,可以查询对应id的用户信息。当加上 `Hyperf\Cache\Annotation\Cacheable` 注解后,会自动生成对应的Redis缓存,key值为`user:id`,超时时间为 `9000` 秒。首次查询时,会从数据库中查,后面查询时,会从缓存中查。
|
||||
|
||||
> 缓存注解基于 [aop](zh/aop.md) 和 [di](zh/di.md),所以只有在 `Container` 中获取到的对象实例才有效,比如通过 `$container->get` 和 `make` 方法所获得的对象,直接 `new` 出来的对象无法使用。
|
||||
|
||||
@ -188,13 +188,13 @@ public function updateUserBook(int $id)
|
||||
|
||||
## 缓存驱动
|
||||
|
||||
### Redis 驱动
|
||||
### Redis驱动
|
||||
|
||||
`Hyperf\Cache\Driver\RedisDriver` 会把缓存数据存放到 `Redis` 中,需要用户配置相应的 `Redis 配置`。此方式为默认方式。
|
||||
`Hyperf\Cache\Driver\RedisDriver` 会把缓存数据存放到 `Redis` 中,需要用户配置相应的 `Redis配置`。此方式为默认方式。
|
||||
|
||||
### 协程内存驱动
|
||||
|
||||
> 本驱动乃 Beta 版本,请谨慎使用。
|
||||
> 本驱动乃Beta版本,请谨慎使用。
|
||||
|
||||
如果您需要将数据缓存到 `Context` 中,可以尝试此驱动。例如以下应用场景 `Demo::get` 会在多个地方调用多次,但是又不想每次都到 `Redis` 中进行查询。
|
||||
|
||||
|
@ -46,8 +46,8 @@
|
||||
|
||||
- [#779](https://github.com/hyperf/hyperf/pull/779) 修复 `JPG` 文件验证不通过的问题。
|
||||
- [#787](https://github.com/hyperf/hyperf/pull/787) 修复 `db:seed` 参数 `--class` 多余,导致报错的问题。
|
||||
- [#795](https://github.com/hyperf/hyperf/pull/795) 修复自定义进程在异常抛出后,无法正常重启的 BUG。
|
||||
- [#796](https://github.com/hyperf/hyperf/pull/796) 修复 `etcd` 配置中心 `enable` 即时设为 `false`,在项目启动时,依然会拉取配置的 BUG。
|
||||
- [#795](https://github.com/hyperf/hyperf/pull/795) 修复自定义进程在异常抛出后,无法正常重启的BUG。
|
||||
- [#796](https://github.com/hyperf/hyperf/pull/796) 修复 `etcd` 配置中心 `enable` 即时设为 `false`,在项目启动时,依然会拉取配置的BUG。
|
||||
|
||||
## 优化
|
||||
|
||||
@ -384,7 +384,7 @@ Config Provider 内数据结构的变化:
|
||||
|
||||
## 变更
|
||||
|
||||
- [#297](https://github.com/hyperf/hyperf/pull/297) 如果服务注册失败,会于 10 秒后重试注册,且屏蔽了连接不上服务中心 (Consul) 而抛出的异常;
|
||||
- [#297](https://github.com/hyperf/hyperf/pull/297) 如果服务注册失败,会于 10 秒后重试注册,且屏蔽了连接不上服务中心(Consul)而抛出的异常;
|
||||
- [#298](https://github.com/hyperf/hyperf/pull/298) [#301](https://github.com/hyperf/hyperf/pull/301) 适配 `openzipkin/zipkin` v1.3.3+ 版本;
|
||||
|
||||
## 修复
|
||||
|
@ -91,7 +91,7 @@ return [
|
||||
|
||||
## 接入 Etcd 配置中心
|
||||
|
||||
- 安装 `Etcd 客户端`
|
||||
- 安装 `Etcd客户端`
|
||||
|
||||
```
|
||||
composer require hyperf/etcd
|
||||
@ -106,7 +106,7 @@ composer require start-point/etcd-php
|
||||
composer require linkorb/etcd-php
|
||||
```
|
||||
|
||||
- 添加 `Etcd 客户端` 配置文件 `etcd.php`
|
||||
- 添加 `Etcd客户端` 配置文件 `etcd.php`
|
||||
|
||||
```php
|
||||
<?php
|
||||
@ -119,13 +119,13 @@ return [
|
||||
];
|
||||
```
|
||||
|
||||
- 安装 `Etcd 配置中心`
|
||||
- 安装 `Etcd配置中心`
|
||||
|
||||
```
|
||||
composer require hyperf/config-etcd
|
||||
```
|
||||
|
||||
- 添加 `Etcd 配置中心` 配置文件 `config_etcd.php`
|
||||
- 添加 `Etcd配置中心` 配置文件 `config_etcd.php`
|
||||
|
||||
> mapping 为 `Etcd` 与 `Config` 的映射关系。映射中不存在的 `key`,则不会被同步到 `Config` 中。
|
||||
|
||||
|
@ -6,11 +6,11 @@ Hyperf 是运行于 `Swoole 4` 的协程之上的,这也是 Hyperf 能提供
|
||||
|
||||
### PHP-FPM 的运作模式
|
||||
|
||||
在聊协程是什么之前,我们先聊聊传统 `PHP-FPM` 架构的运作模式,`PHP-FPM` 是一个多进程的 `FastCGI` 管理程序,是绝大多数 `PHP` 应用所使用的运行模式。假设我们使用 `Nginx` 提供 `HTTP` 服务(`Apache` 同理),所有客户端发起的请求最先抵达的都是 `Nginx`,然后 `Nginx` 通过 `FastCGI` 协议将请求转发给 `PHP-FPM` 处理,`PHP-FPM` 的 `Worker 进程` 会抢占式的获得 CGI 请求进行处理,这个处理指的就是,等待 `PHP` 脚本的解析,等待业务处理的结果返回,完成后回收子进程,这整个的过程是阻塞等待的,也就意味着 `PHP-FPM` 的进程数有多少能处理的请求也就是多少,假设 `PHP-FPM` 有 `200` 个 `Worker 进程`,一个请求将耗费 `1` 秒的时间,那么简单的来说整个服务器理论上最多可以处理的请求也就是 `200` 个,`QPS` 即为 `200/s`,在高并发的场景下,这样的性能往往是不够的,尽管可以利用 `Nginx` 作为负载均衡配合多台 `PHP-FPM` 服务器来提供服务,但由于 `PHP-FPM` 的阻塞等待的工作模型,一个请求会占用至少一个 `MySQL` 连接,多节点高并发下会产生大量的 `MySQL` 连接,而 `MySQL` 的最大连接数默认值为 `100`,尽管可以修改,但显而易见该模式没法很好的应对高并发的场景。
|
||||
在聊协程是什么之前,我们先聊聊传统 `PHP-FPM` 架构的运作模式,`PHP-FPM` 是一个多进程的 `FastCGI` 管理程序,是绝大多数 `PHP` 应用所使用的运行模式。假设我们使用 `Nginx` 提供 `HTTP` 服务(`Apache` 同理),所有客户端发起的请求最先抵达的都是 `Nginx`,然后 `Nginx` 通过 `FastCGI` 协议将请求转发给 `PHP-FPM` 处理,`PHP-FPM` 的 `Worker进程` 会抢占式的获得 CGI 请求进行处理,这个处理指的就是,等待 `PHP` 脚本的解析,等待业务处理的结果返回,完成后回收子进程,这整个的过程是阻塞等待的,也就意味着 `PHP-FPM` 的进程数有多少能处理的请求也就是多少,假设 `PHP-FPM` 有 `200` 个 `Worker进程`,一个请求将耗费 `1` 秒的时间,那么简单的来说整个服务器理论上最多可以处理的请求也就是 `200` 个,`QPS` 即为 `200/s`,在高并发的场景下,这样的性能往往是不够的,尽管可以利用 `Nginx` 作为负载均衡配合多台 `PHP-FPM` 服务器来提供服务,但由于 `PHP-FPM` 的阻塞等待的工作模型,一个请求会占用至少一个 `MySQL` 连接,多节点高并发下会产生大量的 `MySQL` 连接,而 `MySQL` 的最大连接数默认值为 `100`,尽管可以修改,但显而易见该模式没法很好的应对高并发的场景。
|
||||
|
||||
### 异步非阻塞系统
|
||||
|
||||
在高并发的场景下,异步非阻塞就显得优势明显了,直观的优点表现就是 `Worker 进程` 不再同步阻塞的去处理一个请求,而是可以同时处理多个请求,无需 `I/O` 等待,并发能力极强,可以同时发起或维护大量的请求。那么最直观的缺点大家可能也都知道,就是永无止境的回调,业务逻辑必须在对应的回调函数内实现,如果业务逻辑存在多次的 `I/O` 请求,则会存在很多层的回调函数,下面示例一段 `Swoole 1.x` 下的异步回调型的伪代码片段。
|
||||
在高并发的场景下,异步非阻塞就显得优势明显了,直观的优点表现就是 `Worker进程` 不再同步阻塞的去处理一个请求,而是可以同时处理多个请求,无需 `I/O` 等待,并发能力极强,可以同时发起或维护大量的请求。那么最直观的缺点大家可能也都知道,就是永无止境的回调,业务逻辑必须在对应的回调函数内实现,如果业务逻辑存在多次的 `I/O` 请求,则会存在很多层的回调函数,下面示例一段 `Swoole 1.x` 下的异步回调型的伪代码片段。
|
||||
|
||||
```php
|
||||
$db = new swoole_mysql();
|
||||
@ -48,9 +48,9 @@ Swoole 协程也是对异步回调的一种解决方案,在 `PHP` 语言下,
|
||||
|
||||
### 协程是什么?
|
||||
|
||||
我们已经知道了协程可以很好的解决异步非阻塞系统的开发问题,那么协程本身到底是什么呢?从定义上来说,*协程是一种轻量级的线程,由用户代码来调度和管理,而不是由操作系统内核来进行调度,也就是在用户态进行*。可以直接的理解为就是一个非标准的线程实现,但什么时候切换由用户自己来实现,而不是由操作系统分配 `CPU` 时间决定。具体来说,`Swoole` 的每个 `Worker 进程` 会存在一个协程调度器来调度协程,协程切换的时机就是遇到 `I/O` 操作或代码显性切换时,进程内以单线程的形式运行协程,也就意味着一个进程内同一时间只会有一个协程在运行且切换时机明确,也就无需处理像多线程编程下的各种同步锁的问题。
|
||||
单个协程内的代码运行仍是串行的,放在一个 HTTP 协程服务上来理解就是每一个请求是一个协程,举个例子,假设为请求 A 创建了 `协程 A`,为 `请求 B` 创建了 `协程 B`,那么在处理 `协程 A` 的时候代码跑到了查询 `MySQL` 的语句上,这个时候 `协程 A` 则会触发协程切换,`协程 A` 就继续等待 `I/O` 设备返回结果,那么此时就会切换到 `协程 B`,开始处理 `协程 B` 的逻辑,当又遇到了一个 `I/O` 操作便又触发协程切换,再回过来从 `协程 A` 刚才切走的地方继续执行,如此反复,遇到 `I/O` 操作就切换到另一个协程去继续执行而非一直阻塞等待。
|
||||
这里可以发现一个问题就是,*`协程 A` 的 `MySQL` 查询操作必须得是一个异步非阻塞的操作,否则会由于阻塞导致协程调度器没法切换到另一个协程继续执行*,这个也是要在协程编程下需要规避的问题之一。
|
||||
我们已经知道了协程可以很好的解决异步非阻塞系统的开发问题,那么协程本身到底是什么呢?从定义上来说,*协程是一种轻量级的线程,由用户代码来调度和管理,而不是由操作系统内核来进行调度,也就是在用户态进行*。可以直接的理解为就是一个非标准的线程实现,但什么时候切换由用户自己来实现,而不是由操作系统分配 `CPU` 时间决定。具体来说,`Swoole` 的每个 `Worker进程` 会存在一个协程调度器来调度协程,协程切换的时机就是遇到 `I/O` 操作或代码显性切换时,进程内以单线程的形式运行协程,也就意味着一个进程内同一时间只会有一个协程在运行且切换时机明确,也就无需处理像多线程编程下的各种同步锁的问题。
|
||||
单个协程内的代码运行仍是串行的,放在一个 HTTP 协程服务上来理解就是每一个请求是一个协程,举个例子,假设为请求A创建了 `协程A`,为 `请求B` 创建了 `协程B`,那么在处理 `协程A` 的时候代码跑到了查询 `MySQL` 的语句上,这个时候 `协程A` 则会触发协程切换,`协程A` 就继续等待 `I/O` 设备返回结果,那么此时就会切换到 `协程B`,开始处理 `协程B` 的逻辑,当又遇到了一个 `I/O` 操作便又触发协程切换,再回过来从 `协程A` 刚才切走的地方继续执行,如此反复,遇到 `I/O` 操作就切换到另一个协程去继续执行而非一直阻塞等待。
|
||||
这里可以发现一个问题就是,*`协程A` 的 `MySQL` 查询操作必须得是一个异步非阻塞的操作,否则会由于阻塞导致协程调度器没法切换到另一个协程继续执行*,这个也是要在协程编程下需要规避的问题之一。
|
||||
|
||||
### 协程与普通线程有哪些区别?
|
||||
|
||||
@ -71,7 +71,7 @@ Swoole 协程也是对异步回调的一种解决方案,在 `PHP` 语言下,
|
||||
在 `Swoole` 的持久化应用下,一个 `Worker` 内的全局变量是 `Worker` 内共享的,而从协程的介绍我们可以知道同一个 `Worker` 内还会存在多个协程并存在协程切换,也就意味着一个 `Worker` 会在一个时间周期内同时处理多个协程(或直接理解为请求)的代码,也就意味着如果使用了全局变量来储存状态可能会被多个协程所使用,也就是说不同的请求之间可能会混淆数据,这里的全局变量指的是 `$_GET/$_POST/$_REQUEST/$_SESSION/$_COOKIE/$_SERVER`等`$_`开头的变量、`global` 变量,以及 `static` 静态属性。
|
||||
那么当我们需要使用到这些特性时应该怎么办?
|
||||
|
||||
对于全局变量,均是跟随着一个 `请求(Request)` 而产生的,而 `Hyperf` 的 `请求(Request)/ 响应(Response)` 是由 [hyperf/http-message](https://github.com/hyperf/http-message) 通过实现 [PSR-7](https://www.php-fig.org/psr/psr-7/) 处理的,故所有的全局变量均可以在 `请求(Request)` 对象中得到相关的值;
|
||||
对于全局变量,均是跟随着一个 `请求(Request)` 而产生的,而 `Hyperf` 的 `请求(Request)/响应(Response)` 是由 [hyperf/http-message](https://github.com/hyperf/http-message) 通过实现 [PSR-7](https://www.php-fig.org/psr/psr-7/) 处理的,故所有的全局变量均可以在 `请求(Request)` 对象中得到相关的值;
|
||||
|
||||
对于 `global` 变量和 `static` 变量,在 `PHP-FPM` 模式下,本质都是存活于一个请求生命周期内的,而在 `Hyperf` 内因为是 `CLI` 应用,会存在 `全局周期` 和 `请求周期(协程周期)` 两种长生命周期。
|
||||
- 全局周期,我们只需要创建一个静态变量供全局调用即可,静态变量意味着在服务启动后,任意协程和代码逻辑均共享此静态变量内的数据,也就意味着存放的数据不能是特别服务于某一个请求或某一个协程;
|
||||
@ -221,7 +221,7 @@ for ($i = 0; $i < 15; ++$i) {
|
||||
|
||||
### 协程上下文
|
||||
|
||||
由于同一个进程内协程间是内存共享的,但协程的执行 / 切换是非顺序的,也就意味着我们很难掌控当前的协程是哪一个*(事实上可以,但通常没人这么干)*,所以我们需要在发生协程切换时能够同时切换对应的上下文。
|
||||
由于同一个进程内协程间是内存共享的,但协程的执行/切换是非顺序的,也就意味着我们很难掌控当前的协程是哪一个*(事实上可以,但通常没人这么干)*,所以我们需要在发生协程切换时能够同时切换对应的上下文。
|
||||
在 Hyperf 里实现协程的上下文管理将非常简单,基于 `Hyperf\Utils\Context` 类的 `set(string $id, $value)`、`get(string $id, $default = null)`、`has(string $id)`、`override(string $id, \Closure $closure)` 静态方法即可完成上下文数据的管理,通过这些方法设置和获取的值,都仅限于当前的协程,在协程结束时,对应的上下文也会自动跟随释放掉,无需手动管理,无需担忧内存泄漏的风险。
|
||||
|
||||
#### Hyperf\Utils\Context::set()
|
||||
@ -280,7 +280,7 @@ $request = Context::override(ServerRequestInterface::class, function (ServerRequ
|
||||
|
||||
### Swoole Runtime Hook Level
|
||||
|
||||
框架在入口函数中提供了 `SWOOLE_HOOK_FLAGS` 常量,如果您需要修改整个项目的 `Runtime Hook` 等级,比如想要支持 `CURL 协程`,可以修改这里的代码,如下。
|
||||
框架在入口函数中提供了 `SWOOLE_HOOK_FLAGS` 常量,如果您需要修改整个项目的 `Runtime Hook` 等级,比如想要支持 `CURL协程`,可以修改这里的代码,如下。
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
@ -155,4 +155,4 @@ return [
|
||||
## 运行定时任务
|
||||
|
||||
当您完成上述的配置后,以及定义了定时任务后,只需要直接启动 `Server`,定时任务便会一同启动。
|
||||
在您启动后,即便您定义了足够短周期的定时任务,定时任务也不会马上开始执行,所有定时任务都会等到下一个分钟周期时才会开始执行,比如您启动的时候是 `10 时 11 分 12 秒`,那么定时任务会在 `10 时 12 分 00 秒` 才会正式开始执行。
|
||||
在您启动后,即便您定义了足够短周期的定时任务,定时任务也不会马上开始执行,所有定时任务都会等到下一个分钟周期时才会开始执行,比如您启动的时候是 `10时11分12秒`,那么定时任务会在 `10时12分00秒` 才会正式开始执行。
|
||||
|
@ -1,4 +1,4 @@
|
||||
# 极简的 DB 组件
|
||||
# 极简的DB组件
|
||||
|
||||
[hyperf/database](https://github.com/hyperf/database) 功能十分强大,但也不可否认效率上确实些许不足。这里提供一个极简的 `DB` 组件,支持 `PDO` 和 `Swoole Mysql`。
|
||||
|
||||
@ -13,12 +13,12 @@
|
||||
| driver | string | 无 | 数据库引擎 支持 `pdo` 和 `mysql` |
|
||||
| host | string | `localhost` | 数据库地址 |
|
||||
| port | int | 3306 | 数据库地址 |
|
||||
| database | string | 无 | 数据库默认 DB |
|
||||
| database | string | 无 | 数据库默认DB |
|
||||
| username | string | 无 | 数据库用户名 |
|
||||
| password | string | null | 数据库密码 |
|
||||
| charset | string | utf8 | 数据库编码 |
|
||||
| collation | string | utf8_unicode_ci | 数据库编码 |
|
||||
| fetch_mode | int | `PDO::FETCH_ASSOC` | PDO 查询结果集类型 |
|
||||
| fetch_mode | int | `PDO::FETCH_ASSOC` | PDO查询结果集类型 |
|
||||
| pool.min_connections | int | 1 | 连接池内最少连接数 |
|
||||
| pool.max_connections | int | 10 | 连接池内最大连接数 |
|
||||
| pool.connect_timeout | float | 10.0 | 连接等待超时时间 |
|
||||
@ -36,14 +36,14 @@
|
||||
| beginTransaction | void | 开启事务 支持事务嵌套 |
|
||||
| commit | void | 提交事务 支持事务嵌套 |
|
||||
| rollBack | void | 回滚事务 支持事务嵌套 |
|
||||
| insert | int | 插入数据,返回主键 ID,非自增主键返回 0 |
|
||||
| execute | int | 执行 SQL,返回受影响的行数 |
|
||||
| query | array | 查询 SQL |
|
||||
| fetch | array | object| 查询 SQL,返回首行数据 |
|
||||
| insert | int | 插入数据,返回主键ID,非自增主键返回 0 |
|
||||
| execute | int | 执行SQL,返回受影响的行数 |
|
||||
| query | array | 查询SQL |
|
||||
| fetch | array | object|查询SQL,返回首行数据 |
|
||||
|
||||
## 使用
|
||||
|
||||
### 使用 DB 实例
|
||||
### 使用DB实例
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
得益于 [hyperf/event](https://github.com/hyperf/event) 组件的支撑,用户可以很方便的对以下事件进行监听。
|
||||
例如 `QueryExecuted` , `StatementPrepared` , `TransactionBeginning` , `TransactionCommitted` , `TransactionRolledBack` 。
|
||||
接下来我们就实现一个记录 SQL 的监听器,来说一下怎么使用。
|
||||
接下来我们就实现一个记录SQL的监听器,来说一下怎么使用。
|
||||
首先我们定义好 `DbQueryExecutedListener` ,实现 `Hyperf\Event\Contract\ListenerInterface` 接口并对类定义 `Hyperf\Event\Annotation\Listener` 注解,这样 Hyperf 就会自动把该监听器注册到事件调度器中,无需任何手动配置,示例代码如下:
|
||||
|
||||
```php
|
||||
@ -76,7 +76,7 @@ class DbQueryExecutedListener implements ListenerInterface
|
||||
|:------------:|:----------------:|:--------:|:-------------------------- --:|
|
||||
| booting | 模型首次加载前 | 否 | 进程生命周期中只会触发一次 |
|
||||
| booted | 模型首次加载后 | 否 | 进程生命周期中只会触发一次 |
|
||||
| retrieved | 填充数据后 | 否 | 每当模型从 DB 或缓存查询出来后触发 |
|
||||
| retrieved | 填充数据后 | 否 | 每当模型从DB或缓存查询出来后触发 |
|
||||
| creating | 数据创建时 | 是 | |
|
||||
| created | 数据创建后 | 否 | |
|
||||
| updating | 数据更新时 | 是 | |
|
||||
|
@ -1,6 +1,6 @@
|
||||
# 模型缓存
|
||||
|
||||
在高频场景下,我们会频繁的查询数据库,虽然有主键加持,但也会影响到数据库性能。这种 kv 查询方式,我们可以很方便的使用 `模型缓存` 来减缓数据库压力。本模块实现了自动缓存,删除和修改模型时,自动删除缓存。累加、减操作时,直接操作缓存进行对应累加、减。
|
||||
在高频场景下,我们会频繁的查询数据库,虽然有主键加持,但也会影响到数据库性能。这种kv查询方式,我们可以很方便的使用 `模型缓存` 来减缓数据库压力。本模块实现了自动缓存,删除和修改模型时,自动删除缓存。累加、减操作时,直接操作缓存进行对应累加、减。
|
||||
|
||||
> 模型缓存暂支持 `Redis`存储,其他存储引擎会慢慢补充。
|
||||
|
||||
@ -17,11 +17,11 @@ composer require hyperf/model-cache
|
||||
| 配置 | 类型 | 默认值 | 备注 |
|
||||
|:---------------:|:------:|:-----------------------------------------------------:|:-----------------------------------:|
|
||||
| handler | string | Hyperf\DbConnection\Cache\Handler\RedisHandler::class | 无 |
|
||||
| cache_key | string | `mc:%s:m:%s:%s:%s` | `mc:缓存前缀:m:表名:主键 KEY:主键值` |
|
||||
| cache_key | string | `mc:%s:m:%s:%s:%s` | `mc:缓存前缀:m:表名:主键KEY:主键值` |
|
||||
| prefix | string | db connection name | 缓存前缀 |
|
||||
| ttl | int | 3600 | 超时时间 |
|
||||
| empty_model_ttl | int | 60 | 查询不到数据时的超时时间 |
|
||||
| load_script | bool | true | Redis 引擎下 是否使用 evalSha 代替 eval |
|
||||
| load_script | bool | true | Redis引擎下 是否使用evalSha代替eval |
|
||||
|
||||
```php
|
||||
<?php
|
||||
@ -58,7 +58,7 @@ return [
|
||||
|
||||
## 使用
|
||||
|
||||
模型缓存的使用十分简单,只需要在对应 Model 中实现 `Hyperf\ModelCache\CacheableInterface` 接口,当然,框架已经提供了对应实现,只需要引入 `Hyperf\ModelCache\Cacheable` Trait 即可。
|
||||
模型缓存的使用十分简单,只需要在对应Model中实现 `Hyperf\ModelCache\CacheableInterface` 接口,当然,框架已经提供了对应实现,只需要引入 `Hyperf\ModelCache\Cacheable` Trait 即可。
|
||||
|
||||
```php
|
||||
<?php
|
||||
@ -107,7 +107,7 @@ $models = User::findManyFromCache($ids);
|
||||
|
||||
```
|
||||
|
||||
对应 Redis 数据如下,其中 `HF-DATA:DEFAULT` 作为占位符存在于 `HASH` 中,*所以用户不要使用 `HF-DATA` 作为数据库字段*。
|
||||
对应Redis数据如下,其中 `HF-DATA:DEFAULT` 作为占位符存在于 `HASH` 中,*所以用户不要使用 `HF-DATA` 作为数据库字段*。
|
||||
```
|
||||
127.0.0.1:6379> hgetall "mc:default:m:user:id:1"
|
||||
1) "id"
|
||||
|
@ -27,7 +27,7 @@ $ php bin/hyperf.php db:model table_name
|
||||
| --inheritance | string | `Model` | 父类 |
|
||||
| --uses | string | `App\Model\Model` | 配合 `inheritance` 使用 |
|
||||
| --refresh-fillable | bool | `false` | 是否刷新 `fillable` 参数 |
|
||||
| --table-mapping | array | `[]` | 为表名 -> 模型增加映射关系 比如 ['users:Account'] |
|
||||
| --table-mapping | array | `[]` | 为表名->模型增加映射关系 比如 ['users:Account'] |
|
||||
| --ignore-tables | array | `[]` | 不需要生成模型的表名 比如 ['users'] |
|
||||
| --with-comments | bool | `false` | 是否增加字段注释 |
|
||||
|
||||
@ -335,7 +335,7 @@ use App\Model\User;
|
||||
$count = User::query()->where('gender', 1)->count();
|
||||
```
|
||||
|
||||
## 插入 & 更新模型
|
||||
## 插入&更新模型
|
||||
|
||||
### 插入
|
||||
|
||||
|
@ -19,7 +19,7 @@ $users = Db::table('user')->get();
|
||||
$users = Db::table('user')->select('name', 'gender as user_gender')->get();
|
||||
```
|
||||
|
||||
`Db::select()` 方法会返回一个 array,而 `get` 方法会返回 `Hyperf\Utils\Collection`。其中元素是 `stdClass`,所以可以通过以下代码返回各个元素的数据
|
||||
`Db::select()` 方法会返回一个array,而 `get` 方法会返回 `Hyperf\Utils\Collection`。其中元素是 `stdClass`,所以可以通过以下代码返回各个元素的数据
|
||||
|
||||
```php
|
||||
<?php
|
||||
@ -207,7 +207,7 @@ $users = $query->addSelect('age')->get();
|
||||
|
||||
## 原始表达式
|
||||
|
||||
有时你需要在查询中使用原始表达式,例如实现 `COUNT(0) AS count`,这就需要用到 `raw` 方法。
|
||||
有时你需要在查询中使用原始表达式,例如实现 `COUNT(0) AS count`,这就需要用到`raw`方法。
|
||||
|
||||
```php
|
||||
use Hyperf\DbConnection\Db;
|
||||
|
@ -28,7 +28,7 @@ composer require hyperf/database
|
||||
|:--------------------:|:------:|:---------------:|:------------------:|
|
||||
| driver | string | 无 | 数据库引擎 |
|
||||
| host | string | 无 | 数据库地址 |
|
||||
| database | string | 无 | 数据库默认 DB |
|
||||
| database | string | 无 | 数据库默认DB |
|
||||
| username | string | 无 | 数据库用户名 |
|
||||
| password | string | null | 数据库密码 |
|
||||
| charset | string | utf8 | 数据库编码 |
|
||||
|
@ -319,7 +319,7 @@ $userService = make(UserService::class, ['enableCache' => true]);
|
||||
|
||||
## 获取容器对象
|
||||
|
||||
有些时候我们可能希望去实现一些更动态的需求时,会希望可以直接获取到 `容器(Container)` 对象,在绝大部分情况下,框架的入口类(比如命令类、控制器、RPC 服务提供者等)都是由 `容器(Container)` 创建并维护的,也就意味着您所写的绝大部分业务代码都是在 `容器(Container)` 的管理作用之下的,也就意味着在绝大部分情况下您都可以通过在 `构造函数(Constructor)` 声明或通过 `@Inject` 注解注入 `Psr\Container\ContainerInterface` 接口类都能够获得 `Hyperf\Di\Container` 容器对象,我们通过代码来演示一下:
|
||||
有些时候我们可能希望去实现一些更动态的需求时,会希望可以直接获取到 `容器(Container)` 对象,在绝大部分情况下,框架的入口类(比如命令类、控制器、RPC服务提供者等)都是由 `容器(Container)` 创建并维护的,也就意味着您所写的绝大部分业务代码都是在 `容器(Container)` 的管理作用之下的,也就意味着在绝大部分情况下您都可以通过在 `构造函数(Constructor)` 声明或通过 `@Inject` 注解注入 `Psr\Container\ContainerInterface` 接口类都能够获得 `Hyperf\Di\Container` 容器对象,我们通过代码来演示一下:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
@ -17,6 +17,6 @@ Hyperf 是采用 MIT 许可的开源项目,使用完全免费。但是随着
|
||||
|
||||
如果您是企业经营者并且将 Hyperf 用在商业产品中,那么赞助 Hyperf 有商业上的益处:可以让您的产品所依赖的框架保持健康并得到积极的升级维护,也能帮助您在 Hyperf 社区里获得更高的曝光度,从而更容易地吸引到 Hyperf 开发者。
|
||||
|
||||
周期性赞助可以获得额外的回报,比如您的名字或组织 / 公司 Logo 及链接会出现在 Hyperf 的 GitHub 仓库中。
|
||||
周期性赞助可以获得额外的回报,比如您的名字或组织/公司 Logo 及链接会出现在 Hyperf 的 GitHub 仓库中。
|
||||
|
||||
> 如您希望为 Hyperf 团队提供周期性的赞助,可加入官方的微信群或 QQ 群并与群主联系,或邮件至 h@hyperf.io
|
||||
|
@ -176,7 +176,7 @@ class UserService
|
||||
|
||||
1. 这个时候还不是协程环境,如果 `Listener` 中注入了可能会触发协程切换的类,就会导致框架启动失败。
|
||||
2. 运行 `di:init-proxy` 脚本时,因为实例化了 `EventDispatcherInterface`,进而导致所有的 `Listener` 实例化,一旦这个过程生成了代理对象(.proxy.php 扩展名的类),而脚本内部又有删除代理类的逻辑,就会导致代理类生成有误。
|
||||
3. 条件与上述一致,只不过代理类又配置了别名,会导致生成这个别名对象时,因为判断代理类不存在,则会重新生成,但 `Ast` 已经生成了注解树,并被修改为代理类的注解树(Ast 注解树内部节点使用了引用),则会导致代理类生成有误。【上述两个问题会在后面的版本修复,修改 `di:init-proxy` 脚本不再删除缓存】
|
||||
3. 条件与上述一致,只不过代理类又配置了别名,会导致生成这个别名对象时,因为判断代理类不存在,则会重新生成,但 `Ast` 已经生成了注解树,并被修改为代理类的注解树(Ast注解树内部节点使用了引用),则会导致代理类生成有误。【上述两个问题会在后面的版本修复,修改 `di:init-proxy` 脚本不再删除缓存】
|
||||
|
||||
### `BootApplication` 事件尽量避免 IO 操作
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
# 异常处理器
|
||||
|
||||
在 `Hyperf` 里,业务代码都运行在 `Worker 进程` 上,也就意味着一旦任意一个请求的业务存在没有捕获处理的异常的话,都会导致对应的 `Worker 进程` 被中断退出,虽然被中断的 `Worker 进程` 仍会被重新拉起,但对服务而言也是不能接受的,且捕获异常并输出合理的报错内容给客户端也是更加友好的。
|
||||
在 `Hyperf` 里,业务代码都运行在 `Worker进程` 上,也就意味着一旦任意一个请求的业务存在没有捕获处理的异常的话,都会导致对应的 `Worker进程` 被中断退出,虽然被中断的 `Worker进程` 仍会被重新拉起,但对服务而言也是不能接受的,且捕获异常并输出合理的报错内容给客户端也是更加友好的。
|
||||
我们可以通过对各个 `server` 定义不同的 `异常处理器(ExceptionHandler)`,一旦业务流程存在没有捕获的异常,都会被传递到已注册的 `异常处理器(ExceptionHandler)` 去处理。
|
||||
|
||||
## 自定义一个异常处理
|
||||
|
@ -37,7 +37,7 @@ class Foo {
|
||||
}
|
||||
```
|
||||
|
||||
## 使用 Swoole 配置
|
||||
## 使用Swoole配置
|
||||
|
||||
有时候我们想直接修改 `Swoole` 配置,所以我们也提供了相关配置项,不过这项配置在 `Curl Guzzle 客户端` 中是无法生效的,所以谨慎使用。
|
||||
|
||||
@ -69,7 +69,7 @@ Hyperf 除了实现了 `Hyperf\Guzzle\CoroutineHandler` 外,还基于 `Hyperf\
|
||||
|
||||
### 原因
|
||||
|
||||
简单来说,主机 TCP 连接数 是有上限的,当我们并发大到超过这个上限值时,就导致请求无法正常建立。另外,TCP 连接结束后还会有一个 TIME-WAIT 阶段,所以也无法实时释放连接。这就导致了实际并发可能远低于 TCP 上限值。所以,我们需要一个连接池来维持这个阶段,尽量减少 TIME-WAIT 造成的影响,让 TCP 连接进行复用。
|
||||
简单来说,主机 TCP连接数 是有上限的,当我们并发大到超过这个上限值时,就导致请求无法正常建立。另外,TCP连接结束后还会有一个 TIME-WAIT 阶段,所以也无法实时释放连接。这就导致了实际并发可能远低于 TCP 上限值。所以,我们需要一个连接池来维持这个阶段,尽量减少 TIME-WAIT 造成的影响,让TCP连接进行复用。
|
||||
|
||||
### 使用
|
||||
|
||||
|
@ -296,7 +296,7 @@ return [
|
||||
];
|
||||
```
|
||||
|
||||
### 返回 PHP 对象
|
||||
### 返回PHP对象
|
||||
|
||||
当框架导入 `symfony/serialize (^4.3)` 和 `symfony/property-access (^4.3)` 后,`Hyperf\Contract\NormalizerInterface` 的实现会自动使用 `Hyperf\Utils\Serializer\SymfonyNormalizer` 而非 `Hyperf\Utils\Serializer\SimpleNormalizer`。
|
||||
`SymfonyNormalizer` 支持对象的序列化和反序列化。暂时不支持这种 `MathValue[]` 对象数组。
|
||||
|
@ -8,5 +8,5 @@ Hyperf 的命令管理默认由 [symfony/console](https://github.com/symfony/con
|
||||
|
||||
## 请求与协程生命周期
|
||||
|
||||
Swoole 在处理每个连接时,会默认创建一个协程去处理,主要体现在 `onRequest`、`onReceive`、`onConnect` 事件,所以可以理解为每个请求都是一个协程,由于创建协程也是个常规操作,所以一个请求协程里面可能会包含很多个协程,同一个进程内协程之间是内存共享的,但调度顺序是非顺序的,且协程间本质上是相互独立的没有父子关系,所以对每个协程的状态处理都需要通过 [协程上下文](zh/coroutine.md# 协程上下文) 来管理。
|
||||
Swoole 在处理每个连接时,会默认创建一个协程去处理,主要体现在 `onRequest`、`onReceive`、`onConnect` 事件,所以可以理解为每个请求都是一个协程,由于创建协程也是个常规操作,所以一个请求协程里面可能会包含很多个协程,同一个进程内协程之间是内存共享的,但调度顺序是非顺序的,且协程间本质上是相互独立的没有父子关系,所以对每个协程的状态处理都需要通过 [协程上下文](zh/coroutine.md#协程上下文) 来管理。
|
||||
|
||||
|
@ -39,12 +39,12 @@ php bin/hyperf.php vendor:publish hyperf/metric
|
||||
'default' => env('TELEMETRY_DRIVER', 'prometheus'),
|
||||
```
|
||||
|
||||
* `use_standalone_process`: 是否使用 `独立监控进程`。推荐开启。关闭后将在 `Worker 进程` 中处理指标收集与上报。
|
||||
* `use_standalone_process`: 是否使用 `独立监控进程`。推荐开启。关闭后将在 `Worker进程` 中处理指标收集与上报。
|
||||
```php
|
||||
'use_standalone_process' => env('TELEMETRY_USE_STANDALONE_PROCESS', true),
|
||||
```
|
||||
|
||||
* `enable_default_metric`: 是否统计默认指标。默认指标包括内存占用、系统 CPU 负载以及 Swoole Server 指标和 Swoole Coroutine 指标。
|
||||
* `enable_default_metric`: 是否统计默认指标。默认指标包括内存占用、系统 CPU 负载以及Swoole Server 指标和 Swoole Coroutine 指标。
|
||||
```php
|
||||
'enable_default_metric' => env('TELEMETRY_ENABLE_DEFAULT_TELEMETRY', true),
|
||||
```
|
||||
@ -89,7 +89,7 @@ Prometheus 有两种工作模式,爬模式与推模式(通过 Prometheus Pus
|
||||
'mode' => Constants::SCRAPE_MODE
|
||||
```
|
||||
|
||||
并配置爬取地址 `scrape_host`、爬取端口 `scrape_port`、爬取路径 `scrape_path`。Prometheus 可以在对应配置下以 HTTP 访问形式拉取全部指标。
|
||||
并配置爬取地址 `scrape_host`、爬取端口 `scrape_port`、爬取路径 `scrape_path`。Prometheus 可以在对应配置下以HTTP访问形式拉取全部指标。
|
||||
|
||||
> 注意:爬模式下,必须启用独立进程,即 use_standalone_process = true。
|
||||
|
||||
@ -150,7 +150,7 @@ return [
|
||||
];
|
||||
```
|
||||
|
||||
InfluxDB 使用默认的 HTTP 模式,需要配置地址 `host`,UDP 端口 `port`、用户名 `username`、密码 `password`、`dbname` 数据表以及批量推送间隔 `push_interval`。
|
||||
InfluxDB 使用默认的 HTTP 模式,需要配置地址 `host`,UDP端口 `port`、用户名 `username`、密码 `password`、`dbname` 数据表以及批量推送间隔 `push_interval`。
|
||||
|
||||
### 基本抽象
|
||||
|
||||
@ -182,7 +182,7 @@ interface GaugeInterface
|
||||
}
|
||||
```
|
||||
|
||||
* 直方图(Histogram):用于描述对某一事件的持续观测后产生的统计学分布,通常表示为百分位数或分桶。如 HTTP 请求延迟。
|
||||
* 直方图(Histogram):用于描述对某一事件的持续观测后产生的统计学分布,通常表示为百分位数或分桶。如HTTP请求延迟。
|
||||
|
||||
```php
|
||||
interface HistogramInterface
|
||||
@ -212,7 +212,7 @@ return [
|
||||
|
||||
### 自定义使用
|
||||
|
||||
通过 HTTP 中间件遥测仅仅是本组件用途的冰山一角,您可以注入 `Hyperf\Metric\Contract\MetricFactoryInterface` 类来自行遥测业务数据。比如:创建的订单数量、广告的点击数量等。
|
||||
通过HTTP中间件遥测仅仅是本组件用途的冰山一角,您可以注入 `Hyperf\Metric\Contract\MetricFactoryInterface` 类来自行遥测业务数据。比如:创建的订单数量、广告的点击数量等。
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
@ -7,7 +7,7 @@
|
||||
随着需求的迭代,新功能的增加,代码库往往会变得越来越大,尽管我们极力希望在巨大的代码库中做到清晰的模块化,但事实上模块与模块之间的界限很难划分得清楚,逐渐地相似的功能代码在代码库中随处可见,以致于在迭代时想要知道该在什么地方做修改都很困难,修复 `Bug` 和增加新特性新功能越来越难。
|
||||
在一个单体系统中,通常会创建一些抽象层或者实现模块化来保证代码的 `内聚性`,从而避免上面提到的问题。
|
||||
|
||||
> 根据 Robert C. Martin 的一个对 [单一职责原则 (Single Responsibility Principle)](https://baike.baidu.com/item/ 单一职责原则 /9456515) 的论述:“*把因为相同原因而变化的东西聚合到一起,把因为不同原因而变化的东西分离开来。*” 该论述很好地强调了内聚性这一概念。
|
||||
> 根据 Robert C. Martin 的一个对 [单一职责原则 (Single Responsibility Principle)](https://baike.baidu.com/item/单一职责原则/9456515) 的论述:“*把因为相同原因而变化的东西聚合到一起,把因为不同原因而变化的东西分离开来。*” 该论述很好地强调了内聚性这一概念。
|
||||
|
||||
微服务则将这一理念应用在独立的服务上,根据业务的边界确定服务的边界,每个服务专注于服务边界之内的事情,因为可以避免很多由于代码库过大衍生出来的各种问题。
|
||||
那么一个微服务到底应该多微小?足够小即可,不要过小。那么怎么衡量一个系统是否拆足够小了呢?当你面对这个系统时,不会再有 "过大" 想要拆小它的欲望时,那么它应该就足够小了。服务越小,`微服务架构(Microservice)` 的优点和缺点也就越明显,使用的服务越小,独立性带来的好处就越多,但管理大量的服务也会更加复杂。
|
||||
|
@ -1,6 +1,6 @@
|
||||
# 中间件
|
||||
|
||||
这里的中间件指的是 "中间件模式",该功能属于 [hyperf/http-server](https://github.com/hyperf/http-server) 组件内的一项主要功能,主要用于编织从 `请求(Request)` 到 `响应(Response)` 的整个流程,该功能完成基于 [PSR-15](https://www.php-fig.org/psr/psr-15/) 实现。
|
||||
这里的中间件指的是"中间件模式",该功能属于 [hyperf/http-server](https://github.com/hyperf/http-server) 组件内的一项主要功能,主要用于编织从 `请求(Request)` 到 `响应(Response)` 的整个流程,该功能完成基于 [PSR-15](https://www.php-fig.org/psr/psr-15/) 实现。
|
||||
|
||||
## 原理
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
# NATS
|
||||
|
||||
NATS 是一个开源、轻量级、高性能的分布式消息中间件,实现了高可伸缩性和优雅的 `Publish` / `Subscribe` 模型,使用 `Golang` 语言开发。NATS 的开发哲学认为高质量的 QoS 应该在客户端构建,故只建立了 `Request-Reply`,不提供 1. 持久化 2. 事务处理 3. 增强的交付模式 4. 企业级队列。
|
||||
NATS是一个开源、轻量级、高性能的分布式消息中间件,实现了高可伸缩性和优雅的 `Publish` / `Subscribe` 模型,使用 `Golang` 语言开发。NATS的开发哲学认为高质量的QoS应该在客户端构建,故只建立了 `Request-Reply`,不提供 1.持久化 2.事务处理 3.增强的交付模式 4.企业级队列。
|
||||
|
||||
## 使用
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
# 分页器
|
||||
|
||||
在您需要对数据进行分页处理时,可以借助 [hyperf/paginator](https://github.com/hyperf/paginator) 组件很方便的解决您的问题,您可对您的数据查询进行一定的封装处理,以便更好的使用分页功能,该组件也可用于其它框架上。
|
||||
通常情况下,您对分页器的需求可能都是存在于数据库查询上,[hyperf/database](https://github.com/hyperf/database) 数据库组件已经与分页器组件进行了结合,您可以在进行数据查询时很方便的调用分页器来实现分页,具体可查阅 [数据库模型 - 分页](zh/db/paginator.md) 章节。
|
||||
通常情况下,您对分页器的需求可能都是存在于数据库查询上,[hyperf/database](https://github.com/hyperf/database) 数据库组件已经与分页器组件进行了结合,您可以在进行数据查询时很方便的调用分页器来实现分页,具体可查阅 [数据库模型-分页](zh/db/paginator.md) 章节。
|
||||
|
||||
# 安装
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
## 定义访问路由
|
||||
|
||||
Hyperf 使用 [nikic/fast-route](https://github.com/nikic/FastRoute) 作为默认的路由组件并提供服务,您可以很方便的在 `config/routes.php` 中定义您的路由。
|
||||
不仅如此,框架还提供了极其强大和方便灵活的 `注解路由` 功能,关于路由的详情文档请查阅 [路由](zh/router.md) 章节
|
||||
不仅如此,框架还提供了极其强大和方便灵活的`注解路由`功能,关于路由的详情文档请查阅 [路由](zh/router.md) 章节
|
||||
|
||||
### 通过配置文件定义路由
|
||||
路由的文件位于 [hyperf-skeleton](https://github.com/hyperf/hyperf-skeleton) 项目的 `config/routes.php` ,下面是一些常用的用法示例。
|
||||
@ -65,8 +65,8 @@ class IndexController
|
||||
```
|
||||
|
||||
### 通过 `@Controller` 注解定义路由
|
||||
`@Controller` 为满足更细致的路由定义需求而存在,使用 `@Controller` 注解用于表明当前类为一个 `Controller 类`,同时需配合 `@RequestMapping` 注解来对请求方法和请求路径进行更详细的定义。
|
||||
我们也提供了多种快速便捷的 `Mapping 注解`,如 `@GetMapping`、`@PostMapping`、`@PutMapping`、`@PatchMapping`、`@DeleteMapping` 5 种便捷的注解用于表明允许不同的请求方法。
|
||||
`@Controller` 为满足更细致的路由定义需求而存在,使用 `@Controller` 注解用于表明当前类为一个 `Controller类`,同时需配合 `@RequestMapping` 注解来对请求方法和请求路径进行更详细的定义。
|
||||
我们也提供了多种快速便捷的 `Mapping注解`,如 `@GetMapping`、`@PostMapping`、`@PutMapping`、`@PatchMapping`、`@DeleteMapping` 5种便捷的注解用于表明允许不同的请求方法。
|
||||
|
||||
> 使用 `@Controller` 注解时需 `use Hyperf\HttpServer\Annotation\Controller;` 命名空间;
|
||||
> 使用 `@RequestMapping` 注解时需 `use Hyperf\HttpServer\Annotation\RequestMapping;` 命名空间;
|
||||
@ -106,8 +106,8 @@ class IndexController
|
||||
|
||||
## 处理 HTTP 请求
|
||||
|
||||
`Hyperf` 是完全开放的,本质上没有规定您必须基于某种模式下去实现请求的处理,您可以采用传统的 `MVC 模式`,亦可以采用 `RequestHandler 模式` 来进行开发。
|
||||
我们以 `MVC 模式` 来举个例子:
|
||||
`Hyperf` 是完全开放的,本质上没有规定您必须基于某种模式下去实现请求的处理,您可以采用传统的 `MVC模式`,亦可以采用 `RequestHandler模式` 来进行开发。
|
||||
我们以 `MVC模式` 来举个例子:
|
||||
在 `app` 文件夹内创建一个 `Controller` 文件夹并创建 `IndexController.php` 如下,`index` 方法内从请求中获取了 `id` 参数,并转换为 `字符串` 类型返回到客户端。
|
||||
|
||||
```php
|
||||
@ -216,7 +216,7 @@ class IndexController
|
||||
|
||||
通过上面的示例我们不难发现 `$userService` 在没有实例化的情况下, 属性对应的类对象被自动注入了。
|
||||
不过这里的案例并未真正体现出依赖自动注入的好处及其强大之处,我们假设一下 `UserService` 也存在很多的依赖,而这些依赖同时又存在很多其它的依赖时,`new` 实例化的方式就需要手动实例化很多的对象并调整好对应的参数位,而在 `Hyperf` 里我们就无须手动管理这些依赖,只需要声明一下最终使用的类即可。
|
||||
而当 `UserService` 需要发生替换等剧烈的内部变化时,比如从一个本地服务替换成了一个 RPC 远程服务,也只需要通过配置调整依赖中 `UserService` 这个键值对应的类为新的 RPC 服务类即可。
|
||||
而当 `UserService` 需要发生替换等剧烈的内部变化时,比如从一个本地服务替换成了一个 RPC 远程服务,也只需要通过配置调整依赖中 `UserService` 这个键值对应的类为新的RPC服务类即可。
|
||||
|
||||
## 启动 Hyperf 服务
|
||||
|
||||
@ -227,7 +227,7 @@ class IndexController
|
||||
|
||||
由于 `Hyperf` 是持久化的 `CLI` 应用,也就意味着一旦进程启动,已解析的 `PHP` 代码会持久化在进程中,也就意味着启动服务后您再修改的 `PHP` 代码不会改变已启动的服务,如您希望服务重新加载您修改后的代码,您需要通过在启动的 `Console` 中键入 `CTRL + C` 终止服务,再重新执行启动命令完成重启和重新加载。
|
||||
|
||||
> Tips: 您也可以将启动 Server 的命令配置在 IDE 上,便可直接通过 IDE 的 `启动 / 停止` 操作快捷的完成 `启动服务` 或 `重启服务` 的操作。
|
||||
> Tips: 您也可以将启动 Server 的命令配置在 IDE 上,便可直接通过 IDE 的 `启动/停止` 操作快捷的完成 `启动服务` 或 `重启服务` 的操作。
|
||||
> 且非视图开发时可以采用 [TDD(Test-Driven Development)](https://baike.baidu.com/item/TDD/9064369) 测试驱动开发来进行开发,这样不仅可以省略掉服务重启和频繁切换窗口的麻烦,还可保证接口数据的正确性。
|
||||
|
||||
## 多端口监听
|
||||
|
@ -53,7 +53,7 @@ vendor/bin/init-proxy.sh && composer test
|
||||
vendor/bin/init-proxy.sh && php bin/hyperf.php start
|
||||
```
|
||||
|
||||
## PHP7.3 下预先生成代理的脚本 执行失败
|
||||
## PHP7.3下预先生成代理的脚本 执行失败
|
||||
|
||||
`php bin/hyperf.php di:init-proxy` 脚本在 `PHP7.3` 的 `Docker` 打包时,会因为返回码是 `1` 而失败。
|
||||
|
||||
|
@ -13,7 +13,7 @@ composer require hyperf/rate-limit
|
||||
| consume | 1 | 每次请求消耗令牌数 |
|
||||
| capacity | 2 | 令牌桶最大容量 |
|
||||
| limitCallback | NULL | 触发限流时回调方法 |
|
||||
| key | NULL | 生成令牌桶的 key |
|
||||
| key | NULL | 生成令牌桶的key |
|
||||
| waitTimeout | 3 | 排队超时时间 |
|
||||
|
||||
```php
|
||||
|
@ -10,7 +10,7 @@ composer require hyperf/redis
|
||||
|
||||
| 配置项 | 类型 | 默认值 | 备注 |
|
||||
|:--------------:|:-------:|:-----------:|:------------------------------:|
|
||||
| host | string | 'localhost' | Redis 地址 |
|
||||
| host | string | 'localhost' | Redis地址 |
|
||||
| auth | string | 无 | 密码 |
|
||||
| port | integer | 6379 | 端口 |
|
||||
| db | integer | 0 | DB |
|
||||
|
@ -172,7 +172,7 @@ $name = $request->query();
|
||||
|
||||
### 获取 `JSON` 输入信息
|
||||
|
||||
如果请求的 `Body` 数据格式是 `JSON`,则只要 `请求对象(Request)` 的 `Content-Type` `Header 值` 正确设置为 `application/json`,就可以通过 `input(string $key, $default = null)` 方法访问 `JSON` 数据,你甚至可以使用 「点」语法来读取 `JSON` 数组:
|
||||
如果请求的 `Body` 数据格式是 `JSON`,则只要 `请求对象(Request)` 的 `Content-Type` `Header值` 正确设置为 `application/json`,就可以通过 `input(string $key, $default = null)` 方法访问 `JSON` 数据,你甚至可以使用 「点」语法来读取 `JSON` 数组:
|
||||
|
||||
```php
|
||||
// 存在则返回,不存在则返回 null
|
||||
|
@ -117,7 +117,7 @@ class UserController
|
||||
#### `@Controller` 注解
|
||||
|
||||
`@Controller` 为满足更细致的路由定义需求而存在,使用 `@Controller` 注解用于表明当前类为一个 `Controller` 类,同时需配合 `@RequestMapping` 注解来对请求方法和请求路径进行更详细的定义。
|
||||
我们也提供了多种快速便捷的 `Mapping` 注解,如 `@GetMapping`、`@PostMapping`、`@PutMapping`、`@PatchMapping`、`@DeleteMapping` 5 种便捷的注解用于表明允许不同的请求方法。
|
||||
我们也提供了多种快速便捷的 `Mapping` 注解,如 `@GetMapping`、`@PostMapping`、`@PutMapping`、`@PatchMapping`、`@DeleteMapping` 5种便捷的注解用于表明允许不同的请求方法。
|
||||
|
||||
> 使用 `@Controller` 注解时需 `use Hyperf\HttpServer\Annotation\Controller;` 命名空间;
|
||||
> 使用 `@RequestMapping` 注解时需 `use Hyperf\HttpServer\Annotation\RequestMapping;` 命名空间;
|
||||
|
@ -6,18 +6,18 @@
|
||||
|
||||
![snowflake](./imgs/snowflake.jpeg)
|
||||
|
||||
- `1 位`,不用。
|
||||
- `1位`,不用。
|
||||
- 二进制中最高位为符号位,我们生成的 `ID` 一般都是正整数,所以这个最高位固定是 0。
|
||||
|
||||
- `41 位`,用来记录时间戳(毫秒)。
|
||||
- `41 位` 可以表示 `2^41 - 1` 个数字。
|
||||
- 也就是说 `41 位` 可以表示 `2^41 - 1` 个毫秒的值,转化成单位年则是 `(2^41 - 1) / (1000 * 60 * 60 * 24 * 365)` 约为 `69` 年。
|
||||
- `41位`,用来记录时间戳(毫秒)。
|
||||
- `41位` 可以表示 `2^41 - 1` 个数字。
|
||||
- 也就是说 `41位` 可以表示 `2^41 - 1` 个毫秒的值,转化成单位年则是 `(2^41 - 1) / (1000 * 60 * 60 * 24 * 365)` 约为 `69` 年。
|
||||
|
||||
- `10 位`,用来记录工作机器 `ID`。
|
||||
- `10位`,用来记录工作机器 `ID`。
|
||||
- 可以部署在 `2^10` 共 `1024` 个节点,包括 `5` 位 `DatacenterId` 和 `5` 位 `WorkerId`。
|
||||
|
||||
- `12 位`,序列号,用来记录同毫秒内产生的不同 `id`。
|
||||
- `12 位` 可以表示的最大正整数是 `2^12 - 1` 共 `4095` 个数字,来表示同一机器同一时间截(毫秒)内产生的 `4095` 个 `ID` 序号。
|
||||
- `12位`,序列号,用来记录同毫秒内产生的不同 `id`。
|
||||
- `12位` 可以表示的最大正整数是 `2^12 - 1` 共 `4095` 个数字,来表示同一机器同一时间截(毫秒)内产生的 `4095` 个 `ID` 序号。
|
||||
|
||||
`Snowflake` 可以保证:
|
||||
|
||||
@ -34,7 +34,7 @@ composer require hyperf/snowflake
|
||||
|
||||
## 使用
|
||||
|
||||
框架提供了 `MetaGeneratorInterface` 和 `IdGeneratorInterface`,`MetaGeneratorInterface` 会生成 `ID` 的 `Meta` 文件,`IdGeneratorInterface` 则会根据对应的 `Meta` 文件生成 `分布式 ID`。
|
||||
框架提供了 `MetaGeneratorInterface` 和 `IdGeneratorInterface`,`MetaGeneratorInterface` 会生成 `ID` 的 `Meta` 文件,`IdGeneratorInterface` 则会根据对应的 `Meta` 文件生成 `分布式ID`。
|
||||
|
||||
框架默认使用的 `MetaGeneratorInterface` 是基于 `Redis` 实现的 `毫秒级别生成器`。
|
||||
配置文件位于 `config/autoload/snowflake.php`,如配置文件不存在可通过执行 `php bin/hyperf.php vendor:publish hyperf/snowflake` 命令创建默认配置,配置文件内容如下:
|
||||
|
@ -49,7 +49,7 @@
|
||||
* [模型事件](zh/db/event.md)
|
||||
* [模型缓存](zh/db/model-cache.md)
|
||||
* [数据库迁移](zh/db/migration.md)
|
||||
* [极简的 DB 组件](zh/db/db.md)
|
||||
* [极简的DB组件](zh/db/db.md)
|
||||
|
||||
* 微服务
|
||||
|
||||
|
@ -8,7 +8,7 @@ Swoole Tracker 能够帮助企业自动分析并汇总统计关键系统调用
|
||||
> 自动发现应用依赖拓扑结构和展示,时刻掌握应用的架构模型
|
||||
|
||||
- 分布式跨应用链路追踪
|
||||
> 支持无侵入的分布式跨应用链路追踪,让每个请求一目了然,全面支持协程 / 非协程环境,数据实时可视化
|
||||
> 支持无侵入的分布式跨应用链路追踪,让每个请求一目了然,全面支持协程/非协程环境,数据实时可视化
|
||||
|
||||
- 全面分析报告服务状况
|
||||
> 各种维度统计服务上报的调用信息, 比如总流量、平均耗时、超时率等,并全面分析报告服务状况
|
||||
@ -16,17 +16,17 @@ Swoole Tracker 能够帮助企业自动分析并汇总统计关键系统调用
|
||||
- 拥有强大的调试工具链
|
||||
> 本系统支持远程调试,可在系统后台远程开启检测内存泄漏、阻塞检测、代码性能分析和查看调用栈;也支持手动埋点进行调试,后台统一查看结果
|
||||
|
||||
- 同时支持 FPM 和 Swoole
|
||||
> 完美支持 PHP-FPM 环境,不仅限于在 Swoole 中使用
|
||||
- 同时支持FPM和Swoole
|
||||
> 完美支持PHP-FPM环境,不仅限于在Swoole中使用
|
||||
|
||||
- 完善的系统监控
|
||||
> 支持完善的系统监控,零成本部署,监控机器的 CPU、内存、网络、磁盘等资源,可以很方便的集成到现有报警系统
|
||||
> 支持完善的系统监控,零成本部署,监控机器的CPU、内存、网络、磁盘等资源,可以很方便的集成到现有报警系统
|
||||
|
||||
- 一键安装和零成本接入
|
||||
> 规避与减小整体投资风险,本系统的客户端提供脚本可一键部署,服务端可在 Docker 环境中运行,简单快捷
|
||||
> 规避与减小整体投资风险,本系统的客户端提供脚本可一键部署,服务端可在Docker环境中运行,简单快捷
|
||||
|
||||
- 提高各部门生产效率
|
||||
> 在复杂系统中追踪服务及代码层级性能瓶颈,帮助 IT、开发等部门提升工作效率,将重点聚焦在核心工作中
|
||||
> 在复杂系统中追踪服务及代码层级性能瓶颈,帮助IT、开发等部门提升工作效率,将重点聚焦在核心工作中
|
||||
|
||||
## 安装
|
||||
|
||||
|
@ -118,8 +118,8 @@ Swoole 暂时没有协程化的函数列表
|
||||
> 因为 `MongoDB` 没有办法被 `hook`,所以我们可以通过 `Task` 来调用,下面就简单介绍一下如何通过注解方式调用 `MongoDB`。
|
||||
|
||||
以下我们实现两个方法 `insert` 和 `query`,其中需要注意的是 `manager` 方法不能使用 `Task`,
|
||||
因为 `Task` 会在对应的 `Task 进程` 中处理,然后将数据从 `Task 进程` 返回到 `Worker 进程` 。
|
||||
所以 `Task 方法` 的入参和出参最好不要携带任何 `IO`,比如返回一个实例化后的 `Redis` 等等。
|
||||
因为 `Task` 会在对应的 `Task进程` 中处理,然后将数据从 `Task进程` 返回到 `Worker进程` 。
|
||||
所以 `Task方法` 的入参和出参最好不要携带任何 `IO`,比如返回一个实例化后的 `Redis` 等等。
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
@ -142,7 +142,7 @@ class ExampleTest extends TestCase
|
||||
|
||||
## 调试代码
|
||||
|
||||
在 FPM 场景下,我们通常改完代码,然后打开浏览器访问对应接口,所以我们通常会需要两个函数 `dd` 和 `dump`,但 `Hyperf` 跑在 `CLI` 模式下,就算提供了这两个函数,也需要在 `CLI` 中重启 `Server`,然后再到浏览器中调用对应接口查看结果。这样其实并没有简化流程,反而更麻烦了。
|
||||
在FPM场景下,我们通常改完代码,然后打开浏览器访问对应接口,所以我们通常会需要两个函数 `dd` 和 `dump`,但 `Hyperf` 跑在 `CLI` 模式下,就算提供了这两个函数,也需要在 `CLI` 中重启 `Server`,然后再到浏览器中调用对应接口查看结果。这样其实并没有简化流程,反而更麻烦了。
|
||||
|
||||
接下来,我来介绍如何通过配合 `testing`,来快速调试代码,顺便完成单元测试。
|
||||
|
||||
@ -206,11 +206,11 @@ composer test -- --filter=testUserDaoFirst
|
||||
|
||||
Gerard Meszaros 在 Meszaros2007 中介绍了测试替身的概念:
|
||||
|
||||
有时候对被测系统 (SUT) 进行测试是很困难的,因为它依赖于其他无法在测试环境中使用的组件。这有可能是因为这些组件不可用,它们不会返回测试所需要的结果,或者执行它们会有不良副作用。在其他情况下,我们的测试策略要求对被测系统的内部行为有更多控制或更多可见性。
|
||||
有时候对被测系统(SUT)进行测试是很困难的,因为它依赖于其他无法在测试环境中使用的组件。这有可能是因为这些组件不可用,它们不会返回测试所需要的结果,或者执行它们会有不良副作用。在其他情况下,我们的测试策略要求对被测系统的内部行为有更多控制或更多可见性。
|
||||
|
||||
如果在编写测试时无法使用(或选择不使用)实际的依赖组件(DOC),可以用测试替身来代替。测试替身不需要和真正的依赖组件有完全一样的的行为方式;他只需要提供和真正的组件同样的 API 即可,这样被测系统就会以为它是真正的组件!
|
||||
|
||||
下面展示分别通过构造函数注入依赖、通过 inject 注释注入依赖的测试替身
|
||||
下面展示分别通过构造函数注入依赖、通过inject注释注入依赖的测试替身
|
||||
|
||||
### 构造函数注入依赖的测试替身
|
||||
|
||||
@ -306,7 +306,7 @@ class DemoLogicTest extends HttpTestCase
|
||||
|
||||
```
|
||||
|
||||
### 通过 inject 注释注入依赖的测试替身
|
||||
### 通过inject注释注入依赖的测试替身
|
||||
|
||||
```
|
||||
<?php
|
||||
|
@ -189,9 +189,9 @@ return [
|
||||
|
||||
当我们在使用阿里云的链路追踪服务时,由于对端也是支持 `Zipkin` 的协议的,故可以直接通过在 `condif/autoload/opentracing.php` 配置文件内修改 `endpoint_url` 的值为您对应的阿里云 `region` 的地址,具体地址可在阿里云的链路追踪服务内得到,更多细节可参考 [阿里云链路追踪服务帮助文档](https://help.aliyun.com/document_detail/100031.html?spm=a2c4g.11186623.6.547.68f974dcZlg4Mv)。
|
||||
|
||||
### 使用其他 Tracer 驱动
|
||||
### 使用其他Tracer驱动
|
||||
|
||||
您也可以使用其他任意符合 OpenTracing 协议的 Tracer 驱动。在 Driver 项中,填写任意实现了 `Hyperf\Tracer\Contract\NamedFactoryInterface` 的类就可以了。该接口只有一个 make 函数,参数为驱动名称,需返回一个实现了 OpenTracing\Tracer 的实例。
|
||||
您也可以使用其他任意符合OpenTracing协议的Tracer驱动。在Driver项中,填写任意实现了`Hyperf\Tracer\Contract\NamedFactoryInterface`的类就可以了。该接口只有一个make函数,参数为驱动名称,需返回一个实现了OpenTracing\Tracer的实例。
|
||||
|
||||
## Reference
|
||||
|
||||
|
@ -82,7 +82,7 @@ class FooController
|
||||
## 通过全局函数翻译
|
||||
|
||||
您也可以通过全局函数 `__()` 或 `trans()` 来对字符串进行翻译。
|
||||
函数的第一个参数使用 `键`(指使用翻译字符串作为键的键) 或者是 `文件. 键` 的形式。
|
||||
函数的第一个参数使用 `键`(指使用翻译字符串作为键的键) 或者是 `文件.键` 的形式。
|
||||
|
||||
```php
|
||||
echo __('messages.welcome');
|
||||
|
@ -1,6 +1,6 @@
|
||||
# 阿里云日志服务
|
||||
|
||||
在 `Docker 集群` 部署项目时,收集日志会是一个比较麻烦的问题,但阿里云提供了十分好用的 `日志收集系统`,本篇文档就是简略介绍一下阿里云日志收集的使用方法。
|
||||
在 `Docker集群` 部署项目时,收集日志会是一个比较麻烦的问题,但阿里云提供了十分好用的 `日志收集系统`,本篇文档就是简略介绍一下阿里云日志收集的使用方法。
|
||||
|
||||
* [Docker Swarm 集群搭建](zh/tutorial/docker-swarm.md)
|
||||
|
||||
@ -18,8 +18,8 @@
|
||||
|
||||
| 参数 | 说明 |
|
||||
|:-------------------------------------:|:------------------------------------------:|
|
||||
| ${your_region_name} | 区域 ID 比如华东 1 区域是 cn-hangzhou |
|
||||
| ${your_aliyun_user_id} | 用户标识,请替换为您的阿里云主账号用户 ID。 |
|
||||
| ${your_region_name} | 区域ID 比如华东1区域是 cn-hangzhou |
|
||||
| ${your_aliyun_user_id} | 用户标识,请替换为您的阿里云主账号用户ID。 |
|
||||
| ${your_machine_group_user_defined_id} | 集群的机器组自定义标识 以下使用 Hyperf |
|
||||
|
||||
```
|
||||
@ -38,11 +38,11 @@ registry.cn-hangzhou.aliyuncs.com/log-service/logtail
|
||||
|
||||
| 参数 | 填写示例 |
|
||||
|:------------:|:----------------:|
|
||||
| Project 名称 | hyperf |
|
||||
| Project名称 | hyperf |
|
||||
| 注释 | 用于日志系统演示 |
|
||||
| 所属区域 | 华东 1(杭州) |
|
||||
| 所属区域 | 华东1(杭州) |
|
||||
| 开通服务 | 详细日志 |
|
||||
| 日志存储位置 | 当前 Project |
|
||||
| 日志存储位置 | 当前Project |
|
||||
|
||||
### 创建 Logstore
|
||||
|
||||
@ -50,7 +50,7 @@ registry.cn-hangzhou.aliyuncs.com/log-service/logtail
|
||||
|
||||
| 参数 | 填写示例 |
|
||||
|:------------:|:---------------:|
|
||||
| Logstore 名称 | hyperf-demo-api |
|
||||
| Logstore名称 | hyperf-demo-api |
|
||||
| 永久保存 | false |
|
||||
| 数据保存时间 | 60 |
|
||||
|
||||
@ -80,7 +80,7 @@ registry.cn-hangzhou.aliyuncs.com/log-service/logtail
|
||||
|:--------------:|:-------------------------------------------------:|:---------------:|
|
||||
| 配置名称 | hyperf-demo-api | |
|
||||
| 日志路径 | /opt/www/runtime/logs | *.log |
|
||||
| Label 白名单 | app.name | hyperf-demo-api |
|
||||
| Label白名单 | app.name | hyperf-demo-api |
|
||||
| 模式 | 完整正则模式 | |
|
||||
| 单行模式 | false | |
|
||||
| 日志样例 | `[2019-03-07 11:58:57] hyperf.WARNING: xxx` | |
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
首先我们需要在 `项目` 里新建一个项目。DaoCloud 支持多种镜像仓库,这个可以按需选择。
|
||||
|
||||
这里我以 [hyperf-demo](https://github.com/limingxinleo/hyperf-demo) 仓库为例配置。当创建成功后,在对应 `Github 仓库` 的 `WebHooks` 下面就会有对应的 url。
|
||||
这里我以 [hyperf-demo](https://github.com/limingxinleo/hyperf-demo) 仓库为例配置。当创建成功后,在对应 `Github仓库` 的 `WebHooks` 下面就会有对应的url。
|
||||
|
||||
接下来我们修改一下仓库里的 `Dockerfile`,在 `apk add` 下面增加 `&& apk add wget \`。这里具体原因不是很清楚,如果不更新 `wget`, 使用时就会有问题。但是自建 Gitlab CI 就没有任何问题。
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Docker Swarm 集群搭建
|
||||
|
||||
现阶段,Docker 容器技术已经相当成熟,就算是中小型公司也可以基于 Gitlab、Aliyun 镜像服务、Docker Swarm 轻松搭建自己的 Docker 集群服务。
|
||||
现阶段,Docker容器技术已经相当成熟,就算是中小型公司也可以基于 Gitlab、Aliyun镜像服务、Docker Swarm 轻松搭建自己的 Docker集群服务。
|
||||
|
||||
## 安装 Docker
|
||||
|
||||
@ -14,9 +14,9 @@ curl -sSL https://get.daocloud.io/docker | sh
|
||||
ExecStart=/usr/bin/dockerd -H unix:// -H tcp://0.0.0.0:2375
|
||||
```
|
||||
|
||||
## 搭建自己的 Gitlab
|
||||
## 搭建自己的Gitlab
|
||||
|
||||
### 安装 Gitlab
|
||||
### 安装Gitlab
|
||||
|
||||
首先我们修改一下端口号,把 `sshd` 服务的 `22` 端口改为 `2222`,让 `gitlab` 可以使用 `22` 端口。
|
||||
|
||||
@ -49,7 +49,7 @@ gitlab/gitlab-ce:latest
|
||||
|
||||
首次登录 `Gitlab` 会重置密码,用户名是 `root`。
|
||||
|
||||
### 安装 gitlab-runner
|
||||
### 安装gitlab-runner
|
||||
|
||||
[官方地址](https://docs.gitlab.com/runner/install/linux-repository.html)
|
||||
|
||||
@ -150,7 +150,7 @@ docker service create \
|
||||
portainer/portainer
|
||||
```
|
||||
|
||||
## 创建一个 Demo 项目
|
||||
## 创建一个Demo项目
|
||||
|
||||
登录 Gitlab 创建一个 Demo 项目。并导入我们的项目 [hyperf-skeleton](https://github.com/hyperf/hyperf-skeleton)
|
||||
|
||||
@ -212,7 +212,7 @@ networks:
|
||||
external: true
|
||||
```
|
||||
|
||||
然后在我们的 portainer 中,创建对应的 Config demo_v1.0。当然,以下参数需要根据实际情况调整,因为我们的 Demo 中,没有任何 IO 操作,所以填默认的即可。
|
||||
然后在我们的 portainer 中,创建对应的 Config demo_v1.0。当然,以下参数需要根据实际情况调整,因为我们的Demo中,没有任何IO操作,所以填默认的即可。
|
||||
|
||||
```
|
||||
APP_NAME=demo
|
||||
@ -233,7 +233,7 @@ REDIS_PORT=6379
|
||||
REDIS_DB=0
|
||||
```
|
||||
|
||||
因为我们配置的 gitlab-ci.yml 会检测 test 分支和 tags,所以我们把修改的内容合并到 test 分支,然后推到 gitlab 上。
|
||||
因为我们配置的 gitlab-ci.yml 会检测 test 分支和 tags,所以我们把修改的内容合并到test分支,然后推到gitlab上。
|
||||
|
||||
接下来我们就可以访问集群任意一台机器的 9501 端口。进行测试了
|
||||
|
||||
@ -243,8 +243,8 @@ curl http://127.0.0.1:9501/
|
||||
|
||||
## 安装 KONG 网关
|
||||
|
||||
通常情况下,Swarm 集群是不会直接对外的,所以我们这里推荐使用 `KONG` 作为网关。
|
||||
还有另外一个原因,那就是 `Swarm` 的 `Ingress 网络` 设计上有缺陷,所以在连接不复用的情况下,会有并发瓶颈,具体请查看对应 `Issue` [#35082](https://github.com/moby/moby/issues/35082)
|
||||
通常情况下,Swarm集群是不会直接对外的,所以我们这里推荐使用 `KONG` 作为网关。
|
||||
还有另外一个原因,那就是 `Swarm` 的 `Ingress网络` 设计上有缺陷,所以在连接不复用的情况下,会有并发瓶颈,具体请查看对应 `Issue` [#35082](https://github.com/moby/moby/issues/35082)
|
||||
而 `KONG` 作为网关,默认情况下就会复用后端的连接,所以会极大减缓上述问题。
|
||||
|
||||
### 安装数据库
|
||||
|
@ -124,7 +124,7 @@ class IndexController
|
||||
|
||||
#### 自定义错误消息
|
||||
|
||||
您可以通过重写 `messages` 方法来自定义表单请求使用的错误消息,该方法应该返回属性 / 规则对数组及其对应错误消息:
|
||||
您可以通过重写 `messages` 方法来自定义表单请求使用的错误消息,该方法应该返回属性/规则对数组及其对应错误消息:
|
||||
|
||||
```php
|
||||
/**
|
||||
@ -432,7 +432,7 @@ if ($errors->has('foo')) {
|
||||
|
||||
##### date_format:format
|
||||
|
||||
验证字段必须匹配指定格式,可以使用 PHP 函数 date 或 date_format 验证该字段。
|
||||
验证字段必须匹配指定格式,可以使用 PHP 函数date 或 date_format 验证该字段。
|
||||
|
||||
##### different:field
|
||||
|
||||
@ -456,7 +456,7 @@ if ($errors->has('foo')) {
|
||||
|
||||
有效的约束条件包括:`min_width`, `max_width`, `min_height`, `max_height`, `width`, `height`, `ratio`。
|
||||
|
||||
`ratio` 约束宽度 / 高度的比率,这可以通过表达式 `3/2` 或浮点数 `1.5` 来表示:
|
||||
`ratio` 约束宽度/高度的比率,这可以通过表达式 `3/2` 或浮点数 `1.5` 来表示:
|
||||
|
||||
```php
|
||||
'avatar' => 'dimensions:ratio=3/2'
|
||||
@ -573,19 +573,19 @@ $validator = $this->validationFactory->make($data, [
|
||||
|
||||
##### ip
|
||||
|
||||
验证字段必须是 IP 地址。
|
||||
验证字段必须是IP地址。
|
||||
|
||||
##### ipv4
|
||||
|
||||
验证字段必须是 IPv4 地址。
|
||||
验证字段必须是IPv4地址。
|
||||
|
||||
##### ipv6
|
||||
|
||||
验证字段必须是 IPv6 地址。
|
||||
验证字段必须是IPv6地址。
|
||||
|
||||
##### json
|
||||
|
||||
验证字段必须是有效的 JSON 字符串
|
||||
验证字段必须是有效的JSON字符串
|
||||
|
||||
##### lt:field
|
||||
|
||||
@ -722,7 +722,7 @@ $validator = $this->validationFactory->make($request->all(), [
|
||||
|
||||
##### size:value
|
||||
|
||||
验证字段必须有和给定值 `value` 相匹配的尺寸 / 大小,对字符串而言,`value` 是相应的字符数目;对数值而言,`value` 是给定整型值;对数组而言,`value` 是数组长度;对文件而言,`value` 是相应的文件千字节数(KB)。
|
||||
验证字段必须有和给定值 `value` 相匹配的尺寸/大小,对字符串而言,`value` 是相应的字符数目;对数值而言,`value` 是给定整型值;对数组而言,`value` 是数组长度;对文件而言,`value` 是相应的文件千字节数(KB)。
|
||||
|
||||
##### starts_with:foo,bar,...
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user