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, что приведет к перепроданности.