项目说明
该扩展致力于分析接口上的优化能力
项目地址: github.com/GodForHeart/laravel-opt...
安装插件:composer require godforheart/laravel-optimizer
生成配置:php artisan optimizer:publish
1. 插件背景
平时项目开发中,经常会遇到n+1查询,接口中执行的sql过多,导致接口性能不加,但是已有接口过多,或开发过程不注意,还能使用数据库日志,来筛选出性能较差,sql调用次数过多的旧接口,便在实际开发中,开发一个符合个人需求的插件,并且个人感觉还挺好用,遂推荐一波
2. 功能介绍
- 动态开关功能
- 日志存储类型:本地日志,数据库,日志分析平台(暂不可使用)
- 安全日志模式:安全模式下不会记录请求参数和响应内容,以及只记录MD5(sql)
- 持久化方式:同步,异步(异步处理日志,降低插件对接口响应时间的影响度)
- 忽略指定接口日志
- 额外请求参数日志:如需要记录请求用户id(每个项目授权方式不同,插件不集成改功能,需使用者自行配置)
- 忽略指定请求参数:如不希望password记录,可忽略该参数
- 可开启单条数据库sql日志(默认开启)
- 可开启单条redis操作日志(默认关闭,开启该功能需要额外配置,下文会介绍)
- 日志限制器:三种策略(独立请求,根据ip规则,根据接口uri规则)
- 日志限制器:两种规则(随机,顺序)
- 日志限制器:日志比例(百分比,0-100,当为0时,等同于关闭,当为100时,等同于无限制)
3. 配置信息
配置文件地址:config/optimizer.php
<?php
return [
// 是否开启扩展
'enable' => (bool)env('OPTIMIZER_ENABLE', true),
// 是否开启平台日志(可 本地日志+平台日志 双线)
'enable_platform_log' => (bool)env('OPTIMIZER_ENABLE_PLATFORM_LOG', false),
/**
* 默认日志存储类型,默认日志文件,可选:
* logger:本地日志
* database:数据库
* platform:日志分析平台
*/
'default' => env('OPTIMIZER_DEFAULT', 'logger'),
// 是否安全模式,安全模式下,不会记录请求参数和响应内容,以及只记录MD5(sql)
'safe_mode' => (bool)env('OPTIMIZER_SAFE_MODE', false),
.
.
.
'limiter' => [
/**
* strategy(日志策略):
* request:独立请求
* ip:根据独立ip
* uri:根据相同uri
*/
'strategy' => env('OPTIMIZER_LIMITER_STRATEGY', 'request'),
/**
* rule(日志规则):
* orderly:有序的
* disorderly:无序的,伪随机(请注意,大方向上会超过设定的占比)
*/
'rule' => env('OPTIMIZER_LIMITER_RULE', 'orderly'),
/**
* rate(占比):一百次中记录多少次,1-100
*/
'rate' => (int)env('OPTIMIZER_LIMITER_RATE', 100),
]
];
4. 中间件使用方式
在需要的位置增加改扩展中间件,如个人需要在【api】中间件组中增加
<?php
.
.
.
class Kernel extends HttpKernel
{
.
.
.
protected $middlewareGroups = [
.
.
.
'api' => [
.
.
.
\Godforheart\LaravelOptimizer\Middleware\OptimizerLog::class
],
];
}
5. Redis日志配置
在config/app.php文件中,providers数组中,把:
Illuminate\Redis\RedisServiceProvider::class
替换成
\Godforheart\LaravelOptimizer\OptimizerRedisServiceProvider::class
既可监听【Redis门面】和【Cache门店(使用redis)】的操作日志
6. 日志示例
该次请求,经过测试,使用异步日志方式,约占用接口【0.5 - 1】ms 的执行时间影响度较小,请根据实际情况,进行选择使用
[2022-07-21 16:09:26] local.INFO: optimizer:singleSql:[connection:mysql] execution times: 2.84ms; select * from `personal_access_tokens` where `personal_access_tokens`.`id` = "18" limit 1
[2022-07-21 16:09:26] local.INFO: optimizer:singleSql:[connection:mysql] execution times: 1.03ms; select * from `users` where `users`.`id` = 1 limit 1
[2022-07-21 16:09:26] local.INFO: optimizer:singleSql:[connection:mysql] execution times: 2.83ms; update `personal_access_tokens` set `last_used_at` = "2022-07-21 16:09:26", `personal_access_tokens`.`updated_at` = "2022-07-21 16:09:26" where `id` = 18
[2022-07-21 16:09:26] local.INFO: optimizer:singleRedis:[connection:default] execution times: 0.18ms; get "aa"
[2022-07-21 16:09:26] local.INFO: optimizer:singleSql:[connection:mysql] execution times: 1.03ms; select count(*) as aggregate from `organizations`
[2022-07-21 16:09:26] local.INFO: optimizer:singleSql:[connection:mysql] execution times: 0.64ms; select * from `organizations` limit 10 offset 0
[2022-07-21 16:09:26] local.INFO: optimizer:singleSql:[connection:mysql] execution times: 0.68ms; select * from `dic_areas` where `dic_areas`.`id` = 0 limit 1
[2022-07-21 16:09:26] local.INFO: optimizer:singleSql:[connection:mysql] execution times: 0.95ms; select * from `dic_areas` where `dic_areas`.`id` = 0 limit 1
[2022-07-21 16:09:26] local.INFO: optimizer:singleSql:[connection:mysql] execution times: 0.66ms; select * from `dic_areas` where `dic_areas`.`id` = 0 limit 1
[2022-07-21 16:09:26] local.INFO: optimizer:singleSql:[connection:mysql] execution times: 0.57ms; select * from `dic_areas` where `dic_areas`.`id` = 0 limit 1
[2022-07-21 16:09:26] local.INFO: optimizer:singleSql:[connection:mysql] execution times: 0.7ms; select * from `dic_areas` where `dic_areas`.`id` = 0 limit 1
[2022-07-21 16:09:26] local.INFO: optimizer:singleSql:[connection:mysql] execution times: 0.61ms; select * from `dic_areas` where `dic_areas`.`id` = 0 limit 1
[2022-07-21 16:09:26] local.INFO: optimizer:singleSql:[connection:mysql] execution times: 0.64ms; select * from `dic_areas` where `dic_areas`.`id` = 330000 limit 1
[2022-07-21 16:09:26] local.INFO: optimizer:singleSql:[connection:mysql] execution times: 0.63ms; select * from `dic_areas` where `dic_areas`.`id` = 330100 limit 1
[2022-07-21 16:09:26] local.INFO: optimizer:singleSql:[connection:mysql] execution times: 0.64ms; select * from `dic_areas` where `dic_areas`.`id` = 330104 limit 1
[2022-07-21 16:09:26] local.INFO: optimizer {"request_id":"2c6ce952-843a-47be-943b-d79c38350615","api_uri":"organizations","request_method":"GET","time":0.07205510139465332,"business_time":0.0471041202545166,"request_params":[],"response_content":"{\"status\":\"success\",\"result\":[{\"id\":1,\"platform_id\":\"11111111111\",\"name\":\"性能分析平台450752189154113\",\"alias_name\":\"性能分析2\",\"contact_name\":\"哈哈哈\",\"contact_phone\":\"150********\",\"contact_email\":\"610100380@qq.com\",\"memo\":\"测试\",\"status_name\":\"正常\",\"province_name\":null,\"city_name\":null,\"district_name\":null,\"created_at\":\"2021-10-21 17:13:51\",\"updated_at\":\"2022-07-21 09:42:20\"},{\"id\":2,\"platform_id\":\"22222222222222222222\",\"name\":\"科技有限公司\",\"alias_name\":\"亿云互动\",\"contact_name\":\"吴**\",\"contact_phone\":\"186********\",\"contact_email\":\"\",\"memo\":\"测试\",\"status_name\":\"正常\",\"province_name\":null,\"city_name\":null,\"district_name\":null,\"created_at\":\"2021-10-27 16:25:00\",\"updated_at\":\"2021-10-27 16:25:02\"},{\"id\":6,\"platform_id\":\"3333333333333333333\",\"name\":\"营销策划有限公司\",\"alias_name\":\"聚力\",\"contact_name\":\"呵呵呵\",\"contact_phone\":\"150********\",\"contact_email\":\"610100380@qq.com\",\"memo\":\"备注\",\"status_name\":\"正常\",\"province_name\":\"浙江省\",\"city_name\":\"杭州市\",\"district_name\":null,\"created_at\":\"2022-02-10 14:36:12\",\"updated_at\":\"2022-02-10 14:36:12\"}],\"meta\":{\"current_page\":1,\"last_page\":1,\"per_page\":10,\"total\":3}}","logs":[{"sql":"select * from `personal_access_tokens` where `personal_access_tokens`.`id` = ? limit 1","bindings":["18"],"time":0.0028399999999999996,"connection":"mysql"},{"sql":"select * from `users` where `users`.`id` = ? limit 1","bindings":[1],"time":0.00103,"connection":"mysql"},{"sql":"update `personal_access_tokens` set `last_used_at` = ?, `personal_access_tokens`.`updated_at` = ? where `id` = ?","bindings":["2022-07-21 16:09:26","2022-07-21 16:09:26",18],"time":0.00283,"connection":"mysql"},{"sql":"select count(*) as aggregate from `organizations`","bindings":[],"time":0.00103,"connection":"mysql"},{"sql":"select * from `organizations` limit 10 offset 0","bindings":[],"time":0.00064,"connection":"mysql"},{"sql":"select * from `dic_areas` where `dic_areas`.`id` = ? limit 1","bindings":[0],"time":0.00068,"connection":"mysql"},{"sql":"select * from `dic_areas` where `dic_areas`.`id` = ? limit 1","bindings":[0],"time":0.00095,"connection":"mysql"},{"sql":"select * from `dic_areas` where `dic_areas`.`id` = ? limit 1","bindings":[0],"time":0.00066,"connection":"mysql"},{"sql":"select * from `dic_areas` where `dic_areas`.`id` = ? limit 1","bindings":[0],"time":0.00057,"connection":"mysql"},{"sql":"select * from `dic_areas` where `dic_areas`.`id` = ? limit 1","bindings":[0],"time":0.0007,"connection":"mysql"},{"sql":"select * from `dic_areas` where `dic_areas`.`id` = ? limit 1","bindings":[0],"time":0.00061,"connection":"mysql"},{"sql":"select * from `dic_areas` where `dic_areas`.`id` = ? limit 1","bindings":[330000],"time":0.00064,"connection":"mysql"},{"sql":"select * from `dic_areas` where `dic_areas`.`id` = ? limit 1","bindings":[330100],"time":0.00063,"connection":"mysql"},{"sql":"select * from `dic_areas` where `dic_areas`.`id` = ? limit 1","bindings":[330104],"time":0.00064,"connection":"mysql"}],"redis_logs":[{"time":"0.00018","command":"get","parameters":["aa"],"connection_name":"default"}]}
7. 配置推荐
1、全量请求日志(日志量大,生产环境请勿使用)
- 占比(limiter.rate):100
2、所有接口共用日志记录频率(每10次请求,记录一次日志,缺点是部分请求量少的接口,可能正好请求在不记录中,导致接口无日志)
- 日志策略(limiter.strategy):request
- 顺序规则(limiter.rule):orderly
- 请求占比(limiter.rate):10
3、不同接口地址独立规则(推荐该方式),不同接口独立计算,顺序请求记录,每10次记录一次日志
- 日志策略(limiter.strategy):uri
- 顺序规则(limiter.rule):orderly
- 请求占比(limiter.rate):10