php redis 一个信号量的实现

use Predis\Client;
use Predis\Transaction\MultiExec;
use Ramsey\Uuid\Uuid;

/**
 * Class Semaphore
 *
 * A fair, race free implementation of a counting semaphore, based on the algorithm description in
 * section 6.3 of "Redis in Action"
 * (https://redislabs.com/ebook/part-2-core-concepts/chapter-6-application-components-in-redis/6-3-counting-semaphores/)
 *
 * @package CodeOrange\RedisCountingSemaphore
 */
class Semaphore {
    private $client;
    private $name;
    private $limit;
    private $timeout;
    /** @var string $identifier Identifier for the acquired semaphore */
    private $identifier = null;

    /**
     * Semaphore constructor.
     * @param Client $client Predis client with an open connection
     * @param string $name Name of the semaphore
     * @param int $limit The amount of resources this semaphore protects
     * @param int $timeout Timeout of an acquired semaphore, in seconds
     */
    public function __construct(Client $client, $name, $limit = 1, $timeout = 10) {
        $this->client = $client;
        $this->name = $name;
        $this->limit = $limit;
        $this->timeout = $timeout;
    }

    /**
     * @return string
     */
    public function getIdentifier() {
        return $this->identifier;
    }

    /**
     * Try to acquire a semaphore
     *
     * @param float $sleep Number of seconds to sleep between retries. If null, this function will not retry but return immediately.
     * @param int $retries Number of times to retry before giving up
     * @return bool Whether or not the semaphore was acquired correctly
     */
    public function acquire($sleep = null, $retries = null) {
        if ($this->identifier) {
            // We already have it
            return true;
        }
        if ($sleep == null || $retries == null) {
            $acquired = $this->acquire_fair_with_lock();
            return $acquired;
        }

        while ($sleep > 0 && $retries > 0) {
            $acquired = $this->acquire_fair_with_lock();
            if ($acquired) {
                return true;
            }
            $retries -= 1;
            sleep($sleep);
        }
        return false;
    }

    /**
     * Release this semaphore
     *
     * @return void
     */
    public function release() {
        if (!$this->identifier) {
            // We didn't have it
            return;
        }
        if (!$this->client->isConnected()) {
            $this->client = get_predis_client();
        }
        $this->release_fair();
    }

    /**
     * Refresh the semaphore
     *
     * @return bool Whether or not we still have the semaphore
     */
    public function refresh() {
        if (!$this->identifier) {
            return false;
        }
        if (!$this->client->isConnected()) {
            $this->client = get_predis_client();
        }
        return $this->refresh_fair();
    }

    //<editor-fold desc="Methods as built up to in the book">
    private function acquire_unfair() {
        $identifier = (string)Uuid::uuid4();
        $now = time();
        $transaction = $this->client->transaction();
        // Time out old identifiers
        $transaction->zremrangebyscore($this->name, '-inf', $now - $this->timeout);
        // Try to acquire semaphore
        $transaction->zadd($this->name, [$identifier => $now]);
        // Check to see if we have it
        $transaction->zrank($this->name, $identifier);
        $result = $transaction->execute();
        $rank = $result[count($result) - 1];
        if ($rank < $this->limit) {
            // We got it!
            $this->identifier = $identifier;
            return true;
        }
        // We didn't get it, remove the identifier from the table
        $this->client->zrem($this->name, $identifier);
        return false;
    }

    private function release_unfair() {
        $id = $this->identifier;
        $this->identifier = null;
        return $this->client->zrem($this->name, $id);
    }

