저자의 생각에 메시지 큐 , 캐시 , 하위 데이터베이스 및 하위 테이블 은 동시성 솔루션의 세 검객입니다.
저는 경력을 쌓으면서 ActiveMQ, RabbitMQ, Kafka 및 RocketMQ와 같은 잘 알려진 메시지 대기열을 사용해 왔습니다.
이 기사에서 저자는 자신의 실제 경험을 결합하여 메시지 대기열의 7가지 전형적인 애플리케이션 시나리오를 여러분과 공유합니다.
1 비동기식 및 디커플링
저자는 한때 전자상거래 업체에서 사용자 등록, 조회, 수정 등 기본적인 기능을 제공하는 사용자 서비스를 담당한 적이 있다. 사용자가 성공적으로 등록한 후에는 사용자에게 문자 메시지를 보내야 합니다.
그림에서 신규 사용자 추가 와 문자 메시지 전송은 모두 사용자 센터 서비스에 포함되어 있습니다. 이 방법의 단점은 매우 분명합니다.
-
SMS 채널은 충분히 안정적이지 않으며 문자 메시지를 보내는 데 약 5초가 걸립니다. 이로 인해 사용자 등록 인터페이스에 시간이 많이 걸리고 프런트엔드 사용자 경험에 영향을 미칩니다.
-
SMS 채널 인터페이스가 변경되면 사용자 센터 코드를 수정해야 합니다. 하지만 User Center가 핵심 시스템입니다. 온라인에 접속할 때마다 조심해야 합니다. 핵심이 아닌 기능이 핵심 시스템에 영향을 미치기 때문에 이는 매우 어색하게 느껴집니다.
이 문제를 해결하기 위해 작성자는 메시지 큐를 사용하여 재구성했습니다.
-
비동기식
사용자 센터 서비스는 사용자 정보를 성공적으로 저장한 후 메시지 대기열로 메시지를 보내고 즉시 결과를 프런트 엔드에 반환합니다. 이렇게 하면 시간이 오래 걸리고 사용자 경험에 영향을 미치는 문제를 피할 수 있습니다.
-
디커플링
작업 서비스가 메시지를 받으면 SMS 서비스를 호출하여 SMS를 보냅니다. 이는 핵심 서비스와 비핵심 기능을 분리하고 시스템 간의 결합을 크게 줄입니다.
2 피크 제거
동시성이 높은 시나리오에서는 요청이 갑자기 급증하면 시스템이 쉽게 불안정해질 수 있습니다. 예를 들어, 데이터베이스에 액세스하려는 요청이 많으면 데이터베이스에 큰 부담이 가해지거나 시스템 리소스, CPU 및 IO에 병목 현상이 발생할 수 있습니다. .
저자는 승객의 주문 수명 주기 동안 주문 캐시를 먼저 수정한 후 주문 배치 서비스에서 메시지를 소비하고 주문 여부를 결정합니다. 정보가 정상적(예: 고장 여부)인 경우 주문 데이터가 정확하면 데이터베이스에 저장됩니다.
요청 피크에 직면할 때 소비자의 동시성은 임계 범위 내에 있고 소비 속도는 상대적으로 균일하므로 실제로 직면하는 주문 시스템의 생산자는 데이터베이스에 큰 영향을 미치지 않습니다. 프런트 엔드도 더욱 안정적이 될 것입니다.
3개의 메시지 버스
소위 버스는 마더보드의 데이터 버스와 같으며 데이터를 전송하고 상호 작용할 수 있습니다. 당사자는 직접 통신하지 않으며 버스를 표준 통신 인터페이스로 사용합니다 .
저자는 한때 복권 회사의 주문 팀에서 복권 주문의 수명주기 동안 생성, 하위 주문 분할, 티켓 발행 및 상금 계산과 같은 여러 단계를 거쳤습니다. 각 링크에는 서로 다른 서비스 처리가 필요하고, 각 시스템에는 자체 독립 테이블이 있으며, 비즈니스 기능은 상대적으로 독립적입니다. 모든 애플리케이션이 주문 마스터 테이블의 정보를 수정해야 한다면 상당히 혼란스러울 것입니다.
따라서 회사의 설계자는 <font color="red"> 발송 센터 </font> 서비스를 설계했습니다 . 발송 센터는 주문 정보를 유지하지만 하위 서비스와 통신하지 않고 메시지 대기열 및 티켓팅 게이트웨이를 통해 통신합니다. 상금계산 서비스 등 정보를 전송하고 교환하는 서비스입니다.
메시지 버스의 아키텍처 설계를 통해 시스템을 더욱 분리할 수 있으며 각 시스템이 자체 임무를 수행할 수 있습니다.
4 지연된 작업
사용자가 Meituan 앱에서 주문하고 즉시 결제하지 않은 경우, 주문 세부정보 입력 시 카운트다운이 표시됩니다. 결제 시간이 초과되면 주문이 자동으로 취소됩니다.
매우 우아한 방법은 메시지 대기열에서 지연된 메시지를 사용하는 것 입니다 .
주문 서비스는 주문을 생성한 후 지연된 메시지를 메시지 대기열로 보냅니다. 메시지 큐는 메시지가 결제 만료 시간에 도달하면 소비자에게 메시지를 전달하며, 소비자가 메시지를 받은 후 주문 상태가 결제되었는지 확인하면 주문 취소 로직이 실행됩니다.
지연된 메시지를 보내는 RocketMQ 4.X 생산자의 코드는 다음과 같습니다.
Message msg = new Message();
msg.setTopic("TopicA");
msg.setTags("Tag");
msg.setBody("this is a delay message".getBytes());
//设置延迟level为5,对应延迟1分钟
msg.setDelayTimeLevel(5);
producer.send(msg);
RocketMQ 4.X 버전은 기본적으로 18단계의 지연된 메시지를 지원하며, 이는 브로커 측의 messageDelayLevel 구성 항목에 의해 결정됩니다.
RocketMQ 5.X 버전은 언제든지 메시지 지연을 지원합니다. 클라이언트는 메시지 구성 시 지연 시간 또는 타이밍 시간을 지정하는 3가지 API를 제공합니다.
5 라디오 소비
브로드캐스트 소비 : 브로드캐스트 소비 모드를 사용하는 경우 각 메시지는 클러스터의 모든 소비자에게 푸시되어 각 소비자가 메시지를 한 번 이상 소비하도록 합니다.
브로드캐스트 소비는 주로 메시지 푸시 와 캐시 동기화 라는 두 가지 시나리오에서 사용됩니다 .
01 메시지 푸시
아래 그림은 개인 차량의 운전자 측 푸시 메커니즘을 보여줍니다. 사용자가 주문한 후 주문 시스템은 관련 알고리즘을 기반으로 운전자에게 주문을 발송하고 운전자는 이를 전달합니다. -end는 발송 푸시 메시지를 수신합니다.
푸시 서비스는 TCP 서비스(사용자 정의 프로토콜)이며 메시지 모드는 브로드캐스트 소비입니다.
드라이버가 드라이버 앱을 연 후 앱은 로드 밸런싱 및 푸시 서비스를 통해 긴 연결을 생성하고 푸시 서비스는 TCP 연결 참조(예: 드라이버 번호 및 TCP 채널 참조)를 저장합니다.
디스패치 서비스는 생산자이며 디스패치 데이터를 MetaQ로 보냅니다. 각 푸시 서비스는 드라이버의 TCP 채널이 로컬 메모리에 있는지 여부를 확인하고 데이터를 서버로 푸시합니다. 드라이버 측을 통해.
02 캐시 동기화
동시성이 높은 시나리오에서는 많은 애플리케이션이 로컬 캐시를 사용하여 시스템 성능을 향상시킵니다.
로컬 캐시는 HashMap, ConcurrentHashMap 또는 캐싱 프레임워크 Guava Cache 또는 Caffeine 캐시일 수 있습니다.
위 그림과 같이 애플리케이션 A가 시작된 후 RocketMQ 소비자로서 메시지 모드가 브로드캐스트 소비로 설정됩니다. 인터페이스 성능을 향상시키기 위해 각 애플리케이션 노드는 사전 테이블을 로컬 캐시에 로드합니다.
사전 테이블 데이터가 변경되면 비즈니스 시스템을 통해 RocketMQ로 메시지가 전송될 수 있으며, 각 애플리케이션 노드는 메시지를 소비하고 로컬 캐시를 새로 고칩니다.
6 분산 트랜잭션
전자상거래 거래 시나리오를 예로 들면, 주문에 대한 사용자 결제 의 핵심 작업 에는 다운스트림 물류 배송, 포인트 변경, 장바구니 상태 삭제 등 여러 하위 시스템의 변경도 포함됩니다.
1. 기존 XA 트랜잭션 솔루션: 성능 부족
위 네 가지 분기의 실행 결과의 일관성을 보장하기 위해 일반적인 솔루션은 XA 프로토콜을 기반으로 하는 분산 트랜잭션 시스템으로 구현됩니다. 4개의 호출 분기를 4개의 독립적인 트랜잭션 분기를 포함하는 대규모 트랜잭션으로 캡슐화합니다. XA 분산 트랜잭션 기반 솔루션은 비즈니스 처리 결과의 정확성을 충족할 수 있지만, 다중 분기 환경에서는 다운스트림 분기 수가 증가함에 따라 리소스 잠금 범위가 크고 동시성이 낮다는 것이 가장 큰 단점입니다. 시스템 성능은 점점 더 나빠질 것입니다.
2. 일반적인 메시지 체계 기반: 일관성 보장 어려움
이 솔루션에서는 메시지 다운스트림 분기와 주문 시스템 변경의 기본 분기가 불일치하기 쉽습니다. 예를 들면 다음과 같습니다.
- 메시지가 성공적으로 전송되었지만 주문이 성공적으로 실행되지 않았으므로 전체 트랜잭션을 롤백해야 합니다.
- 주문은 성공적으로 실행되었지만 메시지가 성공적으로 전송되지 않았으며 불일치를 발견하려면 추가 보상이 필요했습니다.
- 메시지 전송 시간 초과를 알 수 없으며 주문을 롤백해야 하는지 또는 주문 변경 사항을 제출해야 하는지 판단할 수 없습니다.
3. RocketMQ 분산 트랜잭션 메시지 기반: 최종 일관성 지원
위에서 언급한 일반 메시지 솔루션에서 일반 메시지와 주문 트랜잭션의 일관성을 보장할 수 없는 이유는 본질적으로 일반 메시지가 독립형 데이터베이스 트랜잭션처럼 커밋, 롤백 및 통합 조정 기능을 가질 수 없기 때문입니다.
RocketMQ를 기반으로 구현된 분산 트랜잭션 메시지 기능은 일반 메시지 기반의 2단계 제출 기능을 지원합니다. 2단계 제출을 로컬 트랜잭션에 바인딩하여 전역 제출 결과의 일관성을 유지합니다.
RocketMQ 트랜잭션 메시지는 분산 시나리오에서 메시지 생성 및 로컬 트랜잭션의 최종 일관성을 보장하도록 지원합니다 . 상호 작용 프로세스는 아래 그림에 나와 있습니다.
1. 생산자는 메시지를 브로커에게 보냅니다.
2. 브로커는 메시지를 성공적으로 지속한 후 메시지가 성공적으로 전송되었음을 확인하기 위해 Ack를 생성자에게 반환합니다. 이때 메시지는 " 임시 배달 불가 " 로 표시됩니다 . 거래 메시지 .
3. 생산자는 로컬 트랜잭션 로직을 실행하기 시작합니다 .
4. 생산자는 로컬 트랜잭션 실행 결과를 바탕으로 2차 확인 결과 (Commit 또는 Rollback)를 서버에 제출합니다. 브로커는 확인 결과를 받은 후 처리 로직은 다음과 같습니다.
- 두 번째 확인 결과는 Commit입니다. 브로커는 반트랜잭션 메시지를 전달 가능한 것으로 표시하고 이를 소비자에게 전달합니다.
- 두 번째 확인 결과는 롤백입니다. 브로커는 트랜잭션을 롤백하고 반 트랜잭션 메시지를 소비자에게 전달하지 않습니다.
5. 네트워크 연결이 끊기거나 생산자 애플리케이션이 다시 시작되는 특별한 상황에서 브로커가 보낸 사람이 제출한 2차 확인 결과를 받지 못하거나 브로커가 받은 2차 확인 결과가 알 수 없음 상태인 경우, 일정 시간이 지난 후 터미널은 메시지 생성자, 즉 생성자 클러스터의 생성자 인스턴스로부터 메시지 검토를 .
- 생산자는 메시지 검토를 받은 후 메시지에 해당하는 로컬 트랜잭션 실행의 최종 결과를 확인해야 합니다.
- 생산자는 확인된 로컬 트랜잭션의 최종 상태를 기반으로 2차 확인을 다시 제출하고 , 서버는 여전히 4단계에 따라 절반 트랜잭션 메시지를 처리합니다.
7 데이터 전송 허브
지난 10년 동안 KV 스토리지(HBase), 검색(ElasticSearch), 스트리밍 처리(Storm, Spark, Samza), 시계열 데이터베이스(OpenTSDB) 및 기타 특수 시스템과 같은 특수 시스템이 등장했습니다. 이러한 시스템은 단일 목표를 염두에 두고 만들어졌으며 단순성을 통해 상용 하드웨어에 분산 시스템을 구축하는 것이 더 쉽고 비용 효율적입니다.
동일한 데이터 세트를 여러 특수 시스템에 주입해야 하는 경우가 많습니다.
예를 들어, 애플리케이션 로그를 오프라인 로그 분석에 사용하는 경우 개별 로그 레코드를 검색하는 것도 필수적입니다. 각 유형의 데이터를 수집한 다음 메시지 큐 Kafka를 사용하여 이를 자체 전용 시스템으로 가져오는 독립적인 워크플로를 구축하는 것은 분명히 비현실적입니다. 데이터 전송 허브 역할을 하며 동일한 데이터를 다른 전용 시스템으로 가져올 수 있습니다.
로그 동기화는 주로 로그 수집 클라이언트, Kafka 메시지 큐 및 백엔드 로그 처리 애플리케이션의 세 가지 주요 부분으로 구성됩니다.
- 로그 수집 클라이언트는 다양한 사용자 애플리케이션 서비스의 로그 데이터를 수집하는 역할을 담당하며, 로그를 "일괄" 및 "비동기식"으로 메시지 형식으로 Kafka 클라이언트에 보냅니다. Kafka 클라이언트는 메시지를 일괄적으로 제출하고 압축하므로 애플리케이션 서비스 성능에 거의 영향을 미치지 않습니다.
- Kafka는 로그를 메시지 파일에 저장하여 지속성을 제공합니다.
- Logstash와 같은 로그 처리 애플리케이션은 Kafka에서 로그 메시지를 구독하고 소비하며, 결국 파일 검색 서비스가 로그를 검색하거나 Kafka가 체계적인 저장 및 분석을 위해 Hadoop과 같은 다른 빅데이터 애플리케이션에 메시지를 전달합니다.
제 글이 도움이 되셨다면 좋아요 , 읽어주시고 전달해 주세요 . 여러분의 지지가 제가 더 좋은 글을 만드는 데 큰 힘이 됩니다. 정말 감사합니다!