range

If there were you, the world would be just right

<?php

/**
 * 链式调用方式
 * Class Myreques
 */
class Myreques{

    private $do = false;

    private $resul;

    public function if($bool=false)
    {
        $this->do = $bool;
        return clone $this;
    }

    public function then(callable $func)
    {
        if($this->do){
            $this->resul = $func();
            $this->do = !$this->do;
        }
        return clone $this;
    }

    public function getResul()
    {
        return $this->resul;
    }
}
$req = new Myreques();
$true = true;
$p = json_decode('{"name":"Chinese"}');
$res = $req->if($true)
            ->then(function () use($p){
                return $p->name;
            })
            ->getResul();
print_r($res);

使用闭包

  1. 减少foreach的循环的代码
  2. 减少函数的参数
  3. 解除递归函数

基础使用示例

// 例一: 闭包:匿名函数赋予变量
$clos = function (){
    return 111;
};
var_dump($clos); // object(Closure)#1 (0) { }
die;

//例二:也可以直接将匿名函数进行传递。
$clos = function($a){
    return $a;
};
var_dump($clos(2)); // int(2)
die;

//例三 :连接闭包和外界变量的关键字:USE
function getMoney() {
 $rmb = 1;
 $dollar = 6;
 $func = function() use ( $rmb ) {
  echo $rmb;
  echo $dollar;
 };
 $func();
}
getMoney();
die;
//输出:1
//报错,找不到dorllar变量


//例四:在匿名函数中改变上下文的变量
function getMoney2() {
  $rmb = 1;
  $func = function() use ( &$rmb ) {
   echo $rmb . "<br>";
     //把$rmb的值加1
   $rmb++;
 };
 $func();
 echo $rmb;
}
getMoney2();
die;
//输出:
//1
//2

// 例5
$arr = [1, 2, 3, 4, 5, 6];

//使用array_reduce求和
function sum($arr)
{
    return array_reduce($arr, function ($x, $y) {
        return $x + $y;
    });
}
var_dump(sum($arr)); //int(21)

实际应用

# 实现容器
class Di
{
    private $factory;
 
    public function set($id, $value)
    {
        $this->factory[$id] = $value;
    }
 
    public function get($id)
    {
        $val = $this->factory[$id];
        return $val();//如果不加括号,仅仅返回的是闭包类,并不是User实例
    }
}
 
class User
{
    private $username;
 
    public function __construct($username = '')
    {
        $this->username = $username;
    }
 
    public function getUserName()
    {
        return $this->username;
    }
}
 
$di = new Di();
 
// 在此使用了闭包,所以实际上并不会实例化User类,只有在后面get的时候才会实例化
$di->set('a', function(){
    return new User('张三');
});
 
var_dump($di->get('a')->getUserName());

应用场景1:在很多时候,我们会把数据库中查出来的数据存入缓存,当缓存失效时,再从数据库中取出数据并重新添加到缓存中。但是如果这时并发量很大时,会有很多进程同时去数据库取数据,很多请求穿透到数据库,最终导致数据库崩溃

<?php
    $data = $cache->get('key');
    if(!$data){
        $fp = fopen('lock.txt',"w+");
        if(flock($fp, LOCK_EX)){
            $data = $cache->get('key');//拿到锁后再次检查缓存,这时可能已经有了
            if(!$data){
                $data = mysql->query();
                $cache->set('key', $data);
            }
            flock($fp, LOCK_UN);
        }
        fclose($fp);
    }

应用场景2:准点抢游戏皮肤,我们的代码是以下这种情况:

<?php
    $count = 检查数据库中的库存量
    if($count > 0){
        生成订单,皮肤库存减一
    }

在正常情况下,上面的这个demo是没有任何问题的,但是当大并发产生时,在执行数据库减一之前,库存只剩下1个时,用户1查到库存>0,用户2这个时候也查到库存>0...,后面的代码继续执行时,库存就会出现负数。

解决方案:

  1. 用额外的单进程处理一个队列,下单请求放到队列里,一个个处理,就不会有并发的问题了,但是要额外的后台进程以及延迟问题,不予考虑。
  2. 借助文件锁,如果锁定失败说明有其他订单正在处理,此时要么等待要么直接提示用户"服务器繁忙"

文件锁: (非阻塞【不等待】模式)

<?php
    $fp = fopen("lock.txt", "w+");
    if(flock($fp,LOCK_EX | LOCK_NB)){
      //..处理订单
      flock($fp,LOCK_UN);
    }else{
      echo "系统繁忙,请稍后再试";
    }

    fclose($fp);

文件锁: (阻塞【等待】模式)

<?php
    $fp = fopen("lock.txt", "w+");
    if(flock($fp,LOCK_EX)){
      //..处理订单
      flock($fp,LOCK_UN);
    }
    fclose($fp);

处理高并发:https://blog.csdn.net/gaisidewangzhan1/article/details/80186584


1、区别只有在继承中才能体现出来,如果没有任何继承,那么这两者是没有区别的。
2、new self()返回的实例是万年不变的,无论谁去调用,都返回同一个类的实例,而new static()则是由调用者决定的。
例子:

<?
class Father {

    public function NewSelf() {
        return new self();
    }

    public function Newstatic() {
        return new static();
    }
}
class Sun extends Father {

}
$sun = new Sun();

print get_class($sun->NewSelf());     //=> 返回的是Father类
print get_class($sun->Newstatic());  //=> 返回的是sun类  

Task进程是独立与worker进程的一个进程.他主要处理耗时较长的业务逻辑.并且不影响worker进程处理客户端的请求
适用场景:

情景一:管理员需要给100W用户发送邮件,当点击发送,浏览器会一直转圈,直到邮件全部发送完毕。 
情景二:处理大量数据 
情景三: 数据库写入

这其实都是php进程一直被阻塞,客户端才一直在等待服务端的响应,我们的代码就是同步执行的。对于用户而言,这就是漫长的等待。

task问题

如果投递的任务量总是大于task进程的处理能力,建议适当的调大task_worker_num的数量,增加task进程数,不然一旦task塞满缓冲区,就会导致worker进程阻塞,所以需要使用好task前期必须有所规划 task对worker的影响 

关于task_worker的配置个数问题:

![Snipaste_2021-06-18_16-40-15.jpg][1]