diff --git a/summary.md b/summary.md index 2383a9add..b832a3c4c 100644 --- a/summary.md +++ b/summary.md @@ -62,6 +62,7 @@ * [开发者工具](zh/devtool.md) * [辅助类](zh/utils.md) * [自动化测试](zh/testing.md) + * [限流器](zh/rate-limit.md) * Awesome Hyperf diff --git a/zh/config.md b/zh/config.md index 768c4684d..3e0f633cc 100644 --- a/zh/config.md +++ b/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` 文件夹内的配置文件的关系 diff --git a/zh/consul.md b/zh/consul.md index 6488076ad..1d4eac092 100644 --- a/zh/consul.md +++ b/zh/consul.md @@ -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` 类 diff --git a/zh/coroutine.md b/zh/coroutine.md index 486f4df55..0cdf7b846 100644 --- a/zh/coroutine.md +++ b/zh/coroutine.md @@ -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. ## 使用协程 diff --git a/zh/devtool.md b/zh/devtool.md index e69de29bb..7ec38c72c 100644 --- a/zh/devtool.md +++ b/zh/devtool.md @@ -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. +``` + +## 使用 + +``` \ No newline at end of file diff --git a/zh/logger.md b/zh/logger.md index 39184f0a5..4d880b54a 100644 --- a/zh/logger.md +++ b/zh/logger.md @@ -63,4 +63,171 @@ class DemoService $this->logger->info("Your log message."); } } -``` \ No newline at end of file +``` + +## 关于 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, 方便投递到第三方日志服务 \ No newline at end of file diff --git a/zh/rate-limit.md b/zh/rate-limit.md index e69de29bb..101c03fc9 100644 --- a/zh/rate-limit.md +++ b/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 + 1, + 'consume' => 1, + 'capacity' => 2, + 'limitCallback' => null, + 'key' => null, + 'waitTimeout' => 3, +]; +``` + +## 使用限流器 + +组件提供 `Hyperf\RateLimit\Annotation\RateLimit` 注解,作用于类、类方法,可以覆盖配置文件。 例如, + +```php +process()`继续执行或者自行处理。 + +## 限流 diff --git a/zh/tracer.md b/zh/tracer.md index e69de29bb..8e7aac700 100644 --- a/zh/tracer.md +++ b/zh/tracer.md @@ -0,0 +1,4 @@ + +- Dapper, 大规模分布式系统的跟踪系统: https://bigbully.github.io/Dapper-translation/ +- 分布式跟踪系统, zipkin 的背景和设计: https://blog.csdn.net/manzhizhen/article/details/52811600 +- zipkin 官网: https://zipkin.io/