Рубрики
Uncategorized

PHP и redis разрабатывают действия по второму убийству

Автор оригинала: David Wong.

1 Примечание

Некоторое время назад во время интервью меня спросили, как разработать вторую программу убийства, но у меня не было практического опыта в этой области, поэтому мне пришлось полагаться на свое понимание и некоторую информацию для разработки такой программы. Строка и набор в основном используются в redis . Строка в основном использует свою структуру K-V для обработки запасов. Он также может использовать структуру данных списка для обработки инвентаризации товаров. Набор используется для обеспечения повторной отправки пользователями. Среди них наша главная задача-решить. – Предотвращать и перепродавать

2 проектирование технологического процесса

3 код

3.1 Код Сервера

class MiaoSha{

    Const MSG_REPEAT_USER='Do not repeat participation';
    Const MSG_EMPTY_STOCK='insufficient stock';
    Const MSG_KEY_NOT_EXIST='key does not exist';

    const IP_POOL = 'ip_pool';
    const USER_POOL = 'user_pool';

    /** @var Redis  */
    public $redis;
    public $key;

    public function __construct($key = '')
    {
        $this->checkKey($key);
        $this - > redis = new Redis ();//todo connection pool
        $this->redis->connect('127.0.0.1');
    }

    public function checkKey($key = '')
    {
        if(!$key) {
            throw new Exception(self::MSG_KEY_NOT_EXIST);
        } else {
            $this->key = $key;
        }
    }

    public function setStock($value = 0)
    {
        if($this->redis->exists($this->key) == 0) {
            $this->redis->set($this->key,$value);
        }
    }

    public function checkIp($ip = 0)
    {
        $sKey = $this->key . self::IP_POOL;
        if(!$ip || $this->redis->sIsMember($sKey,$ip)) {
            throw new Exception(self::MSG_REPEAT_USER);
        }
    }

    public function checkUser($user = 0)
    {
        $sKey = $this->key . self::USER_POOL;
        if(!$user || $this->redis->sIsMember($sKey,$user)) {
            throw new Exception(self::MSG_REPEAT_USER);
        }
    }

    public function checkStock($user = 0, $ip = 0)
    {
        $num = $this->redis->decr($this->key);
        if($num < 0 ) {
            throw new Exception(self::MSG_EMPTY_STOCK);
        } else {
            $this->redis->sAdd($this->key . self::USER_POOL, $user);
            $this->redis->sAdd($this->key . self::IP_POOL, $ip);
            //todo add to mysql
            echo 'success' . PHP_EOL;
            error_log('success' . $user . PHP_EOL,3,'/var/www/html/demo/log/debug.log');
        }
    }

    /**
     *@ Note: This approach does not prevent concurrency
     * @func checkStockFail
     * @param int $user
     * @param int $ip
     * @throws Exception
     */
    public function checkStockFail($user = 0,$ip = 0) {
        $num = $this->redis->get($this->key);
        if($num > 0 ){
            $this->redis->sAdd($this->key . self::USER_POOL, $user);
            $this->redis->sAdd($this->key . self::IP_POOL, $ip);
            //todo add to mysql
            echo 'success' . PHP_EOL;
            error_log('success' . $user . PHP_EOL,3,'/var/www/html/demo/log/debug.log');
            $num--;
            $this->redis->set($this->key,$num);
        } else {
            throw new Exception(self::MSG_EMPTY_STOCK);
        }
    }
}

3.2 тестовый код клиента

function test()
{
    try{
        $key = 'cup_';
        $handler = new MiaoSha($key);
        $handler->setStock(10);
        $user = rand(1,10000);
        $ip = $user;
        $handler->checkIp($ip);
        $handler->checkUser($user);
        $handler->checkStock($user,$ip);
    } catch (\Exception $e) {
        echo $e->getMessage() . PHP_EOL;
        error_log('fail' . $e->getMessage() .PHP_EOL,3,'/var/www/html/demo/log/debug.log');
    }
}

function test2()
{
    try{
        $key = 'cup_';
        $handler = new MiaoSha($key);
        $handler->setStock(10);
        $user = rand(1,10000);
        $ip = $user;
        $handler->checkIp($ip);
        $handler->checkUser($user);
        $handler - > checkStockFail ($user, $ip); // Can't prevent concurrency
    } catch (\Exception $e) {
        echo $e->getMessage() . PHP_EOL;
        error_log('fail' . $e->getMessage() .PHP_EOL,3,'/var/www/html/demo/log/debug.log');
    }
}

4 Испытания

Описание тестовой среды

  • Описание тестовой среды
  • redis2.8.4
  • php 5.5

В коде на стороне сервера у нас есть две функции, checkStock и checkStockFail, в которых checkStockFail не может работать очень плохо при высоком параллелизме и не может завершать операции, когда запасы равны нулю на уровне redis. Мы используем инструмент AB для тестирования среди www.hello.com Это имя настроенного виртуального хоста flash-sale.php Имя вашего скрипта

# In case 1, 500 concurrently executes with the client's test2 ().
 ab -n 500 -c 100 www.hello.com/flash-sale.php

Результаты регистрации в журнале:

# In the second case, 5000 concurrently executes with test2 () of the client
 ab -n 5000 -c 1000 www.hello.com/flash-sale.php

Результаты регистрации в журнале:

# In the third case, 500 concurrently executes with the client's test ().
 ab -n 500 -c 100 www.hello.com/flash-sale.php

Результаты регистрации в журнале:

# In the fourth case, 5000 is executed concurrently with the client's test ().
 ab -n 5000 -c 1000 www.hello.com/flash-sale.php

Результаты регистрации в журнале:

5 Резюме

Из журнала мы ясно видим, что в случаях 3 и 4 мы можем гарантировать, что количество товаров всегда составляет 10 от установленной нами стоимости запасов, но в случаях 1 и 2 наблюдается явление перепроданности. Redis контролирует параллелизм в основном за счет использования того преимущества, что его API является атомарной операцией. Из checkStock и checkStockFail мы видим, что один из них-прямое decr для уменьшения запасов на одну операцию, поэтому параллелизма нет, но другой заключается в том, чтобы сначала извлечь стоимость запасов, вычесть ее на одну операцию, а затем повторно назначить ее. В параллельном сценарии несколько процессов будут считывать несколько значений запасов, равное 1, что приведет к перепроданности.