4 решения для Spring Boot для реализации идемпотентности интерфейса! Кто еще не знает?

Spring Boot

публика:Маленькое кофейное шоу Java,Веб-сайт:javaxks.com

Автор: Super Xiaodouding, ссылка:mydlq.club/article/94/

1. Что такое идемпотентность

Идемпотентность — это понятие в математике и информатике.Когда некоторая операция с элементом является идемпотентной в математике, ее влияние на любой элемент дважды будет таким же, как результат ее воздействия один раз.

[В компьютерном программировании характеристика идемпотентной операции заключается в том, что любое количество ее выполнений имеет тот же эффект, что и одно выполнение. Идемпотентная функция или идемпотентный метод — это функция, которая может многократно выполняться с одними и теми же параметрами и достигать одного и того же результата. Эти функции не влияют на состояние системы, и не нужно беспокоиться, что повторное выполнение вызовет изменения в системе. ]

2. Что такое идемпотентность интерфейса

В HTTP/1.1 была определена идемпотентность. Он описывает, что один и несколько запросов к ресурсу должны иметь одинаковый результат для самого ресурса (за исключением таких проблем, как тайм-ауты сети), то есть первый запрос имеет побочные эффекты для ресурса, но последующие запросы не будут иметь побочных эффектов. на ресурсы.

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

3. Зачем нужно реализовывать идемпотентность

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

  • Внешний интерфейс повторно отправляет форму:При заполнении некоторых форм пользователь заполняет и завершает отправку. Много раз из-за колебаний сети пользователь не отвечает вовремя на отправку, заставляя пользователя думать, что отправка не была успешной, а затем продолжайте нажимать кнопку отправки, в это время происходит повторная отправка формы запроса.
  • Пользователи злонамеренно смахивают заказы:Например, при реализации функции пользовательского голосования, если пользователь неоднократно отправляет голос за пользователя, это приведет к тому, что интерфейс будет получать информацию о голосовании, неоднократно отправленную пользователем, что сделает результат голосования серьезно не соответствующим действительности.
  • Время ожидания интерфейса истекает, и он повторно отправляет:Во многих случаях клиентские инструменты HTTP по умолчанию включают механизм повтора тайм-аута, особенно когда третья сторона вызывает интерфейс, чтобы предотвратить сбой запроса, вызванный колебаниями сети и тайм-аутом, будет добавлен механизм повтора, в результате чего запрос будет подано несколько раз.
  • Сообщение для повторного потребления:При использовании промежуточного программного обеспечения сообщений MQ, если в промежуточном программном обеспечении сообщений возникает ошибка, информация о потреблении не отправляется вовремя, что приводит к повторному потреблению.

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

В-четвертых, влияние на систему после введения идемпотентности

Идемпотентность предназначена для упрощения логической обработки клиента и может выполнять такие операции, как повторные отправки, но увеличивает логическую сложность и стоимость сервера, в основном:

  • Функция параллельного выполнения изменена на последовательное выполнение, что снижает эффективность выполнения.
  • Добавлена ​​дополнительная бизнес-логика для контроля идемпотентности, усложняющая бизнес-функции;

Поэтому необходимо учитывать необходимость введения идемпотентности при его использовании.Согласно конкретному анализу реального бизнес-сценария, помимо особых требований бизнеса, идемпотентность интерфейса, как правило, не требуется.

5. Идемпотентность интерфейса Restful API

Среди нескольких методов интерфейса HTTP, рекомендуемых популярным Restful, есть идемпотентные строки и методы, которые не могут гарантировать идемпотентность, а именно:

  • √ Удовлетворить идемпотенту
  • х не идемпотент
  • - Это может быть или не быть идемпотентом, в зависимости от фактической бизнес-логики
тип метода Это идемпотент описывать
Get Метод Get используется для получения ресурсов. Обычно он не изменяет и не должен вносить изменения в системные ресурсы, поэтому он идемпотентный.
Post × Метод Post обычно используется для создания новых ресурсов. Он добавляет новые данные при каждом выполнении, поэтому он не является идемпотентным.
Put - Метод Put обычно используется для изменения ресурсов. Эта операция определяет, является ли он идемпотентным по ситуации.В операции обновления он напрямую обновляется в соответствии с определенным значением, а также может оставаться идемпотентным. Однако обновления, выполняющие операцию накопления, не являются идемпотентными.
Delete - Метод Delete обычно используется для удаления ресурсов. Эта операция определяется в зависимости от ситуации, чтобы определить, является ли она идемпотентной.При удалении в соответствии с уникальным значением эффект удаления одних и тех же данных несколько раз одинаков. Однако следует отметить, что удаление с условиями запроса не обязательно удовлетворяет идемпотентности. Например, после удаления пакета данных по условию, в это время добавляется новый фрагмент данных, также удовлетворяющий условию, а затем выполняется удаление, в результате чего вновь добавленный фрагмент данных, удовлетворяющий условию также подлежит удалению.

Шестое, как добиться идемпотентности

Вариант 1: Уникальный первичный ключ базы данных

Описание схемы

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

При использовании уникального первичного ключа базы данных для завершения идемпотентности следует отметить, что первичный ключ обычно не использует самоинкрементный первичный ключ в базе данных, а использует в качестве первичного ключа распределенный идентификатор (см. схема проектирования распределенного идентификатора в Java)), чтобы обеспечить глобальную уникальность идентификатора в распределенной среде.

Применимые операции:

  • операция вставки
  • удалить операцию

Ограничения использования:

  • Необходимо сгенерировать глобально уникальный идентификатор первичного ключа;

Основной процесс:

image.png

Основной процесс:

  • ① Клиент выполняет запрос на создание и вызывает интерфейс сервера.
  • ② Сервер выполняет бизнес-логику, генерирует распределенный идентификатор, использует идентификатор в качестве первичного ключа вставляемых данных, а затем выполняет операцию вставки данных и запускает соответствующий оператор SQL.
  • ③ Сервер вставляет часть данных в базу данных, если вставка прошла успешно, это означает, что интерфейс не вызывается повторно. Если выдается дублирующее исключение первичного ключа, это означает, что запись уже существует в базе данных, и клиенту возвращается сообщение об ошибке.

Вариант 2: оптимистическая блокировка базы данных

Описание программы:

[Схема оптимистической блокировки базы данных обычно применима только к процессу выполнения «операции обновления». Мы можем заранее добавить еще одно поле в соответствующую таблицу данных, чтобы оно служило идентификатором версии текущих данных. Таким образом, каждый раз при обновлении данных таблицы в базе данных в качестве условия будет использоваться идентификатор версии, а значением будет значение идентификатора версии в данных, которые будут обновляться в последний раз. ]

Применимые операции:

  • операция обновления

Ограничения использования:

  • В бизнес-таблицу, соответствующую базе данных, необходимо добавить дополнительные поля;

Пример описания:

Например, в следующей таблице данных:

id name price
1 Телефон Сяоми 1000
2 айфон 2500
3 сотовый телефон хуавей 1600

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

id name price version
1 Телефон Сяоми 1000 10
2 айфон 2500 21
3 сотовый телефон хуавей 1600 5

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

UPDATE my_table SET price=price+50,version=version+1 WHERE id=1 AND version=5

После выполнения вышеуказанного WHERE, за которым следует условие id=1 AND version=5, версия id=1 обновляется до 6, поэтому, если оператор SQL выполняется повторно, он не вступит в силу, поскольку данные id= 1 И версия = 5 больше недействительна.Существует, чтобы можно было поддерживать идемпотентность обновления, и множественные обновления не повлияют на результат.

Вариант 3: антитяжелый токен

Описание программы:

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

Применимые операции:

  • операция вставки
  • операция обновления
  • удалить операцию

Ограничения использования:

  • Необходимо сгенерировать глобально уникальную строку токена;
  • Необходимо использовать сторонний компонент Redis для проверки данных;

Основной процесс:

  • ① Сервер предоставляет интерфейс для получения токена, который может быть серийным номером, распределенным идентификатором или строкой UUID.
  • ② Клиент вызывает интерфейс для получения токена, и в это время сервер сгенерирует строку токена.
  • ③ Затем сохраните строку в базе данных Redis и используйте токен в качестве ключа Redis (обратите внимание на установку срока действия).
  • ④ Верните Токен клиенту, после того, как клиент его получит, он должен быть сохранен в скрытом поле формы.
  • ⑤ Когда клиент выполняет форму отправки, он сохраняет токен в заголовках и переносит заголовки при выполнении бизнес-запроса.
  • ⑥ После того, как сервер получает запрос, он получает токен из заголовков, а затем ищет в Redis наличие ключа в соответствии с токеном.
  • ⑦ Сервер определяет, существует ли ключ в Redis, и если он существует, он удаляет ключ, а затем выполняет бизнес-логику в обычном режиме. Генерирует исключение, если оно не существует, и возвращает сообщение об ошибке повторной отправки.

Обратите внимание, что в случае параллелизма выполнение поиска и удаления данных Redis должно обеспечивать атомарность, в противном случае очень вероятно, что идемпотентность не может быть гарантирована при параллелизме. Его метод реализации может использовать распределенные блокировки или использовать выражения Lua для отмены операций запроса и удаления.

Вариант 4. Доставка уникальных серийных номеров вниз по течению

Описание программы:

Так называемый серийный номер запроса на самом деле представляет собой серийный номер, который является уникальным и неповторяющимся в течение короткого периода времени каждый раз, когда делается запрос на сервер.Серийный номер может быть порядковым идентификатором или порядковым номером, который обычно генерируется нижестоящим сервером.Серийный номер и идентификатор, используемые для аутентификации, добавляются к интерфейсу вышестоящего сервера.

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

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

Применимые операции:

  • операция вставки
  • операция обновления
  • удалить операцию

Ограничения использования:

  • потребовать от третьего лица передать уникальный серийный номер;
  • Необходимо использовать сторонний компонент Redis для проверки данных;

Основной процесс:

Основные шаги:

  • ① Нижестоящая служба генерирует распределенный идентификатор в качестве серийного номера, а затем выполняет запрос на вызов вышестоящего интерфейса с «уникальным серийным номером» и запрошенным «идентификатором учетных данных аутентификации».
  • ② Восходящий сервис выполняет проверку безопасности, чтобы определить, существуют ли «серийный номер» и «идентификатор учетных данных» в параметрах, передаваемых нижестоящим.
  • ③ Восходящий сервис обращается к Redis, чтобы определить, существует ли соответствующий ключ, состоящий из «серийного номера» и «идентификатора аутентификации».Если он существует, он выдает информацию об исключении повторного выполнения, а затем отвечает на соответствующую информацию об ошибке нижестоящего уровня. Если он не существует, используйте комбинацию «серийный номер» и «идентификатор аутентификации» в качестве ключа и используйте информацию нисходящего ключа в качестве значения, а затем сохраните ее в Redis, а затем выполните входящую бизнес-логику в обычном режиме.

Срок действия должен быть установлен при вставке данных в Redis на вышеуказанных шагах. Это гарантирует, что в пределах этого временного диапазона при повторном вызове интерфейса могут быть выполнены оценка и идентификация. Если время истечения срока действия не установлено, это может привести к тому, что в Redis будет храниться неограниченный объем данных, что приведет к неправильной работе Redis.

7. Пример реализации идемпотентности интерфейса

Здесь используется схема антиповторного токена, которая может обеспечить идемпотентность при разных действиях запроса.Логику реализации можно посмотреть в написанной выше схеме "антиповторный токен", а затем написать код для реализации этой логики.

1. Maven вводит связанные зависимости

Здесь для управления зависимостями используются инструменты Maven, а зависимости, связанные со SpringBoot, Redis и lombok, представлены в pom.xml.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.4.RELEASE</version>
    </parent>

    <groupId>mydlq.club</groupId>
    <artifactId>springboot-idempotent-token</artifactId>
    <version>0.0.1</version>
    <name>springboot-idempotent-token</name>
    <description>Idempotent Demo</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <!--springboot web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--springboot data redis-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

