마이크로 서비스에 가장 많이 사용되는 거래 전략은 무엇입니까


80

내가 본 주요 문제 중 하나는 마이크로 서비스가있는 시스템에서 발생하며 트랜잭션이 다른 서비스에 걸쳐있을 때 트랜잭션이 작동하는 방식입니다. 자체 아키텍처 내에서 분산 트랜잭션을 사용하여이 문제를 해결했지만 자체 문제가 있습니다. 특히 교착 상태는 지금까지 고통 스럽습니다.

또 다른 옵션은 시스템 내 흐름을 알고 있으며 전체 시스템에 걸친 백그라운드 프로세스로 롤백을 처리하는 일종의 맞춤형 트랜잭션 관리자 인 것 같습니다. 따라서 다른 서비스에 롤백하도록 지시합니다 다운되면 나중에 알려주세요).

허용되는 다른 옵션이 있습니까? 이 두 가지 모두 단점이있는 것 같습니다. 첫 번째는 교착 상태 및 기타 여러 가지 문제를 일으킬 수 있으며 두 번째 문제는 데이터 불일치를 초래할 수 있습니다. 더 나은 옵션이 있습니까?


확실히, 많은 고객들이 마이크로 서비스를 동시에 사용합니까, 아니면 한 번에 하나씩 만 사용합니까?
Marcel

2
나는이 질문에 대해이 질문을하려고했다, Marcel. 그러나이 두 가지를 모두 수행 할 수있을 정도로 큰 시스템을 사용하고 있고 두 가지를 모두 지원하는 아키텍처를 원한다고 가정 해 봅시다.
Kristof

4
이것은 말이 좋지 않지만 매우 중요한 질문입니다. 대부분의 사람들이 "트랜잭션"에 대해 생각할 때 트랜잭션의 원 자성, 격리 또는 내구성 측면이 아니라 일관성에 대해 거의 독점적으로 생각합니다. 실제로 질문은 "마이크로 서비스 아키텍처를 고려하여 ACID 호환 시스템을 어떻게 만드는가. 나머지 ACID가 아닌 일관성 만 구현하는 것은 실제로 유용하지 않습니다. 나쁜 데이터를 좋아하지 않는 한
Jeff Fischer

10
Jeff Fischer는 "질문이 덜한"단어를 만들기 위해 항상 내 질문을 수정하려고 할 수 있습니다.
Kristof

이 질문과 토론은 SO에 관한 이 다른 질문 과 밀접한 관련 있습니다.
Paulo Merson

답변:


42

일반적인 접근 방식은 해당 마이크로 서비스를 가능한 한 많이 분리하여 단일 단위로 취급하는 것입니다. 그런 다음 서비스 전체에서 트랜잭션을 개발할 수 있습니다 (즉, 서비스 내부에 DB 트랜잭션을 여전히 가질 수는 있지만 일반적인 DB 트랜잭션의 일부는 아님).

트랜잭션이 어떻게 발생하고 서비스에 어떤 의미가 있는지 생각하면 원래 작업을 취소하는 롤백 메커니즘 또는 실제 작업을 커밋하라는 지시가있을 때까지 원래 작업을 예약하는 2 단계 커밋 시스템을 구현할 수 있습니다. 물론 두 시스템 모두 자체 구현을 의미하지만 이미 마이크로 서비스를 구현하고 있습니다.

금융 서비스는 항상 이런 종류의 일을합니다. 내 은행에서 은행으로 돈을 옮기고 싶다면 DB에서와 같은 단일 거래는 없습니다. 어느 은행이 어떤 시스템을 운영하고 있는지 모르므로 각 마이크로 서비스처럼 효과적으로 취급해야합니다. 이 경우 은행에서 내 계좌에서 보류 계좌로 돈을 옮긴 다음 은행에 돈이 있다고 알려줍니다. 송금에 실패하면 은행에서 송금하려고 한 금액으로 계좌를 환불합니다.


5
환불이 실패 할 수 없다고 가정합니다.
Sanjeev Kumar Dangi

9
@ SanjeevKumarDangi 그것은 가능성이 적고 실패하더라도 보유 계정과 실제 계정이 은행의 통제하에 있기 때문에 쉽게 처리 할 수 ​​있습니다.
gldraphael

30

표준적인 지혜는 거래가 마이크로 서비스 경계를 ​​넘지 않아야한다는 것입니다. 주어진 데이터 세트가 실제로 다른 데이터와 원자 적으로 일치해야하는 경우이 두 가지가 함께 속해 있습니다.

이것이 완전히 설계 될 때까지 시스템을 서비스로 분할하기가 매우 어려운 이유 중 하나입니다. 현대 세계에서 아마도 그것을 쓰는 것을 의미 할 것입니다 ...


37
이러한 접근 방식은 모든 마이크로 서비스를 결국 단일 모 놀리 식 응용 프로그램으로 병합 할 수 있습니다.
Slava Fomin II

4
이것이 올바른 방법입니다. 실제로는 간단합니다. 서비스 간 트랜잭션이 필요한 경우 서비스가 잘못되었습니다. 다시 디자인하십시오! @SlavaFominII 당신이 말하는 것은 마이크로 서비스 시스템을 디자인하는 방법을 모른다면 사실입니다. 마이크로 서비스 접근 방식에 맞서 싸우고 있다면 그렇게하지 마십시오. 모놀리스는 나쁜 마이크로 서비스 디자인보다 낫습니다. 올바른 서비스 경계를 ​​찾을 때만 서비스에서 모놀리스를 분할해야합니다. 그렇지 않으면 마이크로 서비스를 사용하는 것이 올바른 아키텍처 선택이 아니라 과대 광고를 따르는 것입니다.
Francesc Castells

@FrancescCastells 우리의 서비스가 다른 서비스와의 거래를 진정으로 요구한다면, 우리는 제한된 컨텍스트를 무시하고 단일 거래로 끝나는 방식으로 서비스를 모델링해야한다는 것을 의미합니까? 나는 microservices의 초보자인데, 그것이 순진하게 들리면 여전히 내 질문을 용서하십시오 .... : D : D
Bilbo Baggins September

@BilboBaggins 나는 제한된 맥락을 무시하는 것과 반대되는 것을 의미합니다. 정의에 따라 트랜잭션은 여러 컨텍스트가 아닌 제한된 컨텍스트 내에서 발생합니다. 따라서 제한된 컨텍스트와 함께 마이크로 서비스를 올바르게 설계하면 트랜잭션이 여러 서비스에 걸쳐 있지 않아야합니다. 언젠가 필요한 것은 거래가 아니라 상황이 올바르게 진행되지 않을 때 최종 일관성을 적절히 처리하고 적절한 보상 조치를 취하는 것입니다.
Francesc Castells

나는 당신의 요점을 얻지 못했습니다, 우리가 채팅에서 이것을 논의 할 수있는 기회가 있습니까?
Bilbo Baggins

17

응용 프로그램에서 일관성이 강력한 요구 사항이라면 마이크로 서비스가 더 나은 방법인지 스스로에게 문의해야한다고 생각합니다. 마틴 파울러가 같이 말한다 :

마이크로 서비스는 분산 된 데이터 관리에 대한 주장으로 인해 최종 일관성 문제를 야기합니다. 모놀리스를 사용하면 단일 트랜잭션에서 여러 가지를 함께 업데이트 할 수 있습니다. 마이크로 서비스는 업데이트하기 위해 여러 리소스가 필요하며 분산 트랜잭션은 타당한 이유가 있습니다. 이제 개발자는 일관성 문제를 알고 코드가 후회할 일을하기 전에 동기화되지 않은시기를 감지하는 방법을 알아야합니다.

그러나 경우에 따라 가용성의 위치에서 일관성을 희생 할 수 있습니다.

비즈니스 프로세스는 가용성이 더 중요하기 때문에 비즈니스 프로세스는 종종 생각보다 불일치에 대해 더 관대합니다.

그러나 마이크로 서비스에 분산 트랜잭션을위한 전략이 있는지 자문 해 보지만 비용이 너무 비쌀 수 있습니다. 나는 항상 훌륭한 기사 인 Martin Fowler와 CAP 정리로 2 센트를주고 싶었습니다 .


1
분산 비즈니스 트랜잭션에서 워크 플로 엔진 또는 신중하게 설계된 큐와 같은 오케스트레이션 계층을 추가하여 일관성을 해결하는 경우가 있습니다. 이 계층은 2 단계 커밋 및 롤백을 모두 처리하고 마이크로 서비스가 특정 비즈니스 로직에 집중할 수 있도록합니다. 그러나 CAP로 돌아가 가용성과 일관성에 투자하면 성능이 희생됩니다. 마이크로 서비스는 OOP 비즈니스를 구성하는 많은 분리 된 클래스와 어떻게 비교됩니까?
Greg

마이크로 서비스를 통해 RAFT를 사용하여 일관성 FWIW
f0ster