    private function acquire_fair() {
        $identifier = (string)Uuid::uuid4();
        $cszet = $this->name . ':owner';
        $ctr = $this->name . ':counter';
        $now = time();
        $transaction = $this->client->transaction();
        // Time out old entries
        $transaction->zremrangebyscore($this->name, '-inf', $now - $this->timeout);
        $transaction->zinterstore($cszet, [$cszet, $this->name], ['weights' => [1, 0]]);
        // Get the counter
        $transaction->incr($ctr);
        $result = $transaction->execute();
        $counter = $result[count($result) - 1];
        // Try to acquire the semaphore
        $transaction = $this->client->transaction();
        $transaction->zadd($this->name, [$identifier => $now]);
        $transaction->zadd($cszet, [$identifier => $counter]);
        // Check the rank to determine if we got the semaphore
        $transaction->zrank($cszet, $identifier);
        $result = $transaction->execute();
        $rank = $result[count($result) - 1];
        if ($rank < $this->limit) {
            // We got it!
            $this->identifier = $identifier;
            return true;
        }
        // We didn't get the semaphore, clean out the bad data
        $transaction = $this->client->transaction();
        $transaction->zrem($this->name, $identifier);
        $transaction->zrem($cszet, $identifier);
        $transaction->execute();
        return false;
    }

    private function release_fair() {
        $id = $this->identifier;
        $this->identifier = null;
        $transaction = $this->client->transaction();
        $transaction->zrem($this->name, $id);
        $transaction->zrem($this->name . ':owner', $id);
        return $transaction->execute()[0];
    }

    private function refresh_fair() {
        if ($this->client->zadd($this->name, [$this->identifier => time()])) {
            // We lost it
            $this->release_fair();
            return false;
        }
        // We still have it
        return true;
    }

    private function acquire_fair_with_lock() {
        $identifier = $this->acquire_lock(0.01);
        if ($identifier) {
            try {
                return $this->acquire_fair();
            } finally {
                $this->release_lock($identifier);
            }
        }
        return false;
    }

    // From section 6.2 of the book
    private function acquire_lock($acquire_timeout = 10) {
        $identifier = (string)Uuid::uuid4();
        $end = time() + $acquire_timeout;
        while (time() < $end) {
            $lock_name = 'lock:' . $this->name;
            $res = $this->client->setnx($lock_name, $identifier);
            if ($res) {
                //设置10s的过期时间
                $this->client->expire($lock_name, 10);
                return $identifier;
            }
            sleep(0.001);
        }
        return false;
    }

    private function release_lock($id) {
        $lock_name = 'lock:' . $this->name;
        $res = $this->client->transaction(['watch' => $lock_name, 'cas' => true, 'retry' => 1000], function (MultiExec $t) use ($id, $lock_name) {
            $value = $t->get($lock_name);
            if ($value === $id) {
                $t->multi();
                $t->del([$lock_name]);
            }
        });
        if ($res) {
            return true;
        } else {
            // We didn't execute anything, so we've lost the lock
            return false;
        }
    }
    //</editor-fold>
}

PHP 一个redis锁案例

<?php

class RedLock {
    private $retryDelay;
    private $retryCount;
    private $clockDriftFactor = 0.01;
    private $quorum;
    private $servers = array();
    private $instances = array();

    function __construct(array $servers, $retryDelay = 200, $retryCount = 0) {
        $this->servers = $servers;
        $this->retryDelay = $retryDelay;
        $this->retryCount = $retryCount;
        $this->quorum = min(count($servers), (count($servers) / 2 + 1));
    }

    public function lock($resource, $ttl) {
        $this->initInstances();
        $token = uniqid();
        $retry = $this->retryCount;
        do {
            $n = 0;
            $startTime = microtime(true) * 1000;
            foreach ($this->instances as $instance) {
                if ($this->lockInstance($instance, $resource, $token, $ttl)) {
                    $n++;
                }
            }
            # Add 2 milliseconds to the drift to account for Redis expires
            # precision, which is 1 millisecond, plus 1 millisecond min drift
            # for small TTLs.
            $drift = ($ttl * $this->clockDriftFactor) + 2;
            $validityTime = $ttl - (microtime(true) * 1000 - $startTime) - $drift;
            if ($n >= $this->quorum && $validityTime > 0) {
                return [
                    'validity' => $validityTime,
                    'resource' => $resource,
                    'token' => $token,
                ];
            } else {
                foreach ($this->instances as $instance) {
                    $this->unlockInstance($instance, $resource, $token);
                }
            }
            // Wait a random delay before to retry
            $delay = mt_rand(floor($this->retryDelay / 2), $this->retryDelay);
            usleep($delay * 1000);
            $retry--;
        } while ($retry > 0);
        return false;
    }

