参考
作者: yangliuan
Eloquent ORM 解析入门
参考链接https://learnku.com/docs/laravel/5.5/eloquent/1332
Laravel 的 Eloquent ORM 提供了漂亮、简洁的 ActiveRecord 实现来和数据库交互。每个数据库表都有一个对应的「模型」用来与该表交互。你可以通过模型查询数据表中的数据,并将新记录添加到数据表中。
在开始之前,请确保在 config/database.php
中配置数据库连接。更多关于数据库的配置信息,请查看 文档。
艺术,优雅,强大,用过的都说好!
Eloquent 模型约定
数据表名 默认使用模型类的复数形式「蛇形命名」来作为表名 ,否则使用定义的 table属性
protected $connection = 'connection-name';//此模型的连接名称
protected $table = 'my_flights';//表名
public $timestamps = false; //关闭自己动维护时间戳
protected $dateFormat = 'U'; //模型日期字段存储格式 U表示unix时间戳
const CREATED_AT = 'creation_date'; //自定义创建时间字段
const UPDATED_AT = 'last_update'; //自定义更新时间字段
protected $primaryKey = 'id'; // 定义主键字段
protected $keyType = 'int'; //主键类型
public $incrementing = true; //是否开启auto-incrment 自增属性
protected $fillable = []; // 可以被批量赋值的属性
protected $guarded = []; // 不可批量复制的属性
protected $casts = ['is_admin'=>'boolean'];
//属性转换配合修改器使用时要保持类型一致
//转换成数组或json时的可见性
protected $visible = ['first_name', 'last_name'];//显示
//方法显示
$model->makeVisible('attribute')->toArray();
protected $hidden = ['password'];//隐藏
//方法隐藏
$model->makeHidden('attribute')->toArray();
protected $appends = ['is_admin']; //追加到模型数组表单的访问器。
//配合访问器
public function getIsAdminAttribute()
{
return $this->attributes['admin'] == 'yes';
}
//运行时追加
return $model->append('is_admin')->toArray();
return $model->setAppends(['is_admin'])->toArray();
模型方法
模型的查询构造器中注入了DB的查询构造器,所以模型也支持DB查询构造器的方法.这里只解析模型查询构造器的独有方法或模型和DB的特殊方法
all() 查询所有
$flights = App\Flight::all();//查询所有结果
模型内方法,实际调用的是DB的get方法,参数是字段支持数组和多个参数形式
all方法直接创建了一个查询构造器然后执行get()方法,所以调用all方法之前无法调用任何查询构造器的方法
public static function all($columns = ['*'])
{
return (new static)->newQuery()->get(
is_array($columns) ? $columns : func_get_args()
);
}
cursor() 游标遍历,适用于大数据量处理.
foreach (User::select('id', 'name')->cursor() as $user) {
//处理操作
dump($user);
}
内部实现使用PHP generator 进行遍历 生成器是协程调度, 耗时和消耗内存是恒定的(接近),跟数据量无关.
/**
* Get a generator for the given query.
*
* @return \Generator
*/
public function cursor()
{
foreach ($this->applyScopes()->query->cursor() as $record) {
yield $this->model->newFromBuilder($record);
}
}
chunk() 分块处理 适用于小数据量快速处理
Flight::chunk(200, function ($flights) {
foreach ($flights as $flight) {
//
}
});
内部实现 do while 循环 执行的分页查询sql 耗时和消耗内存取决分块数量,分块数量越大消耗内存和耗时相应减少
select * from `flights` order by `flights`.`id` asc limit 200 offset 0
select * from `flights` order by `flights`.`id` asc limit 200 offset 200
select * from `flights` order by `flights`.`id` asc limit 200 offset 400
附上测试代码
//内存计算函数
function print_memory_info($msg, $real_usage = false)
{
echo $msg, ceil(memory_get_usage($real_usage) / 1024), 'KB', '<br>';
return memory_get_usage($real_usage);
}
//使用游标方式
public function test1(Request $request)
{
$start_at = microtime(true);
$start = print_memory_info('cursor开始内存');
foreach (User::where('id', '<=', '10000')->cursor() as $user) {
//dump($user->id);
}
$end = print_memory_info('cursor结束内存');
$end_at = microtime(true);
echo '消耗内存 ',($end - $start) / 1024 .'KB','<br>';
echo '耗时 ',$end_at - $start_at,'微妙';
}
//使用chunk方式
public function test2(Request $request)
{
$start_at = microtime(true);
$start = print_memory_info('Chunk开始内存');
User::where('id', '<=', '10000')->Chunk(10000, function ($users) {
foreach ($users as $user) {
//dump($user->id);
}
});
$end = print_memory_info('Chunk结束内存');
$end_at = microtime(true);
echo '消耗内存 ', ($end - $start) / 1024 .'KB', '<br>';
echo '耗时 ', $end_at - $start_at, '微妙';
}
firstOrNew() 和 firstOrCreate() 和firstOrUpdate()
//传参支持两种方式 两个数组或一个数组 查询和写入的数据匹配为传入的数组
$user = User::firstOrNew(['name' => 'Favian Orn'], ['email' => 'max.koss@example.com']);
firstOrnew 返回的模型还尚未保存到数据库,必须要手动调用 save 方法才能保存它
如果想使用批量赋值,需要先调用fill方法
$user->fill(['name' => '1234'])->save();
$user = User::firstOrCreate(['name' => 'Favian Orn', 'email' => 'max.koss@example.com']);
$user->update(['name' => '1234']);
$user = User::firstOrUpdate(['name' => 'Favian Orn', 'email' => 'max.koss@example.com']);
Larave Horizon 队列仪表盘
安装使用参考文档
https://learnku.com/docs/laravel/5.5/horizon/1345#configuration
本文主要记录一些使用中碰到的问题,文档有的不再赘述.
配置文件介绍
/*
|--------------------------------------------------------------------------
| Horizon Redis Connection
|--------------------------------------------------------------------------
|
| This is the name of the Redis connection where Horizon will store the
| meta information required for it to function. It includes the list
| of supervisors, failed jobs, job metrics, and other information.
|
*/
'use' => 'payhandle',
指定horizon 使用的redis连接,连接配置可在config/database.php中增加
这是Horizon将存储的Redis连接的名称,它运作所需的元信息。 它包括清单主管,失业,工作指标和其他信息。
/*
|--------------------------------------------------------------------------
| Horizon Redis Prefix
|--------------------------------------------------------------------------
|
| This prefix will be used when storing all Horizon data in Redis. You
| may modify the prefix when you are running multiple installations
| of Horizon on the same server so that they don't have problems.
|
*/
'prefix' => env('HORIZON_PREFIX', 'horizon:'),
horizon 在redis中使用的存储前缀, 使用同一名称时,可以负载均衡水平扩展,不会重复处理队列中的任务.
在Redis中存储所有Horizon数据时将使用此前缀。 您可以在同一台服务器运行多个安装horizon时修改前缀,以便它们没有问题。
/*
|--------------------------------------------------------------------------
| Horizon Route Middleware
|--------------------------------------------------------------------------
|
| These middleware will get attached onto each Horizon route, giving you
| the chance to add your own middleware to this list or change any of
| the existing middleware. Or, you can simply stick with this list.
|
*/
'middleware' => ['web'],
Horizon设置的路由中间件
这些中间件将附加到每个Horizon路由上,为您提供机会将您自己的中间件添加到此列表或更改任何现有的中间件
/*
|--------------------------------------------------------------------------
| Queue Wait Time Thresholds
|--------------------------------------------------------------------------
|
| This option allows you to configure when the LongWaitDetected event
| will be fired. Every connection / queue combination may have its
| own, unique threshold (in seconds) before this event is fired.
|
*/
'waits' => [
'redis:default' => 60,
],
队列等待时间阈值
此选项允许您配置LongWaitDetected事件将被解雇的时间。 每个连接/队列组合都可以拥有它,触发此事件之前的唯一阈值(以秒为单位)。
/*
|--------------------------------------------------------------------------
| Job Trimming Times
|--------------------------------------------------------------------------
|
| Here you can configure for how long (in minutes) you desire Horizon to
| persist the recent and failed jobs. Typically, recent jobs are kept
| for one hour while all failed jobs are stored for an entire week.
|
*/
'trim' => [
'recent' => 60,
'failed' => 10080,
],
队列清空时间
在这里,您可以配置Horizon需要多长时间(以分钟为单位)坚持最近和失败的工作。 通常,保留最近的工作 一个小时,所有失败的工作都存储了整整一个星期。
/*
|--------------------------------------------------------------------------
| Fast Termination
|--------------------------------------------------------------------------
|
| When this option is enabled, Horizon's "terminate" command will not
| wait on all of the workers to terminate unless the --wait option
| is provided. Fast termination can shorten deployment delay by
| allowing a new instance of Horizon to start while the last
| instance will continue to terminate each of its workers.
|
*/
'fast_termination' => false,
快速终止
当这个选项被启用.Horizon的 terminate 命令 将不会等待,直到所有的worker(进程)终止后才终止, 除非提供了–wait选项 .快速终止可以缩短部署延时时间,允许新的 Horizon实例在最后一个实例启动时,终止他的所有worker(进程)
/*
|--------------------------------------------------------------------------
| Queue Worker Configuration
|--------------------------------------------------------------------------
|
| Here you may define the queue worker settings used by your application
| in all environments. These supervisors and settings handle all your
| queued jobs and will be provisioned by Horizon during deployment.
|
*/
'environments' => [
'production' => [
'supervisor-1' => [
'connection' => 'redis',
'queue' => ['default'],
'balance' => 'simple',
'processes' => 10,
'tries' => 3,
],
],
'local' => [
'supervisor-1' => [
'connection' => 'redis',
'queue' => ['default'],
'balance' => 'simple',
'processes' => 3,
'tries' => 3,
],
],
],
队列worker(进程)配置
这里可以定义你应用程序在所有的环境中,使用的队列 worker(进程)的设置. 这些 supervisor 和 配置 处理你所有的队列任务 ,在部署期间将由Horizon提供
Redis超时问题
https://blog.minhow.com/2018/01/14/laravel/predis-error-reading/
1.修改 Laravel 的 config/database.php 配置文件,找到 redis 的配置项,加上 read_write_timeout 参数:
'redis' => [
'cluster' => false,
'default' => [
'host' => env('REDIS_HOST', 'localhost'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379),
'database' => 0,
'read_write_timeout' => 0//增加此设置
],
],
2.修改 Redis 的 redis.conf 配置文件,找到 timeout 参数,改成0
timeout=0
Horizon 使用 Redis Cluster (集群)
https://github.com/laravel/horizon/issues/274
config/database.php
'redis' => [
'client' => env('REDIS_CLIENT', 'predis'),
'options' => [
'cluster' => env('REDIS_CLUSTER', 'predis'),
'prefix' => Str::slug(env('APP_NAME', 'laravel'), '_') . '_database_',
],
'clusters' => [
'default' => [
[
'host' => env('REDIS_HOST', 'localhost'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379),
'database' => 0,
'read_write_timeout' => 0
],
],
'cache' => [
[
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379),
'database' => env('REDIS_CACHE_DB', 1),
'read_write_timeout' => 0
]
],
'horizon' => [
[
'host' => env('REDIS_HOST', 'localhost'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379),
'database' => 2,
'read_write_timeout' => 0
],
],
],
config/horizon.php
'use' => env('HORIZON_USE', 'clusters.default'),
最关键的配置
Redis 集群
如果你的 Redis 队列驱动使用了 Redis 集群,你的队列名必须包含一个 key hash tag 。这是为了确保所有的 Redis 键对于一个队列都被放在同一哈希中。
key hash tag 介绍 https://learnku.com/articles/25070
config/horizon.php 需要给队列名称加{},laravel是以队列名作为redis的key
'environments' => [
'production' => [
'supervisor-1' => [
'connection' => 'redis',
'queue' => ['{default}'],
'balance' => 'simple',
'processes' => 10,
'tries' => 3,
],
],
'local' => [
'supervisor-1' => [
'connection' => 'redis',
'queue' => ['{default}'],
'balance' => 'simple',
'processes' => 10,
'tries' => 3,
],
],
],
如果使用queue队列,则修改config/queue.php,给队列名称加{}
'redis' => [
'driver' => 'redis',
'connection' => 'default',
'queue' => '{default}',
'retry_after' => 90,
],
Horizon不适用与redis集群
https://github.com/laravel/horizon/issues/274
horizon自定义队列配置
参考 https://github.com/laravel/horizon/issues/255
需求是要将队列的redis和和缓存的redis区分开,使用不同的redis。
目前尝试,默认队列一定要有,否则Job使用默认队列分发时,队列会出现不执行的情况。所以解决方案是修改config/queue.php中redis的链接来实现。
config/queue.php文件
'redis' => [
'driver' => 'redis',
'connection' => 'horizon',//修改队列连接
'queue' => env('REDIS_QUEUE', 'default'),
'retry_after' => 90,
'block_for' => null,
],
//此处也可以增加自动定义队列
'horizon' => [
'driver' => 'redis',
'connection' => 'horizon',
'queue' => env('HORIZON_REDIS_QUEUE', 'horizon'),
'retry_after' => 1800,
'block_for' => null,
],
config/horizon.php文件
默认队列要保留
'environments' => [
'production' => [
'supervisor-1' => [
'connection' => 'redis',
'queue' => ['default'],
'balance' => 'simple',
'processes' => 10,
'tries' => 3,
],
//此处对应horizon队列
'horizon' => [
'connection' => 'horizon',
'queue' => ['horizon'],
'balance' => 'simple',
'processes' => 10,
'tries' => 3,
'timeout' => 0,
],
],
'local' => [
'supervisor-1' => [
'connection' => 'redis',
'queue' => ['default'],
'balance' => 'simple',
'processes' => 3,
'tries' => 3,
],
//此处对应horizon队列
'horizon' => [
'connection' => 'horizon',
'queue' => ['horizon'],
'balance' => 'simple',
'processes' => 3,
'tries' => 3,
'timeout' => 0,
],
],
],
Laravel搭建OAuth2.0服务
OAuth2.0介绍
http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html
安装参考文档
本案例使用框架版本为5.5
https://learnku.com/docs/laravel/5.5/passport/1309#08129f
https://my.oschina.net/u/2400083/blog/1036519
composer require paragonie/random_compat=~2.0(如果出现包冲突)
composer require laravel/passport=~4.0
在 AuthServiceProvider
的 boot
方法中调用 Passport::routes
函数。这个函数会注册发出访问令牌并撤销访问令牌、客户端和个人访问令牌所必需的路由
授权模式使用演示
客户端模式
客户端模式(Client Credentials Grant)指客户端以自己的名义,而不是以用户的名义,向”服务提供商”进行认证。严格地说,客户端模式并不属于OAuth框架所要解决的问题。在这种模式中,用户直接向客户端注册,客户端以自己的名义要求”服务提供商”提供服务,其实不存在授权问题。
看一下storage目录是否有key ,没有key 执行 php artisan passport:keys
客户端凭据授权适用于机器到机器的认证。例如,你可以在通过 API 执行维护任务中使用此授权。要使用这种授权,你首先需要在 app/Http/Kernel.php
的 $routeMiddleware
变量中添加新的中间件:
use Laravel\Passport\Http\Middleware\CheckClientCredentials::class;
protected $routeMiddleware = [
'client' => CheckClientCredentials::class,
];
然后在路由上追加这个中间件:
Route::get('/orders', function(Request $request) {
...
})->middleware('client');
个人访问令牌
如果用户要在不经过典型的授权码重定向流的情况下向自己发出访问令牌,可以允许用户通过应用程序的用户界面对自己发出令牌,用户可以因此顺便测试你的 API,或者也可以将其作为一种更简单的发布访问令牌的方式。
{note} 个人访问令牌是永久有效的,就算使用了
tokensExpireIn
和refreshTokensExpireIn
方法也不会修改它的生命周期。token设置有效期解决方案 https://github.com/laravel/passport/issues/162
token设置有效期代码示例
<?php
namespace App\Providers;
use DateInterval;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Laravel\Passport\Bridge\PersonalAccessGrant;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* @var array
*/
protected $policies = [
'App\Model' => 'App\Policies\ModelPolicy',
];
/**
* Register any authentication / authorization services.
*/
public function boot()
{
$this->registerPolicies();
//设置个人token失效时间为7天
$authorizarionServer = app()->make(\League\OAuth2\Server\AuthorizationServer::class);
$authorizarionServer->enableGrantType(
new PersonalAccessGrant(),
new DateInterval('PT7D')
);
}
}
实现单一客户端登录(只有一个token生效)
https://segmentfault.com/q/1010000008481512/a-1020000008481698
https://learnku.com/laravel/t/16144
自定义认证看守器(guard)
场景:
需要非用户模型的表进行认证。比如物联网开发中,需要对设备进行认证为例。
安装步骤和配置步骤不变,请参考laravel passport文档
设备表迁移文件
Schema::create('devices', function(Blueprint $table)
{
$table->increments('id');
$table->string('mac_address', 17)->default('')->comment('设备mac地址')->index('mac_address','mac_indx');
$table->string('alias', 20)->default('')->comment('设备别名');
$table->timestamps();
});
DB::statement("ALTER TABLE devices comment '设备表'");
模型配置
//设备模型需要继承用户认证模型 Illuminate\Foundation\Auth\User
// 引用passport 的 HasApiTokens trait
<?php
namespace App\Models;
use Laravel\Passport\HasApiTokens;
use Illuminate\Foundation\Auth\User as Authenticatable;
class Device extends Authenticatable
{
use HasApiTokens;
protected $table = 'devices';
protected $fillable = ['mac_address', 'alias'];
protected $dates = [];
protected $casts = [];
protected $appends = [];
protected $hidden = [
'pivot',
];
}
config/auth.php 看守器
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
//增加设备看守器
'device' => [
'driver' => 'passport',
'provider' => 'devices',
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
//提供者增加设备模型
'devices' => [
'driver' => 'eloquent',
'model' => App\Models\Device::class,
],
],
app/Providers/AuthServiceProvider.php 服务提供者作用域配置
public function boot()
{
$this->registerPolicies();
Passport::routes();
Passport::loadKeysFrom('');
Passport::personalAccessTokensExpireIn(now()->addMonths(6));
Passport::tokensCan([
'api' => 'api',
]);
}
作用域中间件配置
protected $routeMiddleware = [
...
'scopes' => \Laravel\Passport\Http\Middleware\CheckScopes::class,
'scope' => \Laravel\Passport\Http\Middleware\CheckForAnyScope::class,
];
路由和控制器测试代码
Route::prefix('test')->group(function () {
Route::post('login', [TestController::class,'login']);
Route::get('show', [TestController::class,'show'])->middleware(['auth:device','scope:api']);
});
<?php
namespace App\Http\Controllers;
use App\Models\Device;
use App\Models\User;
use Illuminate\Http\Request;
class TestController extends Controller
{
public function login(Request $request)
{
$guard_model = Device::find(1);
$token = $guard_model->createToken('device', ['api'])->accessToken;
return response()->json([
'token' => $token
]);
}
public function show(Request $request)
{
return response()->json($request->user('device'));
}
}
测试结果使用device guard看守器成功获取模型
自建SSR服务器
作为一个码农如果不能翻墙上Google,工作起来很不方便.以前买过国产的梯子流量都没用完就挂了.lantern免费版有流量限制,收费版赶山一台vps的费用了.最后决定自己搭建一个.
购买一台VPS
参考链接https://tlanyan.me/vps-merchant-collection/
最后选择AWS Lightsail廉价版的AWS,月资费3.5刀起,需要信用卡
https://aws.amazon.com/cn/lightsail/
创建过程按照指引即可,云服务器创建大同小异,需要注意的是,AWS使用的免密登录,创建完成后记得下载秘钥文件
给代理端口添加白名单
搭建ssr服务端
参考https://cndaqiang.github.io/2017/09/28/ubuntu1604-ssr/#%E9%81%87%E5%88%B0%E7%9A%84%E9%97%AE%E9%A2%98
安装python
$ sudo apt-get update
$ sudo apt-get install python
下载安装包
$ mkdir shadowsocksr
$ cd shadowsocksr/
$ wget https://github.com/cndaqiang/shadowsocksr/archive/manyuser.zip
fork了一个仓库用于备份
https://github.com/yangliuan/shadowsocksr
安装配置
$ sudo apt-get install unzip
$ unzip manyuser.zip
cd shadowsocksr-manyuser/
解压之后修改配置文件 config.json
- server 服务器公网IP
- server_ipv6 服务器ipv6
- server_port 代理端口
- pasword 密码
- method 加密方式 这里使用aes-256-cfb
- protocol 协议 origin
- obfs 混淆方式 http_simple
- additional 多租户配置
基本操作 启动 停止
sudo python ./shadowsocks/server.py -c config.json -d start
sudo python ./shadowsocks/server.py -c config.json -d stop
配置开机启动
方式很多略过
配置ssr客户端
参考链接https://alanlee.fun/2018/05/18/ubuntu-ssr/
github https://github.com/erguotou520/electron-ssr 作者已删除
备份地址 https://github.com/qingshuisiyuan/electron-ssr-backup
下载deb安装包,安装成功后/opt下会有electron-ssr 如果没有出现快捷方式,可能是安装有问题(权限),可以重启后再试试
其它操作系统参考文档
客户端配置项要与服务端一致
WEB应用常见漏洞
Sql注入
原理:利用网页程序没有对传参进行过滤,执行不允许的sql语句获取重要数据 百度百科 维基百科
防御
后端程序对请求参数进行严格过滤,使用框架的查询构造器和ORM操作Sql,禁止直接用代码拼接sql语句
SQL注入详解
XSS(Cross site scripting 跨站脚本攻击)
原理 前端运行了用户输入的恶意js脚本
防御
php函数htmlspecialchars转义html标签
过滤敏感js标签 ezyang/htmlpurifier,
现代前端框架会自动处理xss问题
CSRF(Cross-site request forgery 跨站请求伪造)
原理 MDN文档说明
防御
SSRF(Server-Side Request Forgery 服务器端请求伪造)
原理
防御
XXE(XML外部实体注入)
原理
防御
参考
Linux 文件权限
linux基本权限
基本权限
左侧第一栏 -drwxr-xr-x表示权限,共10位字符
1.第一位字符表示文件类型
d 目录
– 文件
l 软链接
b 块设备(存储设备接口)文件 如,磁盘
c 串行端口设备 如 键盘 鼠标 usb 等
s 套接字文件 (socket) ,用于进程间通信
p 管道文件 FIFO pipe 用于解决多个程序访问一个文件时造成的错误问题
2.后九位没三位一组表示 user(所有者) group(所属组) other(其它人)的权限
3.操作权限
r : 文件=>读取文件内容( 可用命令 cat more head tail) 目录=>列出目录(ls ll)
w: 文件=>编辑 、新增、修改文件内容(vi echo). 不包括删除文件,删除文件需要文件所在目录(上级)拥有w 和 x权限 . 目录=>新建 删除文件目录, 重命名 移动 (touch rm mv cp)
写文件内容(文件本身) 和 创建删除文件 是不同的权限啊(所在目录),
x:执行权限
权限的作用范围仅限子级
PHP的运行模式
基本概念
CGI 通用网关接口 Common Gateway Interface
CGI 是Web 服务器运行外部程序的规范(协议),按CGI 编写的程序可以扩展服务器功能。CGI 应用程序能与浏览器进行交互,还可通过数据库API 与数据库服务器等外部数据源进行通信,从数据库服务器中获取数据。格式化为HTML文档后,发送给浏览器,也可以将从浏览器获得的数据放到数据库中。几乎所有服务器都支持CGI,可用任何语言编写CGI,包括流行的C、C ++、VB 和Delphi 等。CGI 分为标准CGI 和间接CGI两种。标准CGI 使用命令行参数或环境变量表示服务器的详细请求,服务器与浏览器通信采用标准输入输出方式。间接CGI 又称缓冲CGI,在CGI 程序和CGI 接口之间插入一个缓冲程序,缓冲程序与CGI 接口间用标准输入输出进行通信。
功能,处理浏览器发送的表单请求,处理后并给浏览器响应(HTML).PHP就是C语言编写的CGI程序.其它ASP,JSP, .NET等
简而言之,来自客户端的HTTP POST请求将通过标准输入发送CGI程序HTML表单数据。其他数据(如URL路径和HTTP标头数据)显示为过程环境变量。
缺点
每有一个用户请求,都会先要创建CGI的子进程,然后处理请求,处理完后结束这个子进程(Fork-And-Execute模式). 这种方式开销大,请求过多和高并发是占用系统资源多.
FastCGI 快速通用网关接口 FastCommonGatewayInterface
FastCGI(一种二进制协议,用于将交互式程序与Web服务器连接)像是一个常驻(long-live)型的CGI,它可以一直执行着,只要激活后,不会每次都要花费时间去fork一次(这是CGI最为人诟病的fork-and-execute 模式)。它还支持分布式的运算, 即 FastCGI 程序可以在网站服务器以外的主机上执行并且接受来自其它网站服务器来的请求。
每个单独的FastCGI进程可以在其生命周期内处理许多请求,从而避免了每个请求进程创建和终止的开销。同时处理多个请求可以通过几种方式实现:通过使用具有内部多路复用的单个连接(即通过单个连接的多个请求); 通过使用多个连接; 或者通过这些技术的组合。可以配置多个FastCGI服务器,从而提高稳定性和可扩展性。
进入正题
SAPI
SAPI(Server Application Programming Interface,服务端应用编程端口 )是PHP的应用接口层,提供php和外部程序(web服务器和shell终端)通信的接口.PHP为不同的web服务器提供多种SAPI接口,运行模式指的就是PHP运行的是那种SAPI
获取SAPI http://php.net/manual/zh/function.php-sapi-name.php
php_sapi_name() 和 PHP_SAPI
返回描述 PHP 所使用的接口类型(the Server API, SAPI)的小写字符串。 例如,CLI 的 PHP 下这个字符串会是 “cli”,Apache 下可能会有几个不同的值,取决于具体使用的 SAPI。 以下列出了可能的值。
可能返回的值包括了 aolserver、apache、 apache2filter、apache2handler、 caudium、cgi (直到 PHP 5.3), cgi-fcgi、cli、 cli-server、 continuity、embed、fpm-fcgi、 isapi、litespeed、 milter、nsapi、 phttpd、pi3web、roxen、 thttpd、tux 和 webjames。
CLI模式
http://php.net/manual/zh/features.commandline.php
从版本 4.3.0 开始,PHP 提供了一种新类型的 CLI SAPI(Server Application Programming Interface,服务端应用编程端口)支持,名为 CLI,意为 Command Line Interface,即命令行接口。顾名思义,该 CLI SAPI 模块主要用作 PHP 的开发外壳应用。CLI SAPI 和其它 CLI SAPI 模块相比有很多的不同之处,我们将在本章中详细阐述。值得一提的是,CLI 和 CGI 是不同的 SAPI,尽管它们之间有很多共同的行为。
CLI-Server
PHP 5.4.0起, CLI SAPI 提供了一个内置的Web服务器。这个内置的Web服务器主要用于本地开发使用,不可用于线上产品http://php.net/manual/zh/features.commandline.webserver.php
CGI 模式
http://php.net/manual/zh/install.unix.commandline.php
LoadModule cgi_module modules/mod_cgi.so —cgi 模块 apache
FastCGI模式
FPM模式
http://php.net/manual/zh/install.fpm.php
FPM(FastCGI 进程管理器)用于替换 PHP FastCGI 的大部分附加功能,对于高负载网站是非常有用的。
PHP-FPM 这种模型是非常典型的多进程同步模型,由一个master主进程多个worker进程组成.master进程创建时会创建一个sokect,但是不会处理请求,只负责调度worker进程
worker进程的生命周期,竞争的接受请求,解析FastCGI协议的数据,处理对应脚本,处理完成关闭请求,然后等待新的连接.同一时刻只能处理一个请求(同步阻塞IO).
master进程与worker进程之间不会直接通讯,master进程通过共享内存方式获取worker进程的信息,通过信号方式杀死worker进程.(进程间通讯)
缺点
每次启动都与需要重新加载php文件,浪费了一部分性能
ISAPI模式 (PHP 5.3)
ISAPI(Internet Server Application Program Interface)是微软提供的一套面向 Internet 服务的 API 接口,一个 ISAPI 的 DLL,可以在被用户请求激活后长驻内存,等待用户的另一个请求,还可以在一个 DLL 里设置多个用户请求处理函数,此外,ISAPI 的 DLL 应用程序和 WEB 服务器处于同一个进程中,效率要显著高于 CGI。由于微软的排他性,只能运行于 Windows 环境。
Module 模式
http://php.net/manual/zh/install.unix.apache2.php
PHP 常常与 Apache 服务器搭配形成 LAMP 配套的运行环境。把 PHP 作为一个子模块集成到 Apache 中,就是 Module 模式,Apache 中的常见配置如下:
LoadModule php5_module modules/mod_php5.so
这使用了 LoadModule
命令,该命令的第一个参数是模块的名称,名称可以在模块实现的源码中找到。第二个选项是该模块所处的路径。如果需要在服务器运行时加载模块,可以通过发送信号 HUP
或者 AP_SIG_GRACEFUL
给服务器,一旦接受到该信号,Apache 将重新装载模块,而不需要重新启动服务器。通过注册到 apache2 的 ap_hook_post_config
挂钩,在 Apache 启动的时候启动此模块以接受 PHP 文件的请求。
例如,当客户端访问 PHP 文件时,Apache 就会调用 php5_module
来解析 PHP 脚本。Apache 每接收到一个请求,都会产生一个进程来连接 PHP 完成请求。在 Module 模式下,有时候会因为把 PHP 作为模块编进 Apache,而导致出现问题时很难定位是 PHP 的问题还是 Apache 的问题。
Apache MPM(多处理模块) 选择
要求 更高伸缩性的站点可以选择使用线程的 MPM,即 worker
或 event
需要可靠性或者与旧软件兼容的站点可以使用 prefork
。
运行方式总结
linux + nginx + php-fpm 主流 + 性能和兼容性兼顾
linux + nginx + php-cli + workman/webman 高性能 兼容原先的composer代码库
linux + nginx + php-cli (swoole-cli) 对旧代码兼容不好,如需要重建协程组件
linux + apache mpm event + php-fpm
linux + apache mpm + mod_php
I/O性能优先级
swoole(cli) > php-fpm > mod_php
相关参考
深入理解Zend SAPIs(Zend SAPI Internals) 鸟哥博客
Webshell查杀工具
1.百度出品 WEBDIR+ https://scanner.baidu.com/#/pages/intro
web版在线查杀使用方便,上传时推荐使用压缩率比较高的格式,如xz文件
2.WebShellkiller http://edr.sangfor.com.cn/backdoor_detection.html
推荐使用第二种方式检出数量优先,然后人工排除,第一种有方式有很多扫描不出来
3.河马查杀 http://www.shellpub.com/
4 .D盾查杀 http://www.d99net.net
总结
1.开源框架或cms产品及时更新安全补丁,查不出结果时,对比目录结构文件,使用tree命令,然后人工审核,混入一堆乱码(免杀处理)的都是木马.
2.扫描站点的运行木目录,nginx 或apache配置的root目录 .laravel 或tp5框架 运行目录为public ,木马只能放在此目录运行,主要扫描这个目录就可以
3.查找站点下最近修改的php文件.
- find ./ -name “*.php” -mtime 0 查找当前目录下24小时内更改的php文件
- find ./ -name “*.php” -mmin -60 查找当前目录下60分钟内更改的PHP文件
参考
nginx 让users有权限启动的两种方法
普通用户在restart和reload nginx时,会报错:
nginx: [warn] the “user” directive makes sense only if the master process runs with super-user privileges, ignored in /usr/local/nginx/conf/nginx.conf:2
我又不能给开发人员root权限,没办法,只好这么做。
原因是:默认情况下Linux的1024以下端口是只有root用户才有权限占用
方法一:
所有用户都可以运行(因为是755权限,文件所有者:root,组所有者:root)
chown root.root nginx
chmod 755 nginx
chmod u+s nginx
方法二:
仅 root 用户和 wyq 用户可以运行(因为是750权限,文件所有者:root,组所有者:www)
chown root.www nginx
chmod 750 nginx
chmod u+s nginx
转载原文https://blog.csdn.net/yan7895566/article/details/79876059