Боевая серия Java Spike System ~ Логика шипа оптимизации атомарной операции на основе Redis

Redis

Резюме:

Этот пост в блоге является четырнадцатым в «Серии реальных боевых действий системы Java Second Kill». система второго убийства». Решите такие проблемы, как «перепроданность запасов» и «повторяющиеся всплески».

содержание:

Что касается промежуточного программного обеспечения кэширования Redis, я полагаю, что все вы более или менее слышали об этом или даже боролись с этим на практике.В этой статье мы интегрируем промежуточное программное обеспечение Redis на основе SpringBoot и реализуем «Распределенную блокировку», а затем контролируем «многопоточный доступ к общим ресурсам с высокой степенью параллелизма» и, наконец, решить «безопасность параллелизма», то есть проблему «перепроданного инвентаря» или «повторяющихся всплесков»!

(1) По соглашению сначала нам нужно добавить сторонние зависимости Redis следующим образом:

<!-- redis -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-redis</artifactId>
    <version>1.3.5.RELEASE</version>
</dependency>

Затем вам нужно добавить хост, сообщение порта, пароль ключа ссылки и другую информацию, где служба Redis находится в файле конфигурации application.properties, как показано ниже:

(2) Затем нам также необходимо настроить конфигурацию Bean, связанную с операционными компонентами инъекции Redis.Здесь мы в основном настраиваем компоненты операции RedisTemplate и StringRedisTemplate конфигурации инъекции и указываем стратегию сериализации их соответствующих ключей и значений:

// redis的通用化配置
@Configuration
public class RedisConfig {
    @Autowired
    private RedisConnectionFactory redisConnectionFactory;

    @Bean
    public RedisTemplate<String,Object> redisTemplate(){
        RedisTemplate<String,Object> redisTemplate=new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        //TODO:指定Key、Value的序列化策略
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());

        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        return redisTemplate;
    }

    @Bean
    public StringRedisTemplate stringRedisTemplate(){
        StringRedisTemplate stringRedisTemplate=new StringRedisTemplate();
        stringRedisTemplate.setConnectionFactory(redisConnectionFactory);
        return stringRedisTemplate;
    }
}

(3) На данный момент можно сказать, что были сделаны достаточные приготовления, и тогда мы можем использовать это! Чтобы отличить предыдущие логические методы seckill, мы открыли новый логический метод seckill killItemV3 и приняли методы атомарной операции SETNX и EXPIRE Redis для реализации «распределенной блокировки», тем самым контролируя доступ многопоточных потоков с высокой общие ресурсы, а его полный исходный код выглядит следующим образом:

//商品秒杀核心业务逻辑的处理-redis的分布式锁
@Override
public Boolean killItemV3(Integer killId, Integer userId) throws Exception {
    Boolean result=false;

    if (itemKillSuccessMapper.countByKillUserId(killId,userId) <= 0){

        //TODO:借助Redis的原子操作实现分布式锁-对共享操作-资源进行控制
        ValueOperations valueOperations=stringRedisTemplate.opsForValue();
        final String key=new StringBuffer().append(killId).append(userId).append("-RedisLock").toString();
        final String value=RandomUtil.generateOrderCode();
        Boolean cacheRes=valueOperations.setIfAbsent(key,value); 
        if (cacheRes){
            stringRedisTemplate.expire(key,30, TimeUnit.SECONDS);

            try {
                ItemKill itemKill=itemKillMapper.selectByIdV2(killId);
                if (itemKill!=null && 1==itemKill.getCanKill() && itemKill.getTotal()>0){
                    int res=itemKillMapper.updateKillItemV2(killId);
                    if (res>0){
                        commonRecordKillSuccessInfo(itemKill,userId);

                        result=true;
                    }
                }
            }catch (Exception e){
                throw new Exception("还没到抢购日期、已过了抢购时间或已被抢购完毕!");
            }finally {
                if (value.equals(valueOperations.get(key).toString())){
                    stringRedisTemplate.delete(key);
                }
            }
        }
    }else{
        throw new Exception("Redis-您已经抢购过该商品了!");
    }
    return result;
}

В приведенном выше коде мы в основном реализуем функцию «распределенной блокировки» посредством следующих операций, в том числе

(1) valueOperations.setIfAbsent(key, value);: Указывает, что если текущий ключ не существует в кеше, значение будет установлено успешно.Наоборот, если ключ уже существует в кеше, значение будет поставил неудачно! С помощью этой функции мы можем комбинировать «однозначное соответствие KillId и UserId ~ то есть один человек может получить только один продукт» в качестве ключа!

(2) Если ключ установлен, то его нужно выпустить в определенный момент времени, то есть stringRedisTemplate.expire(key, 30, TimeUnit.SECONDS); операция может помочь в реализации!

(3) Однако реальная операция «освобождения блокировки» реализуется следующим кодом, то есть для определения того, является ли текущая освобождаемая блокировка блокировкой, полученной в начале, и если да, то освободить ее! Это может быть хорошим способом избежать проблемы случайного удаления блокировок!

if (value.equals(valueOperations.get(key).toString())){
    stringRedisTemplate.delete(key);
}

До сих пор распределенные блокировки, основанные на атомарных операциях Redis, использовались для управления доступом к общим ресурсам с высокой степенью параллелизма и многопоточности, чтобы решить проблемы «перепроданного инвентаря» и «повторяющихся всплесков» в сценариях всплесков. для стресс-тестирования ниже.Список пользователей стресс-теста и «общее количество мгновенных убийств» продукта такие же, как и в предыдущей главе, то есть всего=6 книг, и всего 10 пользователей.

Нажмите кнопку запуска JMeter, наблюдайте за выходной информацией консоли и таблицей базы данных item_kill и item_kill_success, вы можете видеть, что результат записи всплеска очень удовлетворительный:


То есть товар с 6 книгами в наличии ~ книгу просто убивают 6 из 10 пользователей! На самом деле, этот результат определенно является счастливым концом для нас и для пользователей!

Хотя актеры остались очень довольны своей концовкой, режиссер заметил, что в спектакле все же есть некоторые огрехи! То есть, если узел Redis выходит из строя после выполнения атомарной операции Redis SetNX и до выполнения Expire, то очень вероятно, что он навсегда попадет в дилемму «Блокировки ключа», то есть после перезапуска, потому что предыдущего ключа не существует. Он выпущен, поэтому этот ключ всегда будет существовать в кеше, то есть соответствующий пользователь не сможет убить продукт за секунды!Это действительно скрытая опасность!

Так как есть скрытые опасности, то мы должны найти способ решить их! Не волнуйтесь, мы продолжим в следующей главе!

Пополнить:

1. В настоящее время общая конструкция и кодирование этой системы seckill завершены.Полный адрес базы данных исходного кода можно скачать здесь:git ee.com/steady jack/… Помните Вилка и Звезда!!

2. Наконец, не забудьте обратить внимание на технический паблик Debug в WeChat: