предисловие
Привет всем, я маленький мальчик, который собирает улиток. Сегодня поговорим об идемпотентном дизайне.
- что такое идемпотент
- Зачем нужен идемпотент
- Таймаут интерфейса, как с этим бороться?
- Как создать идемпотент?
- 8 схем достижения идемпотентности
- Идемпотентность HTTP
- публика:маленький мальчик собирает улиток
1. Что такое идемпотентность?
Идемпотентность - это концепция математики и информатики.
- В математике идемпотентное выражение как функция:
f(x) = f(f(x))
. Например, функция, которая находит абсолютное значение, является идемпотентной.abs(x) = abs(abs(x))
. - В компьютерных науках идемпотентность означает, что однократный и многократный запрос ресурса должен иметь одинаковые побочные эффекты или что несколько запросов имеют тот же эффект, что и эффект выполнения одного запроса.
2. Зачем нужен идемпотент
Например:
Мы разрабатываем передаточную функцию, предполагая, что мы вызываем нижестоящий интерфейстайм-аут. В целом,тайм-аутможет бытьпотеря пакетов при передаче по сетиВозможно, он не был доставлен, когда был сделан запрос, а может быть, запрос пришел.Результат возвращается, но теряется. Можем ли мы попробовать еще раз в это время? еслиПовторить попыткуЕсли да, будет ли переведена дополнительная сумма денег?
После того, как почти все современные интернет-системы будут отделены и изолированы, между различными системами будут осуществляться взаимные удаленные вызовы. Вызов удаленной службы будет иметь три состояния: успех, сбой или тайм-аут. Первые два являются явными состояниями, в то время как тайм-аутНеизвестный статус. мы переводимтайм-аут, если нисходящая система передачиидемпотентконтроль, мы инициируемПовторить попытку, тогда ты можешьОбеспечить, чтобы передача осуществлялась нормально, и чтобы не было лишней передачи..
На самом деле, помимо примера переноса, в ежедневной разработке есть еще иМногие, многие примеры нужно считать идемпотентными. Например:
- Когда потребители MQ (промежуточного программного обеспечения сообщений) читают сообщения, могут быть прочитаны повторяющиеся сообщения. (Повторное потребление)
- Например, при отправке формы, если вы быстро нажмете кнопку отправки, могут быть сгенерированы две части одних и тех же данных (Отправка дубликатов внешнего интерфейса)
3. Таймаут интерфейса, что делать?
Если время ожидания нашего вызова нижестоящего интерфейса истечет, что нам делать?
имеютдва вариантаиметь дело с:
- Решение 1. Нижестоящая система предоставляет соответствующий интерфейс запросов. Если время ожидания интерфейса истекло, сначала проверьте соответствующую запись. Если она будет признана успешной, она пройдет через процесс успеха. Если это ошибка, она будет обработана как ошибка.
Возьмем наш пример перевода, система перевода предоставляет запроспередача записьинтерфейс, есликанальная системаперечислитьсистема передачиКогда время истекло,канальная системаСначала проверьте эту запись, чтобы узнать, была ли запись передачи успешной или нет.Если она успешна, выполните процесс успешного выполнения, а если нет, попробуйте еще раз инициировать передачу.
- Вариант 2: Нисходящий интерфейсидемпотентный носитель, восходящая система, еслитайм-аут вызова, вы можете инициировать повторную попытку.
Оба варианта хороши, но еслиСценарии повторного потребления MQ, первое решение не очень подходит, поэтому нам все еще требуется нижестоящая системаВнешний интерфейс поддерживает идемпотентность.
4. Как разработать идемпотент
Поскольку так много сценариев необходимо рассматривать как идемпотентные, как мы можем спроектировать идемпотентность?
Идемпотентность означает уникальность запроса. Независимо от того, какую схему вы разрабатываете для идемпотентности, вам потребуетсяглобально уникальный идентификатор, чтобы пометить этот запрос как уникальный.
- Если вы используете уникальный индекс для управления идемпотентностью, уникальный индекс уникален.
- Если вы используете первичный ключ базы данных для управления идемпотентностью, первичный ключ уникален.
- Если вы придерживаетесь пессимистического подхода к блокировке, базовый маркер по-прежнемуглобально уникальный идентификатор
4.1 Глобальный уникальный идентификатор
Глобально уникальный идентификатор, как его сгенерировать? Вы можете вспомнить, как генерируется идентификатор первичного ключа базы данных?
Да, мы можем использоватьUUID
, но недостаток UUID очевиден, место, занимаемое строкой, относительно велико, генерируемый идентификатор слишком случайный, читабельность плохая, и он не увеличивается.
мы также можем использовать雪花算法(Snowflake)
Создайте уникальный идентификатор.
Алгоритм снежинки — это алгоритм, который генерирует распределенный глобально уникальный идентификатор, и сгенерированный идентификатор называется
Snowflake IDs
. Этот алгоритм был создан Twitter и использовался для идентификаторов твитов.
Идентификатор снежинки имеет 64 бита.
- Бит 1: старший бит long в Java — это бит знака, который представляет положительные и отрицательные значения, положительные числа равны 0, а отрицательные числа равны 1. Как правило, сгенерированные идентификаторы являются положительными числами, поэтому по умолчанию используется значение 0.
- Следующие первые 41 бит — это метка времени, представляющая количество миллисекунд, прошедших с выбранной эпохи.
- Следующие 10 бит представляют идентификатор компьютера, что предотвращает конфликты.
- Остальные 12 бит представляют собой серийный номер идентификатора, сгенерированного на каждой машине, что позволяет создавать несколько идентификаторов Snowflake за одну миллисекунду.
Конечно, вы также можете использовать идентификатор Baidu для глобальных уникальных идентификаторов.Uidgenerator
, или Meituan'sLeaf
.
4.2 Основной процесс идемпотентного синтеза
Процесс идемпотентной обработки, в конечном счете, заключается в фильтрации полученных запросов.Конечно, запрос должен иметь全局唯一的ID标记
Какие. Тогда как узнать, был ли запрос получен раньше? Сохраните запрос. При получении запроса сначала проверьте запись хранилища. Если запись существует, верните последний результат. Если ее нет, обработайте запрос.
Общая обработка идемпотентов выглядит следующим образом:
5. 8 схем достижения идемпотентности
Базовый процесс создания идемпотентов аналогичен, просто рассмотрим 8 схем реализации идемпотентов~
5.1 выбор+вставка+первичный ключ/конфликт уникального индекса
В повседневной разработке, чтобы реализовать идемпотентность интерфейса транзакций, я реализовал его так:
Приходит запрос на транзакцию, я сначала выполню запросУникальный серийный номер bizSeq
поле, первыйselect
немноготаблица потока базы данных
- Если данные уже существуют, перехват представляет собой повторный запрос, и успех возвращается напрямую;
- Если данные не существуют, выполните
insert
вставить, еслиinsert
В случае успеха вернуть успех напрямую, еслиinsert
Если возникает исключение конфликта первичного ключа, исключение перехватывается, а затем сразу возвращается успех.
Блок-схема выглядит следующим образом
Псевдокод выглядит следующим образом:
/**
* 幂等处理
*/
Rsp idempotent(Request req){
Object requestRecord =selectByBizSeq(bizSeq);
if(requestRecord !=null){
//拦截是重复请求
log.info("重复请求,直接返回成功,流水号:{}",bizSeq);
return rsp;
}
try{
insert(req);
}catch(DuplicateKeyException e){
//拦截是重复请求,直接返回成功
log.info("主键冲突,是重复请求,直接返回成功,流水号:{}",bizSeq);
return rsp;
}
//正常处理请求
dealRequest(req);
return rsp;
}
Почему это было раньшеselect
поинтересовался, еще нужноtry...catch...
Как насчет перехвата повторяющихся исключений?
Это связано с тем, что в сценариях с высокой степенью параллелизма два запроса
select
В моменте его может и не найти, и тогда все они уйдут на место вставки.
Конечно, суникальный индексзаменятьпервичный ключ базы данныхЭто тоже нормальноглобально уникальный идентификаторВот и все.
5.2. Прямая вставка + конфликт первичного ключа/уникального индекса
В схеме 5.1 проверим сначалаводомерЗапрос транзакции, определите, существует ли он, а затем вставьте запись запроса, если он не существует. еслиВероятность повторных запросов относительно низкая, мы можем вставить запрос напрямую, используяКонфликт первичного ключа/уникального индекса, чтобы судить об этомповторите запрос.
Блок-схема выглядит следующим образом:
Псевдокод выглядит следующим образом:
/**
* 幂等处理
*/
Rsp idempotent(Request req){
try{
insert(req);
}catch(DuplicateKeyException e){
//拦截是重复请求,直接返回成功
log.info("主键冲突,是重复请求,直接返回成功,流水号:{}",bizSeq);
return rsp;
}
//正常处理请求
dealRequest(req);
return rsp;
}
Советы :
Не запутайтесь, на самом деле существует разница между антивесовым и идемпотентным дизайном. Основная цель антидупликации — предотвратить генерацию повторяющихся данных, и этого достаточно для перехвата повторяющихся запросов. В дополнение к перехвату обработанных запросов идемпотентный дизайн также требует, чтобы каждыйТот же запрос возвращает тот же результат. Однако во многих случаях их обработка может быть одинаковой, но возвращаемые ответы будут разными.
5.3 Идемпотентность конечного автомата
Многие бизнес-таблицы сохраняют состояние, например таблица потоков передачи.0-待处理,1-处理中、2-成功、3-失败状态
. При обновлении потока передачи будет задействовано обновление состояния потока, то есть будет задействован конечный автомат (т. е. диаграмма изменения состояния). Мы можем использовать конечный автомат для достижения идемпотентности, давайте посмотрим, как это реализовано.
Например, после успешной передачиОбработкаПоток передачи обновляется доуспехСостояние, SQL пишет следующее:
update transfr_flow set status=2 where biz_seq=‘666’ and status=1;
Краткая блок-схемаследующее:
Реализация псевдокода выглядит следующим образом:
Rsp idempotentTransfer(Request req){
String bizSeq = req.getBizSeq();
int rows= "update transfr_flow set status=2 where biz_seq=#{bizSeq} and status=2;"
if(rows==1){
log.info(“更新成功,可以处理该请求”);
//其他业务逻辑处理
return rsp;
}else if(rows==0){
log.info(“更新不成功,不处理该请求”);
//不处理,直接返回
return rsp;
}
log.warn("数据异常")
return rsp:
}
Что такое государственная машинаидемпотенткак насчет?
- Когда пришел первый запрос, серийный номер bizSeq был
666
, статус конвейера обрабатывается, значение равно1
, обновить до2-成功的状态
, поэтому оператор обновления может нормально обновлять данные, количество строк, затронутых результатом выполнения SQL, равно 1, а состояние потока, наконец, становится равным 2. - Пришел и второй запрос, если его серийный номер еще
666
, потому что состояние конвейера имеет2-成功的状态
, поэтому результат обновления равен 0, бизнес-логика не будет обрабатываться, а интерфейс напрямую возвращает успех.
5.4 Извлечение таблицы антивеса
Сценарии для 5.1 и 5.2, основаны на счетчике бизнес-потокаbizSeq
на уникальность. Много раз мыУникальный серийный номер бизнес-таблицыНадеемся, что серверная система сгенерирует, или мы хотимФункция защиты от дублирования отделена от бизнес-таблицыДавай, теперь мы можем сделать это в одиночкуТаблица антивеса. Конечно, антидупликационная таблица также использует уникальность первичного ключа/индекса.Если вставка в антидупликационную таблицу конфликтует, она напрямую вернет успех, и если вставка успешна, запрос будет обработан.
5.5 токен токен
Схема токена токена обычно состоит из двух фаз запроса:
- Клиент запрашивает запрос на получение токена, а сервер генерирует токен и возвращает его.
- Клиент запрашивает токен, а сервер проверяет токен.
Блок-схема выглядит следующим образом:
- Клиент инициирует запрос на получение токена.
- Сервер генерирует глобально уникальный токен, сохраняет его в Redis (обычно устанавливает срок действия) и возвращает его клиенту.
- Клиент несет токен и инициирует запрос.
- Сервер переходит к Redis, чтобы подтвердить, существует ли токен, обычно используемый
redis.del(token)
Если он есть, то удаление пройдет успешно, то есть бизнес-логика будет обработана, если удаление не удалось, бизнес-логика не будет обработана, а будет возвращен результат напрямую.
5.6 Пессимистическая блокировка (например, выбор для обновления)
чтопессимистический замок?
С точки зрения непрофессионала этоочень пессимистично, каждый раз, когда я собираюсь манипулировать данными, я чувствую, что другие будут изменять их в середине, поэтому каждый раз, когда я получаю данные, они будут заблокированы. Официальная точка зрения состоит в том, что общие ресурсы используются только одним потоком за раз, другие потоки блокируются, а ресурсы передаются другим потокам после того, как они израсходованы.
Как пессимистическая блокировка контролирует идемпотентность? этозамокДа, это вообще реализовано с делами.
Для бизнес-сценария обновления заказа:
Предполагая, что заказ сначала найден, если статус обработки найден, бизнес обрабатывается, а затем статус заказа обновляется до завершения. Если заказ найден и находится ли он в состоянии обработки, он вернется напрямую
Общий псевдокод выглядит следующим образом:
begin; # 1.开始事务
select * from order where order_id='666' # 查询订单,判断状态
if(status !=处理中){
//非处理中状态,直接返回;
return ;
}
## 处理业务逻辑
update order set status='完成' where order_id='666' # 更新完成
commit; # 5.提交事务
Этот сценарий является неатомарной операцией. В среде с высокой степенью параллелизма может возникнуть проблема, связанная с тем, что бизнес-процесс выполняется дважды:
Когда выполняется запрос A, другой запрос B также запускает операцию оценки состояния. Поскольку запрос A еще не успел изменить состояние, запрос B также может быть успешно выполнен, в результате чего бизнес будет выполнен дважды.
Вы можете использовать пессимистическую блокировку базы данных (select ...for update
)решить эту проблему.
begin; # 1.开始事务
select * from order where order_id='666' for update # 查询订单,判断状态,锁住这条记录
if(status !=处理中){
//非处理中状态,直接返回;
return ;
}
## 处理业务逻辑
update order set status='完成' where order_id='666' # 更新完成
commit; # 5.提交事务
- Здесь order_id должен бытьпоказательилипервичный ключХа, просто заблокируй эту запись, если нетиндекс или первичный ключ,встречастол блокировкииз!
- Пессимистическая блокировка блокирует строку данных во время одной и той же операции транзакции. Другие запросы могут поступать толькождать, если текущая транзакция занимает много времени, это сильно повлияет на производительность интерфейса. Поэтому обычно не рекомендуется делать это с пессимистической блокировкой.
5.7 Оптимистическая блокировка
Пессимистическая блокировка имеет проблемы с производительностью, вы можете попробоватьоптимистическая блокировка.
чтооптимистическая блокировка?
Оптимистическая блокировка очень оптимистична при работе с данными, полагая, что другие не будут изменять данные в то же время, поэтому оптимистическая блокировка не будет заблокирована. Просто оцените при обновлении, не изменили ли другие данные за этот период.
Как реализовать оптимистическую блокировку?
Просто добавьте еще один столбец в таблицу
version
Номер версии, каждый раз, когда запись обновляетсяversion
обновить его (version=version+1
). Конкретный процесс заключается в том, чтобы сначала узнать номер текущей версии.version
, а затем перейдите к обновлению измененных данных, подтвердите, является ли это только что обнаруженным номером версии, если это так, выполните обновление
Например, перед обновлением мы сначала проверяем данные, и найденный номер версииversion =1
select order_id,version from order where order_id='666';
затем используйтеversion =1
и订单Id
вместе как условие, а затем обновить
update order set version = version +1,status='P' where order_id='666' and version =1
Бизнес-логика может быть обработана только после успешного обновления в конце. Если обновление завершается сбоем, по умолчанию повторяется запрос и возвращается напрямую.
Блок-схема выглядит следующим образом:
Почему предлагается увеличить номер версии?
Поскольку оптимистическая блокировка имеет проблему ABA, если версия version всегда самоувеличивается, ситуации ABA не будет.
5.8 Распределенные блокировки
Логика распределенных блокировок для достижения идемпотентности заключается в том, что при поступлении запроса сначала нужно попытаться получить распределенную блокировку.Если это удается, выполнить бизнес-логику.В противном случае, если получение не удается, отказаться от запроса и вернуть успех напрямую. Поток выполнения показан на следующем рисунке:
- Распределенные блокировки могут использовать Redis или ZooKeeper, но Redis относительно лучше, потому что он легче.
- Распределенная блокировка Redis, вы можете использовать команду
SET EX PX NX + 唯一流水号
реализация, распределенная блокировкаkey
Должен быть уникальным идентификатором компании. - Когда Redis выполняет действие установки ключа, необходимо установить время истечения срока действия.Это время истечения срока действия не может быть слишком коротким, он не может перехватывать повторные запросы и не может быть установлен слишком длинным, что займет место в хранилище.
6. Идемпотентность HTTP
Наши интерфейсы обычно основаны на http, поэтому давайте поговорим об идемпотентности http. Существуют в основном следующие методы HTTP-запроса Давайте посмотрим, является ли каждый интерфейс идемпотентным.
- ПОЛУЧИТЬ метод
- ГОЛОВНОЙ метод
- ВАРИАНТЫ метод
- УДАЛИТЬ метод
- POST-метод
- метод PUT
6.1 ПОЛУЧИТЬ метод
Метод GET HTTP используется для получения ресурсов, которые могут бытьаналогияв базе данныхselect
Запрос не должен иметь побочных эффектов, поэтому он идемпотентный.
Он не меняет состояние ресурса, независимо от того, вызываете ли вы его один раз или несколько раз, эффект один и тот же, и побочных эффектов нет.
Если ваш метод GET предназначен для получения последних новостей и вызова его в разные моменты времени, несмотря на то, что содержимое возвращаемого ресурса разное, в итоге это не влияет на суть ресурса, поэтому он все равно идемпотентный.
6.2 ГОЛОВНОЙ метод
HTTP HEAD немного похож на GET, главное отличиеHEAD
Не содержит данных представления, а только заголовки HTTP, поэтому он также является идемпотентным. Если вы хотите определить, существует ли ресурс, многие люди будут использоватьGET
, на самом деле используяHEAD
является более подходящим. которыйHEAD
Метод обычно используется для разведки.
6.3 Метод ОПЦИИ
HTTP OPTIONS в основном используется для получения методов, поддерживаемых текущим URL-адресом, а также немного похож на запрос, поэтому он также является идемпотентным.
6.4 Метод УДАЛИТЬ
Метод HTTP DELETE используется для удаления ресурсов и является идемпотентным. Например, мы хотим удалитьid=666
Post, один раз выполненный и выполненный несколько раз, эффект одинаков.
6.5 Метод POST
Метод HTTP POST используется для создания ресурсов, которые могут быть аналогичныupdate/提交
, очевидно, что одно и несколько обновлений отправки имеют побочные эффекты, а эффект выполнения отличается.Идемпотентность не выполняется.
Например: ОТПРАВИТЬСемантика www.tianluo.com/articles…В ответ состояние создания поста должно содержать статус сообщения и URI поста. Два идентичных запроса на почту создали два ресурса на стороне сервера, которые имеют разные URI; такМетод POST не является идемпотентным.
6.6 Метод PUT
Метод HTTP PUT используется для создания или обновления операций, а соответствующий URI — это сам ресурс, который нужно создать или обновить. Он имеет побочные эффекты и должен удовлетворять идемпотентности.
Например: ПОСТАВИТЬWoohoo.Tianluo.com/articles/66…Пост с ID 666. Побочные эффекты нескольких PUT для одного и того же URI такие же, как и при использовании одного PUT, поэтому метод PUT является идемпотентным.