Как Redis гарантирует идемпотентность интерфейсов?

Redis

В недавнем обновлении бизнеса мы столкнулись с такой проблемой.Мы разработали новую систему учетных записей.После того, как пользователь обновляет приложение, данные исходной учетной записи необходимо синхронизировать вручную, то есть пользователю необходимо активировать кнопку синхронизации, чтобы синхронизировать, потому что некоторые данные хранятся локально пользователем. Тогда в этом процессе есть проблема.Если это из-за проблем с сетью,Что делать, если пользователь нажимает кнопку несколько раз?Даже если мы выполним некоторую обработку на стороне клиента, мы не сможем снова щелкнуть в процессе синхронизации, но после моей недавней практики с поисковым роботом все еще небезопасно, если кто-то другой перехватит наш интерфейс.

Исходя из такого бизнес-сценария, я используюМетод блокировки Redis запрещает пользователю делать второй запрос при запросе.




После ввода запроса мы сначала пытаемся получить объект блокировки, затем ключ объекта блокировки является фактически идентификатором пользователя.Если получение прошло успешно, мы делаем вывод, что пользователь синхронизировал данные.Если они были синхронизированы, это может вернуться непосредственно, чтобы сообщить пользователю, что он был синхронизирован.Не так просто выполнить бизнес-логику синхронизации данных и, наконец, снять блокировку.Если блокировку не удалось получить после входа в метод, возможно, что сначала запрос не закончился, а потом инициируется запрос, то это время на получение Если блокировки нет, то не будет, чтобы синхронизация данных происходила несколько раз.


великолепная разделительная линия


Поэтому с этим требованием мы будем использовать Redis для реализации следующего кода. Прежде всего, нам нужно знать, что нам предстоит ввести метод Redis.

Затем мы хотим использовать Redis в качестве единственного объекта блокировки пользователя, тогда он должен быть уникальным в Redis и не должен быть перезаписан, этот метод вернет true после успешного сохранения, если элемент уже существует в экземпляре Redis, затем вернет false напрямую

setIfAbsent(key,value)

Но посередине есть еще одна проблема: если наш сервис зависнет после получения объекта блокировки, то другие запросы точно не смогут получить блокировку в это время, исходя из этой ситуации, мы также должны добавить срок действия этого элемента. время, чтобы предотвратить зависание нашего сервиса послетупикПроблема.

/**
 * 添加元素
 *
 * @param key
 * @param value
 */
public void set(Object key, Object value) {

    if (key == null || value == null) {
        return;
    }
    redisTemplate.opsForValue().set(key, value.toString());
}

/**
 * 如果已经存在返回false,否则返回true
 *
 * @param key
 * @param value
 * @return
 */
public Boolean setNx(Object key, Object value, Long expireTime, TimeUnit mimeUnit) {

    if (key == null || value == null) {
        return false;
    }
    return redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, mimeUnit);
}

/**
 * 获取数据
 *
 * @param key
 * @return
 */
public Object get(Object key) {

    if (key == null) {
        return null;
    }
    return redisTemplate.opsForValue().get(key);
}

/**
 * 删除
 *
 * @param key
 * @return
 */
public Boolean remove(Object key) {

    if (key == null) {
        return false;
    }

    return redisTemplate.delete(key);
}

/**
 * 加锁
 *
 * @param key 
 * @param waitTime 等待时间
 * @param expireTime 过期时间
 */
public Boolean lock(String key, Long waitTime, Long expireTime) {

    String value = UUID.randomUUID().toString().replaceAll("-", "").toLowerCase();

    Boolean flag = setNx(key, value, expireTime, TimeUnit.MILLISECONDS);

    // 尝试获取锁 成功返回
    if (flag) {
        return flag;
    } else {
        // 获取失败

        // 现在时间
        long newTime = System.currentTimeMillis();

        // 等待过期时间
        long loseTime = newTime + waitTime;

        // 不断尝试获取锁成功返回
        while (System.currentTimeMillis() < loseTime) {

            Boolean testFlag = setNx(key, value, expireTime, TimeUnit.MILLISECONDS);
            if (testFlag) {
                return testFlag;
            }

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    return false;
}

/**
 * 释放锁
 *
 * @param key
 * @return
 */
public Boolean unLock(Object key) {
    return remove(key);
}

Вся логика нашего кода блокировки написана. Давайте проанализируем ее. После входа пользователя он сначала вызывает lock, чтобы попытаться получить блокировку и выполнить блокировку. Метод lock() имеет три параметра: key, waitTime, если пользователь получает блокировку. Как долго вы можете ждать, пока блокировка не будет достигнута. По истечении этого времени вы больше не будете ждать. Последний параметр указывает, как долго будет действовать блокировка после ее истечения, предотвращая возникновение взаимоблокировок после зависания службы.

После ввода блокировки () сначала выполните операцию блокировки. Если блокировка прошла успешно, верните true, а затем выполните нашу последующую бизнес-логику. Если получение блокировки не удается, будет получено текущее время плюс установленное время истечения срока действия, что является совпадает с текущим временем. Сравните, если оно все еще находится в пределах времени ожидания, попробуйте снова получить блокировку, пока время ожидания не пройдет.


Уведомление: При установке значения мы устанавливаем время истечения срока действия, чтобы предотвратить взаимоблокировку. Вы должны обратить внимание на то, чтобы не ждать, пока установка будет успешной, прежде чем устанавливать время истечения срока действия для элемента, поскольку этот процесс не является атомарной операцией. После того, как вы установите его успешно, до того, как время истечения срока действия будет установлено успешно, служба зависнет напрямую, тогда это произойдет в это времятупикпроблема, поэтому каждый должен убедиться, что хранение элементов и установка времени истечения срока действия должны быть атомарными операциями.

Наконец, давайте напишем тестовый класс для проверки

@Test
public void test01() {

    String key = "uid:12011";

    Boolean flag = redisUtil.lock(key, 10L, 1000L * 60);

    if (!flag) {

        // 获取锁失败
        System.err.println("获取锁失败");
    } else {

        // 获取锁成功
        System.out.println("获取锁成功");
    }

    // 释放锁
    redisUtil.unLock(key);
}