1
+1. 데이터의 모든 '풀 (pull)'뷰가 구체화 된 뷰로 구현 될 수 있다면 마이크로 서비스 컨텍스트 트랜잭션에서 왜 원하는지 궁금합니다. 예를 들어, 한 마이크로 서비스가 한 계정에서 다른 계정으로의 차변을 구현하는 경우, 잔고의 견해는 대변과 차변의 쌍만 고려할 것입니다. 여기에서 비교할 수없는 신용과 차변은 여전히 ​​구체화 된 뷰의 버퍼에 있습니다.
Sentinel

16

여기에 적어도 하나의 대답뿐만 아니라 웹의 다른 곳에서 제안한 것처럼 두 엔터티 간의 일관성이 필요한 경우 정상적인 트랜잭션 내에서 엔터티를 함께 유지하는 하나의 마이크로 서비스를 디자인 할 수 있습니다.

그러나 동시에 판매 레코드 및 주문 레코드 (판매를 수행하기 위해 무언가를 주문할 때)와 같이 엔티티가 실제로 동일한 마이크로 서비스에 속하지 않는 상황이있을 수 있습니다. 이러한 경우 두 마이크로 서비스 간의 일관성을 보장하는 방법이 필요할 수 있습니다.

전통적으로 분산 된 트랜잭션이 사용되었으며 내 경험상 잠금이 문제가되는 크기로 확장 될 때까지 잘 작동합니다. 상태 변경을 사용하여 관련 리소스 (예 : 판매중인 품목) 만 "잠금"상태가되도록 잠금을 완화 할 수 있지만, 여기에서 모든 자원을 구축해야하는 영역에 들어가기 때문에 까다로워지기 시작합니다. 데이터베이스가 직접 처리하지 말고 직접 작성하는 것이 좋습니다.

이 복잡한 문제를 처리하기 위해 자체 트랜잭션 프레임 워크를 구축하는 회사와 협력했지만 비용이 많이 들고 성숙하는 데 시간이 걸리기 때문에 권장하지 않습니다.

일관성을 유지하는 시스템에 볼트로 고정 할 수있는 제품이 있습니다. 비즈니스 프로세스 엔진은 좋은 예이다 그들은 일반적으로 일관성을 처리 결국 보상을 사용하여. 다른 제품도 비슷한 방식으로 작동합니다. 일반적으로 클라이언트 근처에 소프트웨어 계층이 생겨 일관성과 트랜잭션 및 실제 서비스 처리를위한 마이크로 서비스를 처리 합니다. 그러한 제품 중 하나는 Java EE 솔루션과 함께 사용할 수있는 일반 JCA 커넥터입니다 (투명도 : 저자입니다). 여기에서 제기 된 문제에 대한 자세한 내용과 자세한 내용 은 http://blog.maxant.co.uk/pebble/2015/08/04/1438716480000.html 을 참조 하십시오.

트랜잭션 및 일관성을 처리하는 또 다른 방법은 마이크로 서비스에 대한 호출을 메시지 큐와 같은 트랜잭션에 대한 호출로 랩핑하는 것입니다. 위에서 판매 레코드 / 주문 레코드 예제를 보자. 간단히 판매 마이크로 서비스가 주문 시스템에 메시지를 보내도록 할 수있다. 주문 시스템은 판매를 데이터베이스에 쓰는 동일한 트랜잭션에서 커밋된다. 결과는 확장 성이 매우 뛰어난 비동기 솔루션입니다 . 웹 소켓과 같은 기술을 사용하면 종종 비동기 솔루션의 확장과 관련된 차단 문제를 해결할 수 있습니다. 이 같은 패턴에 대한 더 많은 아이디어를 들어, 내 기사의 또 다른 볼 http://blog.maxant.co.uk/pebble/2015/08/11/1439322480000.html을 .

어떤 솔루션을 선택하든 시스템의 일부만 일관성이 필요한 내용을 작성한다는 점을 인식하는 것이 중요합니다. 대부분의 액세스는 읽기 전용 일 가능성이 높습니다. 따라서 트랜잭션 관리 기능을 시스템의 관련 부분에만 구축하여 확장 성을 유지하십시오.


그동안 프로세스의 각 단계가 완전히 거래되는 비동기 프로세스로 전환하는 것을 진지하게 고려해야한다고 말하고 싶습니다. 자세한 내용은 여기를 참조하십시오 : blog.maxant.co.uk/pebble/2018/02/18/1518974314273.html
Ant Kutschera

