Сцены
Клиент вызывает интерфейс службы A, а интерфейс службы A вызывает службу B. Если и служба A, и служба B выполняются успешно, это означает, что обе транзакции должны быть зафиксированы; В случае сбоя службы A или службы B происходит сбой, и ни одна транзакция не выполняется и не откатывается. Из-за ненадежности сетевых запросов, если A не может позвонить B, он может: 1. B не получает сетевой запрос; 2. B не может выполнить после его получения; 3. B выполняется успешно, и запрос возвращается с ошибкой. исключение. 4. B вызывает тайм-аут, B может преуспеть или потерпеть неудачу. Поэтому, когда А звонит Б, могут возникнуть несоответствия.
Несколько способов вызова интерфейса
Способ 1: симулировать звонки напрямую
Интерфейс услуг:
try {
业务代码A
feign调用B服务接口
事务提交
} catch (Exception e) {
事务回滚
}
Сервисный интерфейс Б:
try {
业务代码B
事务提交
//此时接口状态码2XX
} catch (Exception e) {
事务回滚
//此时接口状态码非2XX
}
Анализ консистенции:
- feign успешно вызывает сервисный интерфейс B, и код состояния — 2XX. Затем транзакция службы B была зафиксирована, и транзакция была зафиксирована A. Если транзакция A успешно отправлена, она непротиворечива; если транзакция A не может быть отправлена, но транзакция в B была отправлена в это время, она несогласованна.
- B не получает сетевой запрос. B не выполняется, вызов feign выдает исключение, а транзакция A не фиксируется, она откатывается, и данные непротиворечивы.
- B выполняется успешно, и при возврате запроса возникает исключение. Транзакция B зафиксирована, ложно вызывает интерфейс службы B ненормально, транзакция A откатывается, а данные несовместимы.
- Превышено время ожидания вызова B. Возможно, B не получил сетевой запрос, или возможно, что B успешно выполнился и запрос вернулся ненормально, или возможно, что B получил запрос и ответил медленно. Состояние согласованности неопределенно и возможно.
Второй способ: на основе надежного сообщения
После выполнения бизнес-кода службы сообщение отправляется в очередь сообщений, такую как rabbitmq, и используется ack, чтобы убедиться, что отправка прошла успешно; После того, как служба B успешно получает и потребляет, она вручную подтверждает сообщение, и kafka вручную отправляет смещение.
Производитель услуг:
try {
业务代码A
ack = rabbitmqProducer.sendAndGetAck
if !ack {
//发送失败可以设置自动重试,不重试就抛出异常
throws new RabbitmqSendException
}
事务提交
} catch (Exception e) {
//事务回滚
}
Потребитель очереди сообщений службы B 1:
//
try {
业务代码B
channel.basicAck手动确认//kafka则手动提交位移
事务提交
//此时接口状态码2XX
} catch (Exception e) {
事务回滚
//此时接口状态码非2XX
}
Второй потребитель очереди сообщений службы B:
//
try {
业务代码B
事务提交
//此时接口状态码2XX
} catch (Exception e) {
事务回滚
//此时接口状态码非2XX
}
channel.basicAck手动确认//kafka则手动提交位移
Анализ консистенции:
- Служба A успешно отправляет сообщение в очередь сообщений, но не может отправить транзакцию, что приводит к несогласованности данных.
- Сторона потребителя A B: после того, как сообщение с подтверждением вручную удалено из очереди сообщений, фиксация транзакции B завершается неудачно, несогласованность данных.
- Сторона потребителя B 2: транзакция B успешно отправлена, но ручное подтверждение не удается, и сообщение может быть получено повторно, что приводит к несогласованности. В это время к сообщению можно добавить uuid.После получения сообщения сервис B выполняет дедупликацию и повторную обработку в соответствии с uuid для достижения идемпотентности.
Способ 3: предварительное выполнение + подтверждение + возврат, аналогичный TCC
Службе A необходимо вставить таблицу transaction_record, чтобы записать статус вызова и предоставить его службе B в качестве обратного вызова.
Бизнес-интерфейс сервиса:
String uuid = generateUUID()//生成一个uuid
try{
feign.preCreate(uuid,...)//feign调用B预执行,比如B服务为创建订单服务,预创建一个订单,但状态为待确认
业务代码A
将uuid插入transaction_record表中
事务提交
}catch (Exception e) {
事务回滚
feign.cancel(uuid)//feign调用B取消,比如B服务为创建订单服务,设置该订单状态为取消
}
feign.confirm(uuid)//feign调用B确认,比如B服务为创建订单服务,设置该订单状态为确认,此时订单可用
Интерфейс проверки службы службы предоставляется для состояния проверки службы B:
get /v1/transaction/{uuid}
从transaction_record表中查询,有则返回确认,没有则返回取消
Сервис B должен предоставлять интерфейсы предварительного выполнения, подтверждения и отмены. Если подтверждение или отмена задерживаются после предварительного выполнения, B свяжется с A и подтвердит или отменит в зависимости от результата.
Анализ консистенции:
- Выполнение preCreate в A ненормально. Исключение должно быть выброшено, больше нет бизнес-кода и uuid для вставки таблицы событий, чтобы выполнить отмену. Если выполняется B, статус также неподтвержденный, что не влияет на непротиворечивость; Если отменить также не удается, например, когда B в это время кладет трубку, B должен вызвать интерфейс проверки службы A после перезапуска, чтобы определить статус. Состояние такое же.
- Бизнес-код A не выполняется, транзакция откатывается, и симулируется вызов B для отмены. Если отмена прошла успешно, статус такой же, если отмена не удалась, должно быть выброшено исключение, и подтверждение выполнено не будет. Состояние такое же.
- Если бизнес-код A успешно выполнен, фиксация транзакции завершается ошибкой, и транзакция откатывается. Если откат не удался, выдается исключение, отмена и подтверждение больше не выполняются, а B выполнит проверку тайм-аута, чтобы определить статус; если откат успешен, отмена. Состояние такое же.
- Транзакция успешно отправлена, но подтверждение не выполнено (например, когда A выполняет подтверждение, служба A или B просто зависает). Затем служба B проверяет через некоторое время и обнаруживает, что uuid существует, и статус модификации подтверждается. Состояние такое же.
- После завершения предварительной обработки выполните бизнес-код. Если бизнес-код выполняется медленно, и служба B считает, что время ожидания истекло, служба B через некоторое время проверит обратно. Если транзакция A не была отправлена, интерфейс проверки Вернется отменить. Затем B отменяется, но A фиксирует транзакцию, и в это время состояние транзакции несовместимо. В это время его можно отрегулировать, установив чуть больший период ожидания для B, чтобы служба A могла пройти в ожидаемый период ожидания при вызове предварительно обработанной имитации.
Суммировать
Выше находится несколько способов позвонить между интерфейсами. Вот только вероятность идеи. Вы можете оптимизировать себя при применении, и вы можете изменить его в асинхронную + повторение. Если требования к согласованности могут быть использованы, таблица UUID + INSERT также может быть реализована другими способами. Чтобы улучшить консистенцию, позвоните по интерфейсу также должно быть как мощность и т. Д., И может пройти мощность бизнес-логики или UUID реализация.