마이크로 서비스 : 최종 일관성 처리


22

사용자 비밀번호를 업데이트하는 기능이 있다고 가정합니다.

'비밀번호 업데이트'버튼을 클릭하면 UpdatePasswordEvent가 3 개의 다른 서비스가 가입 된 주제로 전송됩니다.

  1. 실제로 사용자의 비밀번호를 업데이트하는 서비스
  2. 사용자의 비밀번호 기록을 업데이트하는 서비스
  3. 사용자에게 비밀번호가 변경되었음을 알리는 이메일을 보내는 서비스입니다.

최종 일관성에 대해 이해 한 내용을 바탕으로 이러한 모든 서비스 (소비자)는 동시에 이벤트를 수신하고 개별적으로 처리하여 좋은 시나리오에서는 데이터 일관성을 유지합니다.

그러나 서비스가 이벤트를 처리하지 못하면 어떻게됩니까? 예를 들어 갑작스런 연결 끊기, 데이터베이스 오류 등 ... 이러한 트랜잭션 실패를 처리하기위한 좋은 패턴 / 실습은 무엇입니까?

이벤트가 처리되지 않으면 "롤백 서비스"가 작업을 수행하고 데이터를 되 돌리는 주제에서 RollbackEvent가 생성되는 RollbackTopic을 만들려고했습니다.


11
보낸 이메일을 취소 할 수 없습니다 :-)
Laiv

2
그들 모두는 동일한 서비스의 일부 여야하기 때문입니다. 마이크로 서비스는 모놀리스와 반대되는 것이지, "물리적으로"가능한 한 적게 디자인해야한다는 의미는 아닙니다. 이것은 직접적인 관련이 없지만이 질문과 두 가지 주요 답변을 읽어야합니다. softwareengineering.stackexchange.com/questions/339230/…
Walfrat

1
데이터베이스에서 사용자 비밀번호를 동기식으로 업데이트하여 사용자에게 즉각적인 피드백을 제공하고 주제에서 비밀번호가 변경되었다는 메시지를 표시하여 다른 서비스를 비동기식으로 트리거하여 메시지가 필요하지 않도록 할 수 있습니다. 비밀번호를 포함합니다.
cr3

사용자에게 거래가 완료되었음을 알리는 전자 메일입니까, 아니면 누군가 (희망적으로) 암호를 변경했음을 알리는 전자 메일입니까? “당신이 아니라면 행동해야합니다.” 두 번째라면 지금 바로 이메일을 보내십시오.
ctrl-alt-delor

답변:


29

최종 일관성에 대해 이해 한 내용을 바탕으로 이러한 모든 서비스 (소비자)는 동시에 이벤트를 수신하고 개별적으로 처리 하여 좋은 시나리오에서는 데이터 일관성을 유지합니다.

반드시 그런 것은 아닙니다. 내가 언급했듯이, 우리는 전송 된 이메일을 취소 할 수 없으므로 여전히 일종의 "시퀀스"가 필요합니다. 이벤트 중심 데이터 관리를 통한 IPC 는 오케스트레이션 1을 면제하지 않습니다 .

예를 들어, 이전 거래가 성공적으로 완료되고 이메일 서비스가이를 증명하지 않으면 이메일을 보내면 안됩니다.

그러나 서비스가 이벤트를 처리하지 못하면 어떻게됩니까? 예를 들어 갑작스런 연결 끊기, 데이터베이스 오류 등 ... 이러한 트랜잭션 실패를 처리하기위한 좋은 패턴 / 실습은 무엇입니까?

분산 컴퓨팅오류에 대해 인사 . 그것들은 일을 복잡하게 만드는 것이며, 평소와 같이 그것들을 다루는 은색 불릿은 없습니다.

잃어버린 방주를 찾아 여행을 시작하기 전에 먼저 조직에 문의하는 것을 고려해야합니다. 종종 해결책은 조직이 현실에서 이러한 문제에 어떻게 직면하는지에 있습니다. .

특정 데이터가 없거나 불완전 할 때 모든 사람 (부서)은 무엇을합니까?

우리는 부서마다 다른 솔루션을 가지고 있으며 구현할 솔루션을 구성하는 솔루션이 서로 다르다는 것을 알게 될 것입니다.

어쨌든, 여기에 따라야 할 전략에 도움이 될 수있는 몇 가지 관행이 있습니다.

최종 일관성

시스템이 항상 일관된 상태를 유지하는 대신 시스템이 미래에 어느 시점에서 시스템을 사용할 수 있다는 사실을 받아 들일 수 있습니다. 이 접근 방식은 오래 지속되는 비즈니스 운영에 특히 유용합니다.