"위에서 판매 레코드 / 주문 레코드 예제를 작성하십시오. 판매 마이크로 서비스가 단순히 주문 시스템에 메시지를 보내도록 할 수 있습니다. 주문 시스템은 판매를 데이터베이스에 쓰는 동일한 트랜잭션에서 커밋됩니다." 제안하는 것이 기본적으로 분산 트랜잭션인지 확실하지 않습니다. 즉,이 경우 롤백 시나리오를 어떻게 처리 할 것입니까? 예를 들어 메시지가 메시지 큐에 커밋되었지만 DB 측에서 롤백되었습니다.
sactiw

@sactiw 내가 2 단계 커밋을 염두에 두 었는지 확실하지 않지만 지금은 피하고 대신 비즈니스 데이터를 작성하고 한 번의 트랜잭션으로 마이크로 서비스 DB에 대한 메시지를 큐에 추가해야한다는 사실 . 그런 다음 자동화 된 재시도 메커니즘을 사용하여 트랜잭션이 커밋 된 후 "명령"이라고도하는 "사실"이 비동기 적으로 처리됩니다. 예를 보려면 2018-03-10의 블로그 기사를 참조하십시오. 구현이 더 쉬우므로 가능하면 전달 전략에 찬성하여 롤백 또는 보상을 피하십시오.
앤트 Kutschera

1

마이크로 서비스에는 diff간에 일관성을 유지하는 세 가지 방법이 있습니다. 서비스:

  1. 오케스트레이션-서비스 간 트랜잭션 및 롤백을 관리하는 프로세스입니다.

  2. 안무-서비스는 서로간에 메시지를 전달하고 최종적으로 일관된 상태에 도달합니다.

  3. 하이브리드-위의 두 가지를 혼합합니다.

자세한 내용을 보려면 링크로 이동하십시오 : https://medium.com/capital-one-developers/microservices-when-to-react-vs-orchestrate-c6b18308a14c


1

내가 생각하는 것보다 더 많은 것을 타협하는 많은 솔루션이 있습니다. 물론 다른 은행간에 돈을 옮기는 것과 같이 유스 케이스가 복잡한 경우 더 유쾌한 대안이 불가능할 수 있습니다. 그러나 마이크로 서비스 사용이 데이터베이스 트랜잭션을 방해하는 일반적인 시나리오에서 수행 할 수있는 작업을 살펴 ​​보자.

옵션 1 : 가능한 경우 거래가 필요하지 않습니다

이전에 분명하고 언급되었지만 관리 할 수 ​​있다면 이상적입니다. 구성 요소가 실제로 동일한 마이크로 서비스에 속했습니까? 아니면 거래가 불필요하게되도록 시스템을 재 설계 할 수 있습니까? 거래가 아닌 것을 받아들이는 것이 가장 저렴한 희생 일 것입니다.

옵션 2 : 대기열 사용

다른 서비스가 원하는 방식으로 성공할 것이라는 확신이 충분하면 어떤 형태의 대기열을 통해이를 호출 할 수 있습니다. 대기중인 항목은 나중에 픽업하지 않지만 해당 항목 이 대기 중인지 확인할 수 있습니다 .

예를 들어 엔터티를 삽입하고 단일 트랜잭션으로 전자 메일을 보내려고한다고 가정합니다. 메일 서버를 호출하는 대신 이메일을 테이블에 대기시킵니다.

Begin transaction
Insert entity
Insert e-mail
Commit transaction

분명한 단점은 여러 마이크로 서비스가 동일한 테이블에 액세스해야한다는 것입니다.

옵션 3 : 트랜잭션을 완료하기 직전에 외부 작업을 마지막으로 수행

이 접근법은 트랜잭션 커밋이 실패 할 가능성이 거의 없다는 가정에 기반합니다.

Begin transaction
Insert entity
Insert another entity
Make external call
Commit transaction

쿼리가 실패하면 외부 호출이 아직 수행되지 않은 것입니다. 외부 호출이 실패하면 트랜잭션은 커밋되지 않습니다.

이 방법에는 외부 호출 을 번만 할 수 있다는 한계가 있으며 마지막에 수행해야합니다 (예 : 쿼리에서 결과를 사용할 수 없음).

옵션 4 : 보류 상태로 물건 만들기

여기 에 게시 된 것처럼 여러 마이크로 서비스가 서로 다른 구성 요소를 작성하며 각 구성 요소는 트랜잭션 상태가 아닌 보류 상태입니다.

