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