2. Настройте параметры для подключения к Redis

Настройте параметры подключения к Redis в файле конфигурации приложения. Основы Spring Boot не будут представлены.Для последних учебных пособий рекомендуются следующие учебные пособия.

следующее:

spring:
  redis:
    ssl: false
    host: 127.0.0.1
    port: 6379
    database: 0
    timeout: 1000
    password:
    lettuce:
      pool:
        max-active: 100
        max-wait: -1
        min-idle: 0
        max-idle: 20

3. Создайте и проверьте класс инструментов Token.

Создайте класс службы для работы с токенами, и есть методы создания и проверки токенов, в том числе:

  • Метод создания токена:Используйте инструмент UUID для создания строки токена, установите «idempotent_token:» + «строка токена» в качестве ключа, используйте информацию о пользователе в качестве значения и сохраните информацию в Redis.
  • Метод проверки токена:Получите строковый параметр Token, добавьте префикс Key для формирования ключа, а затем передайте значение value и выполните выражение Lua (выражение Lua может обеспечить атомарность выполнения команды) для поиска соответствующего ключа и операций удаления. Проверьте возвращаемый результат команды после завершения выполнения.Если результат не пустой и ненулевой, проверка прошла успешно, в противном случае — ошибка.
import java.util.Arrays;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class TokenUtilService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 存入 Redis 的 Token 键的前缀
     */
    private static final String IDEMPOTENT_TOKEN_PREFIX = "idempotent_token:";

    /**
     * 创建 Token 存入 Redis,并返回该 Token
     *
     * @param value 用于辅助验证的 value 值
     * @return 生成的 Token 串
     */
    public String generateToken(String value) {
        // 实例化生成 ID 工具对象
        String token = UUID.randomUUID().toString();
        // 设置存入 Redis 的 Key
        String key = IDEMPOTENT_TOKEN_PREFIX + token;
        // 存储 Token 到 Redis,且设置过期时间为5分钟
        redisTemplate.opsForValue().set(key, value, 5, TimeUnit.MINUTES);
        // 返回 Token
        return token;
    }

    /**
     * 验证 Token 正确性
     *
     * @param token token 字符串
     * @param value value 存储在Redis中的辅助验证信息
     * @return 验证结果
     */
    public boolean validToken(String token, String value) {
        // 设置 Lua 脚本,其中 KEYS[1] 是 key,KEYS[2] 是 value
        String script = "if redis.call('get', KEYS[1]) == KEYS[2] then return redis.call('del', KEYS[1]) else return 0 end";
        RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
        // 根据 Key 前缀拼接 Key
        String key = IDEMPOTENT_TOKEN_PREFIX + token;
        // 执行 Lua 脚本
        Long result = redisTemplate.execute(redisScript, Arrays.asList(key, value));
        // 根据返回结果判断是否成功成功匹配并删除 Redis 键值对,若果结果不为空和0,则验证通过
        if (result != null && result != 0L) {
            log.info("验证 token={},key={},value={} 成功", token, key, value);
            return true;
        }
        log.info("验证 token={},key={},value={} 失败", token, key, value);
        return false;
    }

}

4. Создайте класс Controller для теста

Создайте класс контроллера для тестирования, который имеет интерфейс для получения идемпотентности токена и тестового интерфейса следующим образом:

import lombok.extern.slf4j.Slf4j;
import mydlq.club.example.service.TokenUtilService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@Slf4j
@RestController
public class TokenController {

    @Autowired
    private TokenUtilService tokenService;

    /**
     * 获取 Token 接口
     *
     * @return Token 串
     */
    @GetMapping("/token")
    public String getToken() {
        // 获取用户信息(这里使用模拟数据)
        // 注:这里存储该内容只是举例,其作用为辅助验证,使其验证逻辑更安全,如这里存储用户信息,其目的为:
        // - 1)、使用"token"验证 Redis 中是否存在对应的 Key
        // - 2)、使用"用户信息"验证 Redis 的 Value 是否匹配。
        String userInfo = "mydlq";
        // 获取 Token 字符串,并返回
        return tokenService.generateToken(userInfo);
    }

