Flutter

| Combine | 10. Managing Backpressure

flutter developer 2024. 5. 14. 15:38

백프레셔 관리는 주로 백엔드에서 하지만 Combine 프레임워크를 활용해서 iOS 단에서도 관리를 해줄 수 있다. 여기서 백프레셔란 서버로부터 날라오는 데이터 속도를, 컴바인으로 한정지으면 업스트림에서 날라오는 데이터 속도를 다운스트림의 처리속도가 따라가지 못해 발생하는 문제로 예시 상황으로는 아래와 같다.

  1. 비동기 데이터 로딩
    iOS 앱에서 외부 API나 데이터베이스로부터 비동기적으로 대량의 데이터를 로드할 때, 네트워크 상태나 데이터베이스의 응답 속도에 따라 데이터가 너무 빨리 도착하면, 앱의 메인 스레드가 블록되거나 UI가 느려질 수 있다. 
  2. 이벤트 스트리밍
    실시간으로 많은 양의 이벤트를 처리해야 하는 앱(예: 금융 앱에서의 주식 가격 업데이트, 스포츠 앱에서의 실시간 점수 업데이트)에서 데이터 스트림의 백프레셔를 관리하지 못하면 앱이 느려지거나, 최악의 경우 응답하지 않을 수 있다.

  3. 대용량 파일 다운로드
    사용자가 대용량 파일을 다운로드하거나 동영상을 스트리밍할 때, 네트워크 상태와 장치의 저장 공간 관리가 백프레셔 문제와 직접적으로 관련될 수 있다. 다운로드 속도가 장치의 저장 처리 속도를 초과하면 문제가 발생할 수 있다.

 

이러한 문제 상황이 발생할 경우 컴바인의 Subscribers.Demand (구독자가 퍼블리셔에게 요청 항목 수 조절) 을 통해 백프레셔를 관리해줄 수 있다.

// 예제 실행을 위한 함수
func example(of description: String, action: () -> Void) {
    print("\n--- Example of:", description, "---")
    action()
}

// 첫 번째 예제: 일반적인 sink 사용
example(of: "Backpressure Issue") {
    let publisher = (1...10).publisher
    let startTime = Date()
    
    publisher
        .sink(receiveCompletion: { completion in
            let processTime = Date().timeIntervalSince(startTime)
            print("Completed with: \(completion) after \(processTime) seconds")
        }, receiveValue: { value in
            let processTime = Date().timeIntervalSince(startTime)
            print("Received value \(value) at \(processTime) seconds")
            sleep(1) // 데이터 처리 시간
        })
}

// 두 번째 예제: Subscriber를 사용한 백프레셔 관리
example(of: "Handling Backpressure with IntSubscriber") {
    final class IntSubscriber: Subscriber {
        typealias Input = Int
        typealias Failure = Never
        var startTime = Date()

        func receive(subscription: Subscription) {
            subscription.request(.max(1)) // 초기 데이터 요청
            startTime = Date() // 시작 시간 초기화
        }

        func receive(_ input: Int) -> Subscribers.Demand {
            let processTime = Date().timeIntervalSince(startTime)
            print("Received value \(input) at \(processTime) seconds")
            if input % 2 == 0 {
                sleep(2) // 짝수일 때는 처리 시간을 더 길게
                return .max(2) // 더 많은 데이터 요청
            } else {
                sleep(1) // 홀수일 때는 기존과 동일
                return .max(1) // 기본 데이터 요청
            }
        }

        func receive(completion: Subscribers.Completion<Never>) {
            let processTime = Date().timeIntervalSince(startTime)
            print("Completed with: \(completion) after \(processTime) seconds")
        }
    }

    let publisher = (1...10).publisher
    let subscriber = IntSubscriber()
    publisher.subscribe(subscriber)
}

 

예제 코드에서는 일부러 sleep 을 실행시켜 인위적으로 처리 시간을 지연시켰다.  2초간 지연됨에도 1초간 데이터가 계속 날라오고 있기 때문에 BackPressure 관리를 위해 max(2)개씩 데이터를 요청함으로써 프론트단에서 부족한 속도를 보완해주었다.


https://www.kodeco.com/21773708-intermediate-combine/lessons/3

 

Intermediate Combine, Episode 3: Managing Backpressure

The Subscriber protocols allows developers to define the maximum amount of data that subscriber will accept each time it receives a new value from the Publisher, allowing you to dynamically manage backpressure. However, the adjustment is additive, and must

www.kodeco.com