    public function unlock(array $lock) {
        $this->initInstances();
        $resource = $lock['resource'];
        $token = $lock['token'];
        foreach ($this->instances as $instance) {
            $this->unlockInstance($instance, $resource, $token);
        }
    }

    private function initInstances() {
        if (empty($this->instances)) {
            foreach ($this->servers as $server) {
                list($host, $port, $timeout) = $server;
                $redis = new \Redis();
                $redis->connect($host, $port, $timeout);
                $this->instances[] = $redis;
            }
        }
    }

    private function lockInstance($instance, $resource, $token, $ttl) {
        return $instance->set($resource, $token, ['NX', 'PX' => $ttl]);
    }

    private function unlockInstance($instance, $resource, $token) {
        $script = '
            if redis.call("GET", KEYS[1]) == ARGV[1] then
                return redis.call("DEL", KEYS[1])
            else
                return 0
            end
        ';
        return $instance->eval($script, [$resource, $token], 1);
    }
}

简易版实现

use Ramsey\Uuid\Uuid;

class RedisLock {

    private $redis;

    /**
     * RedisLock constructor.
     * @param $redis Redis
     */
    public function __construct($redis) {
        $this->redis = $redis;
    }

    /**
     * @param $lock_name
     * @param int $acquire_time
     * @param int $lock_timeout
     * @return bool|string
     *
     * @throws Exception
     */
    public function acquire_lock($lock_name, $acquire_time = 0, $lock_timeout = 10) {
        $identifier = (string)Uuid::uuid4();
        $lock_name = 'lock:' . $lock_name;
        $lock_timeout = intval(ceil($lock_timeout));
        if ($acquire_time == 0) {
            $result = $this->got_lock($lock_name, $identifier, $lock_timeout);
        } else {
            $end_time = time() + $acquire_time;
            while (time() < $end_time) {
                $result = $this->got_lock($lock_name, $identifier, $lock_timeout);
                if ($result != '1') {
                    //sleep 0.1 sec
                    usleep(100000);
                }
            }
        }
        return $result == '1' ? $identifier : false;
    }

    private function got_lock($lock_name, $identifier, $lock_timeout) {
        $script = <<<luascript
                 local result = redis.call('setnx',KEYS[1],ARGV[1]);
                    if result == 1 then
                        redis.call('expire',KEYS[1],ARGV[2])
                        return 1
                    elseif redis.call('ttl',KEYS[1]) == -1 then
                       redis.call('expire',KEYS[1],ARGV[2])
                       return 0
                    end
                    return 0
luascript;
        $result = $this->redis->evaluate($script, array($lock_name, $identifier, $lock_timeout), 1);
        return $result;
    }

    /**
     * @param $lock_name
     * @param $identifier
     * @return bool
     */
    public function release_lock($lock_name, $identifier) {
        $lock_name = 'lock:' . $lock_name;
        while (true) {
            $script = <<<luascript
                local result = redis.call('get',KEYS[1]);
                if result == ARGV[1] then
                    if redis.call('del',KEYS[1]) == 1 then
                        return 1;
                    end
                end
                return 0
luascript;
            $result = $this->redis->evaluate($script, array($lock_name, $identifier), 1);
            if ($result == 1) {
                return true;
            }
            break;
        }
        return false;
    }
}

How to Read the PHP Slow Request Log

The PHP slow request log is where PHP records information about any request that takes more than five seconds to execute.