    /**
     * 接口幂等性测试接口
     *
     * @param token 幂等 Token 串
     * @return 执行结果
     */
    @PostMapping("/test")
    public String test(@RequestHeader(value = "token") String token) {
        // 获取用户信息(这里使用模拟数据)
        String userInfo = "mydlq";
        // 根据 Token 和与用户相关的信息到 Redis 验证是否存在对应的信息
        boolean result = tokenService.validToken(token, userInfo);
        // 根据验证结果响应不同信息
        return result ? "正常调用" : "重复调用";
    }

}

5. Создайте класс запуска SpringBoot

Создайте класс запуска для запуска приложения SpringBoot. Базовое руководство не будет представлено, рекомендуется прочитать следующее руководство, которое является очень полным.

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

6. Напишите тестовые классы для тестирования

Напишите тестовый класс для тестирования, несколько раз обращайтесь к одному и тому же интерфейсу и проверяйте, может ли он быть успешно выполнен только в первый раз.

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

@Slf4j
@SpringBootTest
@RunWith(SpringRunner.class)
public class IdempotenceTest {

    @Autowired
    private WebApplicationContext webApplicationContext;

    @Test
    public void interfaceIdempotenceTest() throws Exception {
        // 初始化 MockMvc
        MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
        // 调用获取 Token 接口
        String token = mockMvc.perform(MockMvcRequestBuilders.get("/token")
                .accept(MediaType.TEXT_HTML))
                .andReturn()
                .getResponse().getContentAsString();
        log.info("获取的 Token 串:{}", token);
        // 循环调用 5 次进行测试
        for (int i = 1; i <= 5; i++) {
            log.info("第{}次调用测试接口", i);
            // 调用验证接口并打印结果
            String result = mockMvc.perform(MockMvcRequestBuilders.post("/test")
                    .header("token", token)
                    .accept(MediaType.TEXT_HTML))
                    .andReturn().getResponse().getContentAsString();
            log.info(result);
            // 结果断言
            if (i == 0) {
                Assert.assertEquals(result, "正常调用");
            } else {
                Assert.assertEquals(result, "重复调用");
            }
        }
    }

}

Дисплей выглядит следующим образом:

[main] IdempotenceTest:  获取的 Token 串:980ea707-ce2e-456e-a059-0a03332110b4
[main] IdempotenceTest:  第1次调用测试接口
[main] IdempotenceTest:  正常调用
[main] IdempotenceTest:  第2次调用测试接口
[main] IdempotenceTest:  重复调用
[main] IdempotenceTest:  第3次调用测试接口
[main] IdempotenceTest:  重复调用
[main] IdempotenceTest:  第4次调用测试接口
[main] IdempotenceTest:  重复调用
[main] IdempotenceTest:  第5次调用测试接口
[main] IdempotenceTest:  重复调用

8. Заключительный вывод

Идемпотентность является очень распространенным и важным требованием при разработке, особенно для сервисов, связанных с деньгами, таких как платежи и заказы.Особенно важно обеспечить идемпотентность интерфейсов. В реальной разработке нам нужно гибко выбирать реализацию идемпотентности для разных бизнес-сценариев:

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

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

Название схемы Применимый метод сложность реализации Недостатки схемы
уникальный первичный ключ базы данных операция вставки операция удаления Простой - Может использоваться только для операций вставки - Может использоваться только в сценариях с уникальным первичным ключом.
Оптимистическая блокировка базы данных операция обновления Простой - Может использоваться только для операций обновления - В таблицу необходимо добавить дополнительные поля;
запросить серийный номер операция вставки операция обновления операция удаление Простой - Необходимо убедиться, что нижний по течению генерирует уникальный серийный номер; - Третья сторона Redis требуется для хранения запрошенного серийного номера;
Токен токен против воспроизведения операция вставки операция обновления операция удаление Умеренный - Требуется строка токена, сгенерированная сторонним хранилищем Redis;