mirror of
https://gitee.com/hyperf/hyperf.git
synced 2024-12-04 20:58:13 +08:00
Merge branch 'gh-pages' of https://github.com/hyperf-cloud/hyperf into gh-pages
This commit is contained in:
commit
e36fda1f38
@ -62,6 +62,7 @@
|
||||
* [开发者工具](zh/devtool.md)
|
||||
* [辅助类](zh/utils.md)
|
||||
* [自动化测试](zh/testing.md)
|
||||
* [限流器](zh/rate-limit.md)
|
||||
|
||||
* Awesome Hyperf
|
||||
|
||||
|
12
zh/config.md
12
zh/config.md
@ -18,24 +18,26 @@ config
|
||||
├── autoload // 此文件夹内的配置文件会被配置组件自己加载,并以文件夹内的文件名作为第一个键值
|
||||
│ ├── amqp.php // 用于管理 AMQP 组件
|
||||
│ ├── annotations.php // 用于管理注解
|
||||
│ ├── apollo.php // 用于管理基于 Apollo 实现的配置中心
|
||||
│ ├── aspects.php // 用于管理 AOP 切面
|
||||
│ ├── async_queue.php // 用于管理基于 Redis 实现的简易队列服务
|
||||
│ ├── cache.php // 用于管理缓存组件
|
||||
│ ├── commands.php // 用于管理自定义命令
|
||||
│ ├── config-center.php // 用于管理配置中心
|
||||
│ ├── consul.php // 用于管理 Consul 客户端
|
||||
│ ├── databases.php // 用于管理数据库客户端
|
||||
│ ├── devtool.php // 用于管理开发者工具
|
||||
│ ├── exceptions.php // 用于管理异常处理器
|
||||
│ ├── listeners.php // 用于管理事件监听者
|
||||
│ ├── logger.php // 用于管理日志
|
||||
│ ├── middlewares.php // 用于管理中间件
|
||||
│ ├── opentracing.php // 用于管理调用链追踪
|
||||
│ ├── queue.php // 用于管理基于 Redis 实现的简易队列服务
|
||||
│ └── redis.php // 用于管理 Redis 客户端
|
||||
│ ├── processes.php // 用于管理自定义进程
|
||||
│ ├── redis.php // 用于管理 Redis 客户端
|
||||
│ └── server.php // 用于管理 Server 服务
|
||||
├── config.php // 用于管理用户或框架的配置,如配置相对独立亦可放于 autoload 文件夹内
|
||||
├── container.php // 负责容器的初始化,作为一个配置文件运行并最终返回一个 Psr\Container\ContainerInterface 对象
|
||||
├── dependencies.php // 用于管理 DI 的依赖关系和类对应关系
|
||||
├── routes.php // 用于管理路由
|
||||
└── server.php // 用于管理 Server 服务
|
||||
└── routes.php // 用于管理路由
|
||||
```
|
||||
|
||||
### `config.php` 与 `autoload` 文件夹内的配置文件的关系
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Consul 协程客户端
|
||||
|
||||
Hyperf 提供了一个 [Consul](https://www.consul.io/api/index.html) 的协程客户端,由于 Consul 本身的 API 比较简单,也支持 HTTP 的请求方法,顾该组件仅对 API 进行了一些封装上的简化,基于 [hyperf/guzzle](https://github.com/hyperf-cloud/guzzle) 提供的协程 HTTP 客户端支持。
|
||||
Hyperf 提供了一个 [Consul](https://www.consul.io/api/index.html) 的协程客户端,由于 Consul 本身的 API 比较简单,也支持 HTTP 的请求方法,故该组件仅对 API 进行了一些封装上的简化,基于 [hyperf/guzzle](https://github.com/hyperf-cloud/guzzle) 提供的协程 HTTP 客户端支持。
|
||||
|
||||
> `ConsulResponse` 类指的是 `Hyperf\Consul\ConsulResponse` 类
|
||||
|
||||
|
@ -53,6 +53,9 @@ Swoole 协程也是对异步回调的一种解决方案,在 PHP 语言下,Sw
|
||||
|
||||
都说协程是一个轻量级的线程,协程和线程都适用于多任务的场景下,从这个角度上来说,协程与线程很相似,都有自己的上下文,可以共享全局变量,但不同之处在于,在同一时间可以有多个线程处于运行状态,但对于 Swoole 协程来说只能有一个,其它的协程都会处于暂停的状态。此外,普通线程是抢占式的,那个线程能得到资源由操作系统决定,而协程是协作式的,执行权由用户态自行分配。
|
||||
|
||||
- [swoole 中的协程原理](https://www.jianshu.com/p/745b0b3ffae7)
|
||||
- [swoole 中的协程用法](https://www.jianshu.com/p/b620836c461a)
|
||||
|
||||
## 协程编程注意事项
|
||||
|
||||
### 不能存在阻塞代码
|
||||
@ -74,7 +77,9 @@ Swoole 协程也是对异步回调的一种解决方案,在 PHP 语言下,Sw
|
||||
- 全局周期,我们只需要创建一个静态变量供全局调用即可,静态变量意味着在服务启动后,任意协程和代码逻辑均共享此静态变量内的数据,也就意味着存放的数据不能是特别服务于某一个请求或某一个协程;
|
||||
- 协程周期,由于 Hyperf 会为每个请求自动创建一个协程来处理,那么一个协程周期在此也可以理解为一个请求周期,在协程内,所有的状态数据均应存放于 `Hyperf\Utils\Context` 类中,通过该类的 `get`、`set` 来读取和存储任意结构的数据,这个 `Context(协程上下文)` 类在执行任意协程时读取或存储的数据都是仅限对应的协程的,同时在协程结束时也会自动销毁相关的上下文数据。
|
||||
|
||||
### 协程 server 可开启的协程数限制
|
||||
|
||||
在 `swoole server` 的 `set` 方法中增加了一个配置参数 `max_coroutine`,用于配置一个 `Worker` 进程最多同时处理的协程数目。因为随着 `Worker` 进程处理的协程数目的增加,其占用的内存也会增加,为了避免超出 php 的 `memory_limit` 限制,请根据实际业务的压测结果设置该值,默认为 swoole 中设置的默认值为 3000, hyperf 中修改为 100000.
|
||||
|
||||
## 使用协程
|
||||
|
||||
|
@ -0,0 +1,28 @@
|
||||
# devtool 开发者工具
|
||||
|
||||
## 安装
|
||||
|
||||
```
|
||||
# 引入
|
||||
composer require hyperf/devtool
|
||||
|
||||
# 查看支持的命令
|
||||
php bin/hyperf.php
|
||||
|
||||
gen
|
||||
gen:amqp-consumer Create a new amqp consumer class
|
||||
gen:amqp-producer Create a new amqp producer class
|
||||
gen:aspect Create a new aspect class
|
||||
gen:command Create a new command class
|
||||
gen:controller Create a new controller class
|
||||
gen:job Create a new job class
|
||||
gen:listener Create a new listener class
|
||||
gen:middleware Create a new middleware class
|
||||
gen:process Create a new process class
|
||||
vendor
|
||||
vendor:publish Publish any publishable configs from vendor packages.
|
||||
```
|
||||
|
||||
## 使用
|
||||
|
||||
```
|
169
zh/logger.md
169
zh/logger.md
@ -63,4 +63,171 @@ class DemoService
|
||||
$this->logger->info("Your log message.");
|
||||
}
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
## 关于 monolog 的基础知识
|
||||
|
||||
结合代码来看 monolog 中涉及的基础概念:
|
||||
|
||||
```php
|
||||
use Monolog\Formatter\LineFormatter;
|
||||
use Monolog\Handler\FirePHPHandler;
|
||||
use Monolog\Logger;
|
||||
use Monolog\Handler\StreamHandler;
|
||||
|
||||
// create log channel
|
||||
$log = new Logger('log');
|
||||
|
||||
// create log handler
|
||||
$stream = new StreamHandler('test.log', Logger::WARNING);
|
||||
$fire = new FirePHPHandler();
|
||||
|
||||
// custom log format
|
||||
// the default date format is "Y-m-d H:i:s"
|
||||
$dateFormat = "Y n j, g:i a";
|
||||
// the default output format is "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n"
|
||||
$output = "%datetime%||%channel||%level_name%||%message%||%context%||%extra%\n";
|
||||
// finally, create a formatter
|
||||
$formatter = new LineFormatter($output, $dateFormat);
|
||||
|
||||
// set log format
|
||||
$stream->setFormatter($formatter);
|
||||
|
||||
// add handler to log channel
|
||||
$log->pushHandler($stream);
|
||||
$log->pushHandler($fire);
|
||||
|
||||
// clone new log channel
|
||||
$log2 = $log->withName('log2');
|
||||
|
||||
// add records to the log
|
||||
$log->warning('Foo');
|
||||
|
||||
// add extra data to record
|
||||
// 1. log context
|
||||
$log->error('a new user', ['username' => 'daydaygo']);
|
||||
// 2. processor
|
||||
$log->pushProcessor(function ($record) {
|
||||
$record['extra']['dummy'] = 'hello';
|
||||
return $record;
|
||||
});
|
||||
$log->pushProcessor(new \Monolog\Processor\MemoryPeakUsageProcessor());
|
||||
$log->alert('czl');
|
||||
```
|
||||
|
||||
- 首先, 实例化一个 `Logger`, 取个名字, 名字对应的就是 `channel`
|
||||
- 可以为 `Logger` 绑定多个 `Handler`, `Logger` 打日志, 交由 `Handler` 来处理
|
||||
- `Handler` 可以指定需要处理那些 **日志级别** 的日志, 比如 `Logger::WARNING`, 只处理日志级别 `>=Logger::WARNING` 的日志
|
||||
- 谁来格式化日志? `Formatter`, 设置好 Formatter 并绑定到相应的 `Handler` 上
|
||||
- 日志包含那些部分: `"%datetime%||%channel||%level_name%||%message%||%context%||%extra%\n"`
|
||||
- 区分一下日志中添加的额外信息 `context` 和 `extra`: `context` 由用户打日志时额外指定, 更加灵活; `extra` 由绑定到 `Logger` 上的 `Processor` 固定添加, 比较适合收集一些 **常见信息**
|
||||
|
||||
## hyperf/logger 的高级用法
|
||||
|
||||
### 封装 Log 类
|
||||
|
||||
```php
|
||||
namespace App;
|
||||
|
||||
use Hyperf\Logger\Logger;
|
||||
use Hyperf\Utils\ApplicationContext;
|
||||
|
||||
/**
|
||||
* @method static Logger get($name)
|
||||
* @method static log($level, $message, array $context = array())
|
||||
* @method static info($message, array $context = array())
|
||||
* @method static error($message, array $context = array())
|
||||
*/
|
||||
class Log
|
||||
{
|
||||
public static function __callStatic($name, $arguments)
|
||||
{
|
||||
$container = ApplicationContext::getContainer();
|
||||
$log = $container->get(\Hyperf\Logger\LoggerFactory::class);
|
||||
if ($name == 'get') {
|
||||
return $log->get(...$arguments);
|
||||
}
|
||||
$log = $log->get('app');
|
||||
$log->$name(...$arguments);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `__callStatic()` 使用魔术方法实现 **静态方法调用**
|
||||
- 默认使用 `app` channel(回忆一下上面提到的 monolog 基础概念) 来打日志,
|
||||
- 使用 `Log::get()` 就可以切换到不同 channel, 强大的 `container` 解决了这一切
|
||||
|
||||
### stdout 日志
|
||||
|
||||
默认 `StdoutLoggerInterface` 接口的实现类 `StdoutLogger`, 其实并没有使用 monolog, 如果想要使用 monolog 保持一致呢?
|
||||
|
||||
是的, 还是强大的 `container`.
|
||||
|
||||
- 首先, 实现一个 `StdoutLoggerFactory`
|
||||
|
||||
```php
|
||||
namespace App;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
||||
class StdoutLoggerFactory
|
||||
{
|
||||
public function __invoke(ContainerInterface $container)
|
||||
{
|
||||
return Log::get('sys');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- 申明依赖, 使用 `StdoutLoggerInterface` 的地方, 由实际依赖的 `StdoutLoggerFactory` 实例化的类来完成
|
||||
|
||||
```php
|
||||
// config/dependencies.php
|
||||
return [
|
||||
'dependencies' => [
|
||||
\Hyperf\Contract\StdoutLoggerInterface::class => \App\StdoutLoggerFactory::class,
|
||||
],
|
||||
];
|
||||
```
|
||||
|
||||
### 不同环境下输出不同格式的日志
|
||||
|
||||
上面这么多的使用, 都还只在 monolog 中的 `Logger` 这里打转, 这里来看看 `Handler` 和 `Formatter`
|
||||
|
||||
```php
|
||||
// config/autoload/logger.php
|
||||
$app_env = env('APP_ENV', 'dev');
|
||||
if ($app_env == 'dev') {
|
||||
$formatter = [
|
||||
'class' => \Monolog\Formatter\LineFormatter::class,
|
||||
'constructor' => [
|
||||
'format' => "||%datetime%||%channel%||%level_name%||%message%||%context%||%extra%\n",
|
||||
'allowInlineLineBreaks' => true,
|
||||
'includeStacktraces' => true,
|
||||
],
|
||||
];
|
||||
} else {
|
||||
$formatter = [
|
||||
'class' => \Monolog\Formatter\JsonFormatter::class,
|
||||
'constructor' => [],
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'default' => [
|
||||
'handler' => [
|
||||
'class' => \Monolog\Handler\StreamHandler::class,
|
||||
'constructor' => [
|
||||
'stream' => 'php://stdout',
|
||||
'level' => \Monolog\Logger::INFO,
|
||||
],
|
||||
],
|
||||
'formatter' => $formatter,
|
||||
],
|
||||
]
|
||||
```
|
||||
|
||||
- 默认配置了名为 `default` 的 `Handler`, 并包含了此 `Handler` 及其 `Formatter` 的信息
|
||||
- 获取 `Logger` 时, 如果没有指定 `Handler`, 底层会自动把 `default` 这一 `Handler` 绑定到 `Logger` 上
|
||||
- dev(开发)环境: 日志使用 `php://stdout` 输出到 stdout, 并且 `Formatter` 中设置 `allowInlineLineBreaks`, 方便查看多行日志
|
||||
- 非 dev 环境: 日志使用 `JsonFormatter`, 会被格式为 json, 方便投递到第三方日志服务
|
108
zh/rate-limit.md
108
zh/rate-limit.md
@ -0,0 +1,108 @@
|
||||
# 令牌桶限流器
|
||||
|
||||
## 安装
|
||||
|
||||
```bash
|
||||
composer require hyperf/rate-limit
|
||||
```
|
||||
## 默认配置
|
||||
|
||||
| 配置 | 默认值 | 备注 |
|
||||
|:--------------:|:------:|:-------------------:|
|
||||
| create | 1 | 每秒生成令牌数 |
|
||||
| consume | 1 | 每次请求消耗令牌数 |
|
||||
| capacity | 2 | 令牌桶最大容量 |
|
||||
| limitCallback | NULL | 触发限流时回调方法 |
|
||||
| key | NULL | 生成令牌桶的key |
|
||||
| waitTimeout | 3 | 排队超时时间 |
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
return [
|
||||
'create' => 1,
|
||||
'consume' => 1,
|
||||
'capacity' => 2,
|
||||
'limitCallback' => null,
|
||||
'key' => null,
|
||||
'waitTimeout' => 3,
|
||||
];
|
||||
```
|
||||
|
||||
## 使用限流器
|
||||
|
||||
组件提供 `Hyperf\RateLimit\Annotation\RateLimit` 注解,作用于类、类方法,可以覆盖配置文件。 例如,
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use Hyperf\HttpServer\Annotation\Controller;
|
||||
use Hyperf\HttpServer\Annotation\RequestMapping;
|
||||
use Hyperf\RateLimit\Annotation\RateLimit;
|
||||
|
||||
/**
|
||||
* @Controller(prefix="rate-limit")
|
||||
*/
|
||||
class RateLimitController
|
||||
{
|
||||
/**
|
||||
* @RequestMapping(path="test")
|
||||
* @RateLimit(create=1, capacity=3)
|
||||
*/
|
||||
public function test()
|
||||
{
|
||||
return ["QPS 1, 峰值3"];
|
||||
}
|
||||
|
||||
/**
|
||||
* @RequestMapping(path="test2")
|
||||
* @RateLimit(create=2, consume=2, capacity=4)
|
||||
*/
|
||||
public function test2()
|
||||
{
|
||||
return ["QPS 2, 峰值2"];
|
||||
}
|
||||
}
|
||||
```
|
||||
配置优先级`默认配置 < 配置文件 < 类注解 < 方法注解`
|
||||
|
||||
## 触发限流
|
||||
当限流被触发时, 默认会抛出`Hyperf\RateLimit\Exception\RateLimitException` 异常
|
||||
|
||||
或者配置`limitCallback`限流回调来处理。例如:
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use Hyperf\Di\Aop\ProceedingJoinPoint;
|
||||
use Hyperf\HttpServer\Annotation\Controller;
|
||||
use Hyperf\HttpServer\Annotation\RequestMapping;
|
||||
use Hyperf\RateLimit\Annotation\RateLimit;
|
||||
|
||||
/**
|
||||
* @Controller(prefix="rate-limit")
|
||||
* @RateLimit(limitCallback={RateLimitController, 'limitCallback'})
|
||||
*/
|
||||
class RateLimitController
|
||||
{
|
||||
/**
|
||||
* @RequestMapping(path="test")
|
||||
* @RateLimit(create=1, capacity=3)
|
||||
*/
|
||||
public function test()
|
||||
{
|
||||
return ["QPS 1, 峰值3"];
|
||||
}
|
||||
|
||||
public function limitCallback(float $seconds, ProceedingJoinPoint $proceedingJoinPoint)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
回调方法中的`$seconds` 参数是下次生成Token 的间隔, `$proceedingJoinPoint` 则是此次请求要执行的切入点, 可以通过调用`$proceedingJoinPoint->process()`继续执行或者自行处理。
|
||||
|
||||
## 限流
|
@ -0,0 +1,4 @@
|
||||
|
||||
- Dapper, 大规模分布式系统的跟踪系统: https://bigbully.github.io/Dapper-translation/
|
||||
- 分布式跟踪系统, zipkin 的背景和设计: https://blog.csdn.net/manzhizhen/article/details/52811600
|
||||
- zipkin 官网: https://zipkin.io/
|
Loading…
Reference in New Issue
Block a user