시스템이 일관성에 도달하는 방법은 시스템마다 다릅니다. 자동화 된 프로세스부터 일종의 인간 개입이 포함될 수 있습니다. 예를 들어, 나중에 다시 시도 하거나 고객 서비스 센터에 문의하십시오. .

모든 작업 중단

보상 거래 를 통해 시스템을 일관된 상태로 되돌립니다. . 그러나 이러한 트랜잭션도 실패 할 수 있으므로 불일치를 해결하기가 더 어려워 질 수 있다는 점을 고려해야합니다. 그리고 다시, 우리는 보낸 이메일을 취소 할 수 없습니다.

적은 수의 트랜잭션의 경우 보상 트랜잭션 수가 너무 적기 때문에이 방법이 가능 합니다. IPC에 여러 비즈니스 트랜잭션이 포함 된 경우 각 트랜잭션 마다 하나의 보상 트랜잭션 을 처리 하는 것이 어려울 수 있습니다.

우리가 가면 거래를 보상 , 우리는 찾을 회로 차단기 디자인 패턴을 매우 유용하게 - 의무적 내가 말을 감히 -

분산 트랜잭션

아이디어는 Transaction Manager 라고하는 전반적인 관리 프로세스를 통해 단일 트랜잭션 내에서 여러 트랜잭션을 확장하는 것입니다 . 분산 트랜잭션을 처리하기위한 일반적인 알고리즘은 2 단계 커밋 입니다.

분산 트랜잭션의 주요 관심사는 수명 기간 동안 리소스를 잠그는 것에 의존한다는 것입니다. 아시다시피 트랜잭션 관리자 에도 문제가 발생할 수 있습니다.

경우 트랜잭션 관리자가 손상됩니다, 우리는 인해 메시지의 대기열에 포함에 예기치 않은 동작의 결과로, 모든 differents의 경계 상황에서 여러 가지 잠금으로 끝낼 수 있습니다.2

분해 작업. 왜?

기존 시스템을 분해하고 실제로 단일 트랜잭션 경계 내에 포함하려는 개념 모음을 찾으면 마지막까지 남겨 두십시오.

샘 뉴먼

위의 주장과 관련하여 Sam은 자신의 저서 Building Microservices 에서 실제로 최종적으로 일관성을 유지할 수 없다면 지금 작업을 분리하지 말아야한다고 말합니다.

특정 작업을 둘 이상의 트랜잭션으로 분할 할 여유가없는 경우 이러한 트랜잭션은 동일한 경계 컨텍스트에 속하거나 모델링해야하는 교차 절단 컨텍스트에 속한다고 할 수 있습니다.

예를 들어, 우리의 경우 트랜잭션 # 1과 # 2는 서로 밀접하게 관련되어 있으며 아마도 둘 다 동일한 경계 컨텍스트 Accounts , Users , Register에 속할 수 있습니다 .

동일한 트랜잭션의 경계 내에 두 작업을 모두 배치하십시오. 전체 작업을보다 쉽게 ​​처리 할 수 ​​있습니다. 또한 각 거래의 중요도 수준을 파악하십시오. 트랜잭션 # 2가 실패하더라도 전체 작업이 손상되지 않아야합니다. 의심스러운 경우 조직에 문의하십시오 .


1 : 당신이 생각하는 오케스트레이션이 아닙니다. ESB의 오케스트레이션에 대해서는 이야기하지 않습니다. 서비스가 적절한 이벤트에 반응하게 만드는 것에 대해 이야기하고 있습니다.

2 : 분산 트랜잭션에 대한 흥미로운 Sam Newman의 의견을 찾을 수 있습니다 .

3 :이 주제에 관한 David Parker의 답변을 확인하십시오.


3
아주 좋은 대답입니다. 분산 트랜잭션을 사용할 때 발생하는 위험 (주로 리소스 잠금 생성 교착 상태 및 시스템 정지)을 고려하는 것의 중요성만을 강조합니다. 약 3 년 전에 근무했던 전자 상거래 제품에서 DT를 메시징 시스템으로 교체해야했습니다. 시스템에서 사용 가능한 사용자 수가 많으면 시스템에 오류가 발생하기 쉽기 때문입니다. DT와 관련된 문제는 대부분 사용자층이 커질 때 발생합니다.
Andy

7

귀하의 경우 한 번에 세 가지를 모두 처리 할 수 ​​없습니다. 필요한 것은 프로세스입니다. 다음은 매우 간단한 예입니다.

명령 및 이벤트 오케스트레이션

상태 변경 작업은 항상 일관된 엔터티에서 수행되어야한다는 것을 아는 것이 중요합니다. 보장 할 수 없다면강력한 일관성을 마스터 레코드에서 작성해야합니다.

시스템은 이벤트가 발생하기 전에 시스템 변경이 트랜잭션 안전성과 함께 지속되어야한다는 것을 보증해야합니다. 이는 발생한 이벤트가 실제로 발생한 상황을 확인하는 것입니다.