모든 유효성 검사가 수행되지만 결정적인 상태에서는 아무것도 생성되지 않습니다. 모든 것이 성공적으로 생성되면 각 구성 요소가 활성화됩니다. 일반적으로이 작업은 매우 간단하고 문제가 발생할 가능성이 너무 작아서 비 거래 적으로 활성화하는 것을 선호 할 수도 있습니다.

가장 큰 단점은 아마도 보류중인 항목의 존재를 설명해야한다는 것입니다. 선택 쿼리는 보류중인 데이터를 포함할지 여부를 고려해야합니다. 대부분은 무시해야합니다. 그리고 업데이트는 또 다른 이야기입니다.

옵션 5 : 마이크로 서비스가 쿼리를 공유하도록하십시오

다른 옵션은 당신을 위해 그것을하지 않습니다? 그런 다음 unorthodox를 얻자 .

회사에 따라 허용되지 않을 수 있습니다. 알고 있어요 이것은 정통입니다. 허용되지 않으면 다른 경로로 이동하십시오. 그러나 이것이 상황에 맞으면 간단하고 강력하게 문제를 해결합니다. 가장 수용 가능한 타협 일 수 있습니다.

여러 마이크로 서비스의 쿼리를 간단한 단일 데이터베이스 트랜잭션으로 전환하는 방법이 있습니다.

쿼리를 실행하지 말고 반환하십시오.

Begin transaction
Execute our own query
Make external call, receiving a query
Execute received query
Commit transaction

네트워크별로 각 마이크로 서비스는 각 데이터베이스에 액세스 할 수 있어야합니다. 향후 확장과 관련하여이 점을 명심하십시오.

트랜잭션과 관련된 데이터베이스가 동일한 서버에있는 경우 이는 일반 트랜잭션입니다. 서로 다른 서버에있는 경우 분산 트랜잭션이됩니다. 코드는 관계없이 동일합니다.

연결 유형, 매개 변수 및 연결 문자열을 포함하여 쿼리를받습니다. 플로우를 읽을 수있는 상태로 깔끔한 실행 가능 Command 클래스로 마무리 할 수 ​​있습니다. 마이크로 서비스 호출은 트랜잭션의 일부로 실행되는 Command를 생성합니다.

연결 문자열은 원래 마이크로 서비스가 제공하는 것이므로 모든 의도와 목적을 위해 쿼리는 해당 마이크로 서비스에 의해 여전히 실행되는 것으로 간주됩니다. 우리는 단지 클라이언트 마이크로 서비스를 통해 물리적으로 라우팅하고 있습니다. 차이가 있습니까? 글쎄, 다른 쿼리와 같은 트랜잭션에 넣을 수 있습니다.

타협이 수용 가능한 경우,이 접근 방식은 마이크로 서비스 아키텍처에서 단일 응용 프로그램의 직접적인 트랜잭션 성을 제공합니다.


0

서비스 공간을 식별하는 문제 공간을 분해하는 것으로 시작하겠습니다 . 제대로 완료되면 서비스 간 트랜잭션이 필요하지 않습니다.

각기 다른 서비스에는 자체 데이터, 행동, 동기 부여 력, 정부, 비즈니스 규칙 등이 있습니다. 좋은 출발은 기업이 가지고있는 높은 수준의 기능을 나열하는 것입니다. 예를 들어, 마케팅, 영업, 회계, 지원. 또 다른 출발점은 조직 구조이지만, 어떤 이유로 (예를 들어 정치적) 최적의 비즈니스 분해 체계가 아닐 수 있다는 경고가 있다는 점에 유의하십시오. 보다 엄격한 접근 방식은 가치 사슬 분석 입니다. 귀하의 서비스에는 사람도 포함될 수 있으며, 소프트웨어는 아닙니다. 서비스는 이벤트 를 통해 서로 통신해야 합니다 .

다음 단계는 이러한 서비스를 개척하는 것입니다. 결과적으로 여전히 상대적으로 독립적 인 집계 를 얻습니다 . 일관성 단위를 나타냅니다. 다시 말해, 내부는 일관성 있고 ACID 여야합니다. 집계는 이벤트를 통해 서로 통신합니다.

도메인이 일관성을 먼저 요구한다고 생각되면 다시 생각하십시오. 이것을 염두에두고 설계된 크고 중요한 시스템은 없습니다. 그것들은 모두 분배되고 결국 일관성이 있습니다. Pat Helland의 클래식 용지를 확인하십시오 .

다음 은 분산 시스템을 구축하는 방법에 대한 실용적인 팁입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.