Having slow PHP code not only creates a bad experience for your site’s visitors, but it is also expensive for you. An app with fast PHP code only needs a single PHP process to handle many requests each second. However, since each PHP process can only execute one request at a time, when a PHP script is slow, the app needs additional PHP processes to handle the requests. Each additional process uses more memory, so having slow code means your server needs a lot more memory.

If your server has high memory usage or requests that are timing out, you may have slow requests that you can debug by reading the PHP slow request log.

Location of the PHP Slow Request Log

On the ServerPilot Business plan, you can view the PHP slow request log from each app’s Logs tab in ServerPilot.

If you’re not on the Business plan, you can SSH or SFTP into your server to view the PHP slow request log.

Each app has its own PHP slow request log. The log file is located under the app’s system user’s home directory at:

log/APPNAME/APPNAME_phpX.Y.slow.log

where phpX.Y is the PHP version the app is using, for example php7.0.

Understanding the PHP Slow Request Log

The PHP slow request log records stack traces (also known as tracebacks) of each slowly executing script at the moment the request passed five seconds in execution.

For example, an entry in the slow request log might look like this:

[06-Oct-2015 12:20:34]  [pool example] pid 1852
script_filename = /srv/users/serverpilot/apps/example/public/index.php
[0x00007f7e475ba138] curl_exec() /srv/users/serverpilot/apps/example/public/wp-content/plugins/wp-piwik/classes/WP_Piwik/Request/Rest.php:44
[0x00007f7e475b9f10] curl() /srv/users/serverpilot/apps/example/public/wp-content/plugins/wp-piwik/classes/WP_Piwik/Request/Rest.php:20
[0x00007f7e475b9d48] request() /srv/users/serverpilot/apps/example/public/wp-content/plugins/wp-piwik/classes/WP_Piwik/Request.php:63
[0x00007f7e475b9be0] perform() /srv/users/serverpilot/apps/example/public/wp-content/plugins/wp-piwik/classes/WP_Piwik.php:941
[0x00007f7e475b99f8] request() /srv/users/serverpilot/apps/example/public/wp-content/plugins/wp-piwik/classes/WP_Piwik.php:1098
[0x00007f7e475b98b8] updateTrackingCode() /srv/users/serverpilot/apps/example/public/wp-content/plugins/wp-piwik/classes/WP_Piwik/TrackingCode.php:16
[0x00007f7e475b9720] __construct() /srv/users/serverpilot/apps/example/public/wp-content/plugins/wp-piwik/classes/WP_Piwik.php:288
[0x00007fffa8cdcac0] addJavascriptCode() unknown:0
[0x00007f7e475b9540] call_user_func_array() /srv/users/serverpilot/apps/example/public/wp-includes/plugin.php:503
[0x00007f7e475b9428] do_action() /srv/users/serverpilot/apps/example/public/wp-includes/general-template.php:2284
[0x00007f7e475b9358] wp_footer() /srv/users/serverpilot/apps/example/public/wp-content/themes/k2/footer.php:17
[0x00007f7e475b9358] +++ dump failed

The first two lines tell you the time of the slow request and which script was initially requested. In the above example, these first two lines are:

[06-Oct-2015 12:20:34]  [pool example] pid 1852
script_filename = /srv/users/serverpilot/apps/example/public/index.php

The third line (the line immediately below the first two lines) is the top of the stack trace. This tells you the currently executing function at the time the script passed five seconds in execution. In the example above, the top of the stack trace is:

[0x00007f7e475ba138] curl_exec() /srv/users/serverpilot/apps/example/public/wp-content/plugins/wp-piwik/classes/WP_Piwik/Request/Rest.php:44

The three parts of this line are a unique identifier for the request ([0x00007f7e475ba138]), the name of the executing function (curl_exec()), and the file name and line number of that function (/srv/users/.../Request/Rest.php:44).

The top of the stack trace is generally the cause of your script’s slowness. The rest of the lines below that tell you each function call that led up to that point.

Note that you will sometimes see +++ dump failed at the bottom of a stack trace. That indicates the end of the stack trace and does not indicate a problem.

If you see a WordPress plugin name in the slow request log, you may be able to resolve your app’s slow request problems by disabling the plugin.

Common Causes of Slow Requests

There are a few common causes of slow requests.

Slow Requests Due to curl_exec()

If you see the curl_exec() function in your PHP slow request log, the problem is not a bug in cURL but rather that the request is taking a long time to complete.

When your scripts rely on making requests to external services and other websites, your own site can become slow and unreliable due to problems with the other website. For example, if their website is slow or is rate-limiting your requests, your own app will become very slow. These issues can be resolved by disabling plugins that make external requests and not writing your own PHP code to perform HTTP requests to external websites or services.

Slow Requests Due to mysqli_query()

If you see the mysqli_query() function in your PHP slow request log, the problem is not a bug in MySQL or any MySQL settings but rather that your PHP code is executing slow SQL queries.

You can have slow SQL queries due to, for example, lack of proper indexes in your database tables or PHP code that is inefficiently using the databases. The solution in this case is to track down the particular queries that are slow and fix them.

Acceptable Cases of Slow Requests

There is one acceptable case of slow requests where you can safely ignore entries in the PHP slow request log: slow requests that are not being performed when answering a website visitor’s request but rather are scheduled requests being performed in the background. Common example of such requests are requests for wp-cron.php or any requests you may have scheduled using cron.

Use PHP composer to clone git repo

DO YOU HAVE A REPOSITORY?

Git, Mercurial, SVN is supported by Composer.

DO YOU HAVE WRITE ACCESS TO THE REPOSITORY?

Yes?

DOES THE REPOSITORY HAVE A composer.json FILE

If you have a repository you can write to: Add a composer.json file, or fix the existing one, and DON’T use the solution below.

Go to @igorw ‘s answer

ONLY USE THIS IF YOU DON’T HAVE A REPOSITORY
OR IF THE REPOSITORY DOES NOT HAVE A composer.json AND YOU CANNOT ADD IT

This will override everything that Composer may be able to read from the original repository’s composer.json, including the dependencies of the package and the autoloading.

Using the package type will transfer the burden of correctly defining everything onto you. The easier way is to have a composer.json file in the repository, and just use it.

This solution really only is for the rare cases where you have an abandoned ZIP download that you cannot alter, or a repository you can only read, but it isn’t maintained anymore.

"repositories": [
    {
        "type":"package",
        "package": {
          "name": "l3pp4rd/doctrine-extensions",
          "version":"master",
          "source": {
              "url": "https://github.com/l3pp4rd/DoctrineExtensions.git",
              "type": "git",
              "reference":"master"
            }
        }
    }
],
"require": {
    "l3pp4rd/doctrine-extensions": "master"
}

ThinkPHP的CBD模式

thinkphp使用核心c(core)+行为b(behavior)+驱动d(driver)三种方式架构。

Core(核心)

ThinkPHP的核心部分包括核心函数库、惯例配置、核心类库(包括基础类和内置驱动及核心行为)

Driver(驱动)

//包含以下文件
ThinkPHP/Library/Think/Cache/Driver // 缓存驱动类库
ThinkPHP/Library/Think/Db/Driver // 数据库驱动类库
ThinkPHP/Library/Think/Log/Driver // 日志记录驱动类库
ThinkPHP/Library/Think/Session/Driver // Session驱动类库
ThinkPHP/Library/Think/Storage/Driver // 存储驱动类库
ThinkPHP/Library/Think/Template/Driver // 第三方模板引擎驱动类库
ThinkPHP/Library/Think/Template/TagLib // 内置模板引擎标签库扩展类库
Behavior(行为)

什么是行为,行为是应用的执行过程中的一个动作一个处理。有些行为具有位置共性。
行为发生的位置可以称为标签或钩子。

系统核心的一些标签