프로세스에는 몇 가지 까다로운 부분이 있으며, 다음과 같은 명백한 부분은 무시하겠습니다. UpdatePassword를 다시 발행하면됩니다. 그러나 일부 부품은 사용자가 관리해야하며 다음과 같습니다.

  • 메시지 복제 처리
  • 이메일 전송 처리.

시스템에서 프로세스 오케 스트레이터 (PO)는 문자 그대로의 용어로 내부 상태를 포함하고 상태 간 전환을 허용하여 상태 머신의 일종으로 효과적으로 작용하는 다른 엔티티 일뿐입니다. 내부 상태 덕분에 메시지 복제 처리를 제거 할 수 있습니다.

PO가 New상태에 있고 처리 UserPasswordHasBeenUpdated하면 상태가 UserPasswordHasBeenUpdated(또는 어떤 상태 이름이 적합한 지)로 변경됩니다. PO가 여전히 a에 UserPasswordHasBeenUpdated있고 다른 UserPasswordHasBeenUpdatedPO가 도착하면 PO는 메시지가 완전히 무시되어 중복임을 알 수 있습니다. 다른 상태에서도 유사한 메커니즘이 구현 될 것입니다.

이메일의 실제 전송을 처리하는 것은 약간 까다 롭습니다. 여기에는 두 가지 옵션이 있습니다.

  1. 최대 한 번 보내세요
  2. 적어도 한 번 보내십시오.

최대 한 번 보내

이 옵션을 사용하면 PO가 UserPasswordHistoryHasBeenSaved상태에 도달 하면 전자 메일을 보내는 명령이 상태 변경에 대한 반응으로 발송됩니다. 당신의 시스템은UserPasswordHistoryHasBeenSaved 전자 우편을 보내기 전에 상태가 지속 . 즉, 복제 된 메시지가 전자 우편을 다시 트리거하지 않습니다. 이 방법을 사용하면 PO에 대해 올바른 상태가 저장되지만 다음 작업을 보장 할 수는 없습니다.

적어도 한 번 보내

이것이 내가 갈 것입니다.

이에 UserPasswordHistoryHasBeenSaved대한 반응으로 전자 메일 을 저장 하고 보내는 대신 먼저 전자 메일을 보내려고합니다. 전송 작업이 실패하면 PO의 상태가로 변경되지 않고 UserPasswordHistoryHasBeenSaved동일한 유형의 다른 메시지가 계속 처리됩니다. 전자 우편 전송이 실제로 성공하지만 PO가 새 UserPasswordHistoryHasBeenSaved상태로 지속되는 동안 시스템이 실패 하면UserPasswordHistoryHasBeenSaved 는 전자 우편 발송 명령을 다시 한 번 트리거하여 사용자가 여러 번 수신했을 것입니다. .

귀하의 경우에는 사용자가 실제로 전자 메일을 받도록하고 싶습니다. 그래서 첫 번째 옵션보다 두 번째 옵션을 선택합니다.


2

큐 시스템은 생각만큼 취약하지 않습니다.

세 프로세스를 모두 관계형 DB에 쓰는 경우 트랜잭션을 사용하여 중간 프로세스 실패를 처리 할 수 ​​있습니다.

최종 커밋이 없으면 부분 작업이 삭제됩니다.

대기열 기반 시스템에서는 중간 프로세스 오류를 처리하기 위해 대기열에서 메시지를 읽을 때 유사한 옵션이 있습니다.

예를 들어 Amazon SQS는 단순히 읽은 메시지를 숨 깁니다. 최종 삭제 명령이 전송되지 않으면 메시지가 다시 나타나거나 데드 레터 큐에 놓입니다.

성공적인 처리 확인을받을 때까지 메시지 사본을 보유하는 다양한 방식으로 유사한 '트랜잭션'을 구현할 수 있습니다. 제 시간에 확인을받지 못한 경우. 메시지를 다시 보내거나 수동으로 관리 할 수 ​​있습니다.

잠재적으로 이러한 오류 메시지를 모니터링하고 관련 메시지 및 과거 상태를 알고 롤백을 수행하는 '롤백 서비스'를 만들 수 있습니다.

하나! 일반적으로 오류 메시지를 다시 보내는 것이 좋습니다. 이 모든 것이 결국에는 우세한 경향이있다. 서버가 치명적으로 실패했거나 특정 메시지 유형을 처리하는 데 버그가있었습니다.

오류에 대해 경고하면 서비스를 복구하고 메시지를 성공적으로 처리 할 수 ​​있습니다. 시스템 전체를 일관된 상태로 되돌립니다.


2