app_init 应用初始化标签位
module_check 模块检测标签位(**3.2.1版本新增**)
path_info PATH_INFO检测标签位
app_begin 应用开始标签位
action_name 操作方法名标签位
action_begin 控制器开始标签位
view_begin 视图输出开始标签位
view_template 视图模板解析标签位
view_parse 视图解析标签位
template_filter 模板解析过滤标签位
view_filter 视图输出过滤标签位
view_end 视图输出结束标签位
action_end 控制器结束标签位
app_end 应用结束标签位

自定义标签

// 添加my_tag 标签侦听
tag('my_tag'); 
// 下面的写法作用一致
ThinkHook::listen('my_tag');

tag函数用于设置某个标签位,可以传入并且只接受一个参数,如果需要传入多个参数,请使用数组,

tag('my_tag',$params); // 添加my_tag 标签侦听

该参数为引用传值,所以只能传入变量,因此下面的传值是错误的:

tag('my_tag','param'); // 添加my_tag 标签侦听行为的定义

行为定义
自定义的扩展行为可以放在核心或者应用目录,只要遵循命名空间的定义规则即可。
行为类的命名采用:行为名称(驼峰法,首字母大写)+Behavior 行为类的定义方式如下:

namespace HomeBehavior;
//**3.2.1版本**开始,行为类的定义无需继承ThinkBehavior类
use ThinkBehavior; 
//**3.2.1版本**开始,行为类的定义无需继承ThinkBehavior类
class TestBehavior extends Behavior {
 //行为扩展的执行入口必须是run
//run方法的参数只允许一个,但可以传入数组。
 public function run(&$params){
 if(C('TEST_PARAM')) {
     echo 'RUNTEST BEHAVIOR '.$params;}
 }
}

行为绑定

行为定义完成后,就需要绑定到某个标签位置才能生效,否则是不会执行的。
我们需要在应用的行为定义文件tags.php
文件中进行行为和标签的位置定义,格式如下:

return array(
 '标签名称1'=>array('行为名1','行为名2',...), 
 '标签名称2'=>array('行为名1','行为名2',...), 
 );

单独执行

行为的调用不一定要放到标签才能调用,如果需要的话,我们可以在控制器中或者其他地方直接调用行为。例如,我们可以把用户权限检测封装成一个行为类,例如:

namespace HomeBehavior;
use ThinkBehavior;
class AuthCheckBehavior extends Behavior {
 // 行为扩展的执行入口必须是run
 public function run(&$return){
 if(C('USER_AUTH_ON')) {
 // 进行权限认证逻辑 如果认证通过 $return = true;
 // 否则用halt输出错误信息
 }
 }
}

定义了AuthCheck行为后,我们可以在控制器的_initialize方法中直接用下面的方式调用:

B('HomeBehaviorAuthCheck');

thinkphp 的 Action 控制器中的系统常量

THINK_PATH // ThinkPHP系统目录

APP_PATH // 当前项目目录

APP_NAME // 当前项目名称

CONTROLLER_NAME // 当前控制器名称

MODULE_NAME //当前模块名称

ACTION_NAME // 当前操作名称

TMPL_PATH // 项目模版目录

LIB_PATH // 项目类库目录

CACHE_PATH // 项目模版缓存目录

CONFIG_PATH //项目配置文件目录

LOG_PATH // 项目日志文件目录

LANG_PATH // 项目语言文件目录

TEMP_PATH //项目临时文件目录

PLUGIN_PATH // 项目插件文件目录

VENDOR_PATH // 第三方类库目录

DATA_PATH // 项目数据文件目录

IS_Apache // 是否属于 Apache

IS_IIS //是否属于 IIS

IS_WIN //是否属于Windows 环境

IS_Linux //是否属于 Linux 环境

IS_FREEBSD //是否属于 FreeBsd 环境

NOW_TIME // 当前时间戳

MEMORY_LIMIT_ON // 是否有内存使用限制

OUTPUT_GZIP_ON // 是否开启输出压缩