여기서 직면하고있는 것은 두 가지 일반 문제 입니다. 본질적으로 : 메시지가 수신되고 해당 메시지에 대한 응답이 발생하는지 어떻게 확인할 수 있습니까? 많은 경우 완벽한 솔루션이 존재하지 않습니다. 실제로 분산 시스템에서는 정확히 한 번만 메시지를 전달하는 것이 불가능 합니다.

첫 번째 명백한 사실은 비밀번호를 변경하는 서비스가 비밀번호 변경 이벤트를 전송해야한다는 것입니다. 이 방법으로 비밀번호 히스토리 및 메일 전송 서비스는 비밀번호가 변경된 이유에 관계없이 비밀번호가 실제로 변경 될 때만 트리거됩니다.

실제로 문제를 해결하려면 분산 트랜잭션을 고려하지 말고 적어도 한 번에 메시지 배달 및 i 등원 처리 방향을 찾으십시오.

  • 적어도 한 번

    암호 변경 이벤트가 실제로 모든 소비자에게 표시되도록하려면 메시지를 "적어도 한 번"스타일로 사용할 수있는 내구성있는 통신 채널을 사용해야합니다. 소비자는 메시지를 완전히 처리했을 때 메시지를 소비 한 것으로 인정합니다. 예를 들어, 히스토리 항목을 쓰는 중에 비밀번호 히스토리 서비스가 충돌하면 재시작 후 동일한 비밀번호 변경 이벤트를 다시 읽은 후 다시 시도하여 해당 이벤트가 히스토리에 작성된 후 읽기 전용으로 확인합니다. 메시지가 확인 될 때까지 메시지를 다시 보낼 수있는 기능에 따라 메시지 큐 솔루션을 선택해야합니다.

  • dem 등식

    최소 1 회 전달을 달성 한 후 소비자가 중단되기 전에 메시지가 부분적으로 처리 된 후 나중에 다시 처리 될 때 중복 작업이 발생하는 문제가 있습니다. 각 서비스를 설계하여 dem 등원이되도록 해결해야합니다. 수행하는 쓰기는 부작용없이 여러 번 발생할 수 있으며, 수행 한 작업을 자체 저장소에 유지하고 작업을 두 번 이상 수행하지 않습니다. 메일 발송의 경우, 메일이 dem 등적으로 작동하도록하는 것은 가치가 없으며 때로는 메일이 두 번 발송되는 것만으로도 괜찮을 것입니다.

어쨌든, 당신이 서비스를 얼마나 미세하게 만드는지주의하십시오. 비밀번호 히스토리 서비스가 비밀번호 변경 서비스와 독립적이어야합니까?


1

나는 많은 답변에 동의하지 않습니다.

  1. 지금 전자 메일 보내기“누군가 암호를 변경했습니다. 그것이 당신이라면 어떤 것도 할 필요가 없습니다. 당황하지 않으면.”도착하면 도착합니다.
  2. 비밀번호를 변경하십시오. 결국 일관성이 있습니다. 이 세션에 사용자가 변경 한 내용이 표시되도록하려고합니다.

추가 할 수있는 다른 일관성 약속이 있습니다.

  • 시간 순서대로 변경 사항이 발생하는지 확인하십시오.
  • 사용자에게는 롤백이 표시되지 않지만 다른 사용자는 여전히 변경 사항을 볼 수 없습니다.
  • 다른 사람이 있습니다

이러한 추가 일관성은 응용 프로그램의 작업에 따라 구현되어야합니다.


“역사 업데이트”가 무슨 뜻인지 모르겠지만, 절대 역사를 바꾸지 마십시오. DAG 만 확장하는 경우 현재 상태가 변경됩니다. 그들은 독립적이지 않습니다. 그들이 그렇다면 당신은 일어난 일을 반영하는 역사에 의존 할 수 없습니다. (그리고 마지막으로, 암호를 참조 저장하지 않는 방법을 저장하지 암호로 )


당신이 처음에 이메일을 보낼 수 있다면 당신의 접근은 괜찮습니다. 이메일과 함께 무언가를 보내야하는 경우. 어쩌면 일관성이 달성 된 후에 만 ​​얻을 수있는 일종의 링크 / 데이터 일 수 있으며 먼저 전자 메일을 보낼 수 없습니다. 그것이 내가 논평 한 것 consider asking the organization first.입니다. 당신이 옳을 것입니다. 그러나 취소 할 수없는 이벤트를 처리하는 것이 중요하다는 것을 알게되었습니다. 예를 들어 최종 사용자에게 알림. 사용자 데이터의 실제 상태에있는 알림은 나쁜 인상을 줄 수 있습니다.
Laiv

즉,이 특정 시나리오 (암호 변경 알림)의 경우이 접근법에 동의했습니다. 요구 사항을 충족시키는 즉시.
Laiv
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.