MAGIC_QUOTES_GPC // MAGIC_QUOTES_GPC

THINK_VERSION //ThinkPHP 版本号

LANG_SET // 浏览器语言

TEMPLATE_NAME //当前模版名称

TEMPLATE_PATH //当前模版路径

__ROOT__ // 网站根目录地址

__APP__ // 当前项目(入口文件)地址

__URL__ // 当前模块地址

__ACTION__ // 当前操作地址

__SELF__ // 当前 URL 地址

__PUBLIC__ // 网站公共目录

TMPL_FILE_NAME //当前操作的默认模版名(含路径)

WEB_PUBLIC_URL //网站公共目录

APP_PUBLIC_URL //项目公共模版目录模板中使用的系统常量

CONTROLLER_NAME // 当前控制器名称

ThinkPHP框架开发的应用的标准执行流程

  1. 用户URL请求
  2. 调用应用入口文件(通常是网站的index.php)
  3. 载入框架入口文件(ThinkPHP.php)
  4. 记录初始运行时间和内存开销
  5. 系统常量判断及定义
  6. 载入框架引导类(ThinkThink)并执行Think::start方法进行应用初始化
  7. 设置错误处理机制和自动加载机制
  8. 调用ThinkStorage类进行存储初始化(由STORAGE_TYPE常量定义存储类型)
  9. 部署模式下如果存在应用编译缓存文件则直接加载(直接跳转到步骤22)
  10. 读取应用模式(由APP_MODE常量定义)的定义文件(以下以普通模式为例说明)
  11. 加载当前应用模式定义的核心文件(普通模式是 ThinkPHP/Mode/common.php)
  12. 加载惯例配置文件(普通模式是 ThinkPHP/Conf/convention.php)
  13. 加载应用配置文件(普通模式是 Application/Common/Conf/config.php)
  14. 加载系统别名定义
  15. 判断并读取应用别名定义文件(普通模式是 Application/Common/Conf/alias.php)
  16. 加载系统行为定义
  17. 判断并读取应用行为定义文件(普通模式是 Application/Common/Conf/tags.php)
  18. 加载框架底层语言包(普通模式是 ThinkPHP/Lang/zh-cn.php)
  19. 如果是部署模式则生成应用编译缓存文件
  20. 加载调试模式系统配置文件(ThinkPHP/Conf/debug.php)
  21. 判断并读取应用的调试配置文件(默认是 Application/Common/Conf/debug.php)
  22. 判断应用状态并读取状态配置文件(如果APP_STATUS常量定义不为空的话)
  23. 检测应用目录结构并自动生成(如果CHECK_APP_DIR配置开启并且RUNTIME_PATH目录不存在的情况下)
  24. 调用ThinkApp类的run方法启动应用
  25. 应用初始化(app_init)标签位侦听并执行绑定行为
  26. 判断并加载动态配置和函数文件
  27. 调用ThinkDispatcher::dispatch方法进行URL请求调度
  28. 自动识别兼容URL模式和命令行模式下面的$_SERVER[‘PATH_INFO’]参数
  29. 检测域名部署以及完成模块和控制器的绑定操作(APP_SUB_DOMAIN_DEPLOY参数开启)
  30. 分析URL地址中的PATH_INFO信息
  31. 获取请求的模块信息
  32. 检测模块是否存在和允许访问
  33. 判断并加载模块配置文件、别名定义、行为定义及函数文件
  34. 判断并加载模块的动态配置和函数文件
  35. 模块的URL模式判断
  36. 模块的路由检测(URL_ROUTER_ON开启)
  37. PATH_INFO处理(path_info)标签位侦听并执行绑定行为
  38. URL后缀检测(URL_DENY_SUFFIX以及URL_HTML_SUFFIX处理)
  39. 获取当前控制器和操作,以及URL其他参数
  40. URL请求调度完成(url_dispatch)标签位侦听并执行绑定行为
  41. 应用开始(app_begin)标签位侦听并执行绑定行为
  42. 调用SESSION_OPTIONS配置参数进行Session初始化(如果不是命令行模式)
  43. 根据请求执行控制器方法
  44. 如果控制器不存在则检测空控制器是否存在
  45. 控制器开始(action_begin)标签位侦听并执行绑定行为
  46. 默认调用系统的ReadHtmlCache行为读取静态缓存(HTML_CACHE_ON参数开启)
  47. 判断并调用控制器的_initialize初始化方法
  48. 判断操作方法是否存在,如果不存在则检测是否定义空操作方法
  49. 判断前置操作方法是否定义,有的话执行
  50. Action参数绑定检测,自动匹配操作方法的参数
  51. 如果有模版渲染(调用控制器display方法)
  52. 视图开始(view_begin)标签位侦听并执行绑定行为
  53. 调用ThinkView的fetch方法解析并获取模版内容
  54. 自动识别当前主题以及定位模版文件
  55. 视图解析(view_parse)标签位侦听并执行绑定行为
  56. 默认调用内置ParseTemplate行为解析模版(普通模式下面)
  57. 模版引擎解析模版内容后生成模版缓存
  58. 模版过滤替换(template_filter)标签位侦听并执行绑定行为
  59. 默认调用系统的ContentReplace行为进行模版替换
  60. 输出内容过滤(view_filter)标签位侦听并执行绑定行为
  61. 默认调用系统的WriteHtmlCache行为写入静态缓存(HTML_CACHE_ON参数开启)
  62. 调用ThinkView类的render方法输出渲染内容
  63. 视图结束(view_end)标签位侦听并执行绑定行为
  64. 判断后置操作方法是否定义,有的话执行
  65. 控制器结束(action_end)标签位侦听并执行绑定行为
  66. 应用结束(app_end)标签位侦听并执行绑定行为
  67. 执行系统的ShowPageTrace行为(SHOW_PAGE_TRACE参数开启并且不是AJAX请求)
  68. 日志信息存储写入

红包算法

JAVA

public class SplitRedPacket {

	public static void main(String[] args) {
		LeftMoneyPackage leftMoneyPackage = new LeftMoneyPackage();
		leftMoneyPackage.remainMoney = 10;
		leftMoneyPackage.remainSize = 8;
		int i = 1;
		while (leftMoneyPackage.remainSize > 0) {
			double money = getRandomMoney(leftMoneyPackage);
			System.out.println(i + " : " + money);
			i++;
		}
	}

	public static double getRandomMoney(LeftMoneyPackage _leftMoneyPackage) {
		// remainSize 剩余的红包数量
		// remainMoney 剩余的钱
		if (_leftMoneyPackage.remainSize == 1) {
			_leftMoneyPackage.remainSize--;
			return (double) Math.round(_leftMoneyPackage.remainMoney * 100) / 100;
		}
		Random r = new Random();
		double min = 0.01; //最小金额
		double max = _leftMoneyPackage.remainMoney / _leftMoneyPackage.remainSize * 2;
		double money = r.nextDouble() * max;
		money = money <= min ? min : money;
		money = Math.floor(money * 100) / 100;
		_leftMoneyPackage.remainSize--;
		_leftMoneyPackage.remainMoney -= money;
		return money;
	}

}

class LeftMoneyPackage {
	public int remainSize;
	public double remainMoney;

	public static class BuildLeftMoneyPackage {
		
	}

}

PHP

$total=10;//红包总额
$num=8;// 分成8个红包,支持8人随机领取
$min=0.01;//每个人最少能收到0.01元

for ($i=1;$i<$num;$i++)
{
    $safe_total=($total-($num-$i)*$min)/($num-$i);//随机安全上限
    $money=mt_rand($min*100,$safe_total*100)/100;
    $total=$total-$money;
    echo '第'.$i.'个红包:'.$money.' 元,余额:'.$total.' 元 
';
}
echo '第'.$num.'个红包:'.$total.' 元,余额:0 元';