μλ¦Ό κΈ°λ₯
μ΄λ€ μ΄λ²€νΈκ° λ°μ νμ λ, μ¬μ©μμκ² μλ¦Όμ΄ κ°λλ‘νλ μλ¦Ό κΈ°λ₯μ μΆκ°νλ €κ³ νλ€.
μ¬μ©ν μ μλ νλ‘ν μ½
1. Polling
μ£ΌκΈ°μ μΌλ‘ μλ²μ μμ²νμ¬ λ³κ²½ λ΄μ©μ νμΈνλ ν΄λ§ λ°©μ. νμ§λ§ μ€μκ° ν΅μ μ΄ νμν κ²½μ° λΉν¨μ¨μ μ΄λ€.
μ€μκ° μμ²μ΄ μλκ³ μ ν΄μ§ μκ°(N)μ κΈ°μ€μΌλ‘ (μμ² -> μλ΅)μ λ°λ³΅νλ€.
Short Pollingν΄λΌμ΄μΈνΈκ° μλ²λ‘λΆν° μ κΈ°μ μΈ μ 보λ₯Ό λ°κΈ° μν νλ‘ν μ½μ΄λ€. κ³Όμ μ λ€μκ³Ό κ°λ€. 1) ν΄λΌμ΄μΈνΈκ° μλ²μ μλ‘μ΄ μ 보μ λν HTTP μμ²μ 보λΈλ€. 2) μλ²λ μλ‘μ΄ μ 보λ₯Ό λ°ννλ€. 3) ν΄λΌμ΄μΈνΈλ μ€μ ν μ£ΌκΈ°(time)λ‘ μμ²μ λ°λ³΅νλ€. μ£ΌκΈ°μ μΌλ‘ HTTP Reqeustλ₯Ό 보λ΄λ λ°©μμΌλ‘, μ€μκ°μ±μ΄ λ¨μ΄μ§κ³ μλ²μ λΆνμν λΆνλ₯Ό μ€ μ μλ€. TCPμ 컀λ₯μ μ λ§Ίκ³ λλ κ² μμ²΄κ° λ¬΄κ²κΈ° λλ¬Έμ μ€μκ°μΌλ‘ λ³νλλ λΉ λ₯Έ μ 보μ μλ΅μ κΈ°λνκΈ° νλ€λ€.Long PollingShort Polling 보λ€λ λ ν¨μ¨μ μΈ λ°©μμ΄λ€. μ΄λ²€νΈκ° λ°μν λκΉμ§ μ°κ²°μ μ μ§νλ λ°©μμΌλ‘ μ¬μ ν μλ‘μ΄ μ°κ²°μ μμ£Ό μ€μ ν΄μΌ νλ€. 1) ν΄λΌμ΄μΈνΈκ° μλ²μ μμ²μ 보λΈλ€. μλ²μ μλ‘μ΄ μ΄λ²€νΈκ° λ°μν λκΉμ§ κΈ°λ€λ¦°λ€. 2) μλ²λ μ΄λ²€νΈ μ 보λ₯Ό λ°ννλ€. 3) ν΄λΌμ΄μΈνΈλ μλ΅μ λ°κ³ μμ²νλ€. Shor Pollingμ λΉν΄ νμν μμ² μλ₯Ό μ€μΌ μ μλ€. νμ§λ§ μλ²λ‘λΆν° μλ΅μ λ°κ³ λλ©΄ λ€μ μ°κ²° μμ²μ ν΄μΌνκΈ° λλ¬Έμ λΉλ²νκ² μ°κ²° μμ²μ νκ²λλ©΄ μλ²μ λΆλ΄μ΄ κ°λκ²μ λμΌνλ€. λ°λΌμ μ΄ λ°©μμ μλ²μ μνκ° μ£ΌκΈ°μ μΌλ‘ λ³νμ§ μλ κ²½μ°μ μ ν©νλ€.
2. Web Socket
μΉ μμΌμ OSI 4κ³μΈ΅ νλ‘ν μ½μΈ TCPμ κΈ°λ°ν μλ°©ν₯ λ©μμ§ μ λ¬ νλ‘ν μ½μ΄λ€. κΈ°μ‘΄μ HTTP μμ²-μλ΅ κ΅¬μ‘°μ λ¬λ¦¬, μ§μμ μΌλ‘ μ°κ²°μ μ μ§νλ©° μλ²μ ν΄λΌμ΄λνΈ κ° μ¦κ°μ μΈ λ°μ΄ν° κ΅νμ κ°λ₯νκ² νλ€. μ΄λ‘μΈν΄ HTTPλ³΄λ€ μ€λ²ν€λκ° μ κ³ λ°μ΄ν° μ μ‘ μλκ° λΉ λ₯΄λ€.
μΉμμΌμ μ€μκ° μλ°©ν₯ ν΅μ μ΄ νμν μ ν리μΌμ΄μ μ μ ν©νλ©°, νΉν λ©ν°νλ μ΄μ΄ κ²μ, μ€μκ° νμ λꡬ, λΌμ΄λΈ μ±ν λ±κ³Ό κ°μ΄ μ§μμ μ΄κ³ λΉ λ₯Έ λ°μ΄ν° μ λ°μ΄νΈκ° μꡬλλ κ²½μ°μ μ μ©νλ€. Pollingμ΄λ Server-Sent Events(SSE)κ° μΆ©λΆνμ§ μμ μν©μμ μΉμμΌ μ¬μ©μ κ³ λ €ν μ μλ€. νμ§λ§ ꡬνμ΄ μλμ μΌλ‘ 볡μ‘ν μ μλ€.
3. SSE (Server Sent Events)
SSEλ μλ²μμ ν΄λΌμ΄μΈνΈλ‘μ λ¨λ°©ν₯ μ€μκ° ν΅μ μ μν κΈ°μ λ‘ HTTP νλ‘ν μ½μ μ¬μ©νμ¬ κ΅¬ννλ€. μΆκ°μ μΈ λΌμ΄λΈλ¬λ¦¬λ νν μ½μ΄ νμνμ§λ μλ€. μ°κ²°μ΄ λμ΄μ§λ©΄ μλ μ¬μ°κ²° κΈ°λ₯μ μ 곡νλ©°, ꡬνμ΄ κ°λ¨νκ³ λλΆλΆμ λΈλΌμ°μ μμλ μ§μνλ€. λν HTTPμ Persistence Connectionμ μ΄μ©ν κΈ°μ μ΄κΈ° λλ¬Έμ μ¬λ¬ HTTP μμ²κ³Ό μλ΅μ ν λ²μ TCP μ°κ²°λ‘ μ²λ¦¬ν μ μλ€. (μ§μμ μΌλ‘ λ°μ΄ν°λ₯Ό μ€νΈλ¦¬λ° ν μ μμ)
κ³Όμ μ λ€μκ³Ό κ°λ€. 1) ν΄λΌμ΄μΈνΈλ μλ²λ₯Ό ꡬλ νλ€. (SSE Connectionμ λ§Ίμ) 2) μλ²λ μ΄λ²€νΈκ° λ°μν λλ§λ€ ꡬλ λ ν΄λΌμ΄μΈνΈμκ² λ°μ΄ν°λ₯Ό μ μ‘νλ€.
SSEλ λ¨λ°©ν₯ ν΅μ (μλ² -> ν΄λΌμ΄μΈνΈ)μ΄κΈ° λλ¬Έμ μλ°©ν₯μ΄ νμνλ©΄ WebSocketμ κ³ λ €ν΄λ³Έλ€.
SSE μ±ν μ΄μ
μ±νν κ°μ₯ ν° μ΄μ λ λ¨λ°©ν₯ ν΅μ μ΄κΈ° λλ¬Έμ΄λ€. ꡬννλ €λ μλ¦Ό κΈ°λ₯μ μλ²μμ ν΄λΌμ΄μΈνΈλΌλ νΉμ±μ΄ μμ΄μ, ν΄λΌμ΄μΈνΈκ° μλ²μ μμ²μ λ³΄λΌ νμκ° μμ΄ μ€μκ° λ©μμ§ μ μ‘μλ λ§€μ° μ ν©νλ€. μ΄λ¬ν νΉμ±μΌλ‘ ν μν©μμλ Web Sockect 보λ€λ SSEκ° λ μ ν©νκ³ νλ¨νλ€.
νμ§λ§ SSEμλ μ¬λ¬ λ¨μ λ€μ μ‘΄μ¬νλ€. ν΄λΌμ΄μΈνΈμ HTTP μ°κ²°μ μ§μμ μΌλ‘ μ μ§ν΄μΌ νλ―λ‘, λμμ λ§μ γ λ κ²°μ΄ λ°μν κ²½μ° μλ²μ λΆν κ΄λ¦¬κ° λ¬Έμ κ° λ μ μλ€. λν, λλμ ν΄λΌμ΄μΈνΈμκ² λμμ λ°μ΄ν°λ₯Ό μ μ‘νλ κ²½μ°μλ μ±λ₯ λ¬Έμ λ κ³ λ―Όν΄ λ΄μΌνλ μμ μ€ νλμΌ κ²μ΄λ€.
SseEmitter κ°μ²΄
ꡬνμ μμ Spring Framework 4.2λΆν° SSE ν΅μ μ μ§μνλ SSeEmitter ν΄λμ€κ° μΆκ°λκ³ , Spring 5λΆν°λ webfluxλ₯Ό μ΄μ©ν΄μ SSE ν΅μ λ ν μ μλ€.
λμ Flow
λ¨Όμ μ μ λ₯Ό subscribe(Long userId) ꡬλ ν΄μΌνλ€.
컨νΈλ‘€λ¬μμ header("lastEventId")λ₯Ό λ°κ³ Serviceμ subscribe() μΈμλ‘ μ λ¬νλ€.
SseEmitter κ°μ²΄λ₯Ό μμ±ν΄μ μ μ₯μμ μ μ₯μν€κ³ νμν λλ§λ€ μ μ μ λ§€μΉλλ SseEmitterλ₯Ό λΆλ¬μμ μ΄λ²€νΈμ λν μλ΅μ μ μ‘ν΄μ€λ€.
ꡬν
β
EmitterRepositoryImpl
μ΄λ²€νΈκ° λ°μνλ©΄ userIdλ‘ SseEmitter κ°μ²΄λ₯Ό μμ±νλ€. κ·Έκ²μ μ μ₯νκ³ κ΄λ¦¬νκΈ° μν μ μ₯μμ΄λ€. (μλ¦Ό μ‘°ν, μμ ..etc)
β‘οΈ ConcurrentHashMap
Thread-safeν Map, λμμ μ¬λ¬ μ€λ λκ° μ κ·ΌνλλΌλ λ°μ΄ν°λ₯Ό μ‘°μν μ μλλ‘ λ³΄μ₯νλ€.
[μ°Έκ³ ] https://pplenty.tistory.com/17
β‘οΈ saveEventCache
νΉμλ μ°κ²°μ΄ λμ΄μ‘μ κ²½μ° μ΄λ²€νΈκ° λ°μν΄μ ν΄λΌμ΄μΈνΈμκ² μ μ‘μ΄ κ°μ§ μλλ€λ©΄ μ¬κΈ°μ μ μ₯νκ³ λ€μ ꡬλ
ν λ μ μ‘ν μ μλλ‘ νλ€. SseEmitter κ°μ²΄μ μ μ€μ λ§κΈ° μν¨μ΄λ€.
β
SubscribeService
onCompletion()λ©μλλ SSE ν΅μ μ΄ μ±κ³΅μ μΌλ‘ μλ£λλ©΄ νΈμΆλλ€. μ¦, ν΄λΌμ΄μΈνΈμμ μ°κ²°μ΄ μ μμ μΌλ‘ μ’ λ£λ¨μ μλ―Ένλ€. ν΅μ μ΄ μ’ λ£λ ν μΆκ°μ μΈ μμ μ μννκ±°λ 리μμ€λ₯Ό μ 리ν μ μλ€.onTimeout()μ μμμ μ€μ ν μκ°, SSE ν΅μ μ΄ νμμμ λμμ λ νΈμΆλλ€. νμμμμ ν΄λΌμ΄μΈνΈκ° μΌμ μκ° λμ μλ΅μ λ°μ§ λͺ»ν κ²½μ° μ°κ²°μ΄ μ’ λ£λ μνλ₯Ό μκΈ°νλ€. μ¦, μ°κ²° μκ°μ΄ λ§λ£λλ©΄ μΆκ° μμ μ μννκ±°λ μμΈ μ²λ¦¬λ₯Ό ν μ μλ€.onCompletion(), onError()λ©μλλ μ’ λ£ μνμ νΈλ¦¬κ±°μ΄λ€. λ μ€ νλκ° νΈλ¦¬κ±° λ νμλ μλ¦Όμ΄ μ μ‘λμ§ μλλ€.
β‘οΈ Mapμ μ μ₯ν Key(String) κ°μ μ ꡬλΆμμ μκ°μΌλ‘ μ‘°ν©ν κΉ? Last-Event-IDλ ν€λμ λ΄κ²¨μ Έ μ€λ κ°μΌλ‘ μ΄μ μ λ°μ§ λͺ»ν μ΄λ²€νΈκ° μ‘΄μ¬νλ κ²½μ° (SSE μ°κ²°μ λν μκ° λ§λ£ νΉμ μ’ λ£)λ λ°μ λ§μ§λ§ μ΄λ²€νΈ ID κ°μ λ겨 κ·Έ μ΄νμ λ°μ΄ν°(λ°μ§ λͺ»ν λ°μ΄ν°) λΆν° λ°μ μ μκ² ν λ νμν κ°μ΄λ€.
β‘οΈ connectNotification()
SseEmitterμ μ ν¨ μκ°(ν΄λΌ-μλ² μ°κ²° μκ°) λμ μ΄λ λ°μ΄ν°λ μ μ‘λμ§ μλλ€λ©΄ 503 μν μ½λλ₯Ό λ°ννλ€.
μ΄μ λν λ°©μμΌλ‘ 맨 μ²μ μ°κ²°νκ² λλ©΄ Connection Message(λλ―Έ λ°μ΄ν°)λ₯Ό 보λ΄μ μ΄λ₯Ό λ°©μ§νλ€.
β
SendNotificationService
β‘οΈ sendLostData()
λ―Έμμ ν λ°μ΄ν°λ₯Ό μ μ‘νλ λ©μλ. ν΄λΌμ΄μΈνΈκ° λ―Έμμ ν μ΄λ²€νΈ λͺ©λ‘μ΄ μ‘΄μ¬ν κ²½μ°, subScribe ν λ ν΄λΌμ΄μΈνΈμκ² μ μ‘μμΌ μ μ€μ λ°©μ§νλ€.
μ΄ μΈμλ μλ¦Όμ μ½λ μλΉμ€, μμ , μ 체 μ‘°ννλ μλΉμ€λ€μ μΆκ°λ‘ ꡬννλ€.
β
Controller
μκ°ν΄μΌ ν λΆλΆ
Repositoryμμ MapμΌλ‘ SseEmitter κ°μ²΄λ₯Ό μ μ₯ν λ scale-outλ νκ²½μμ μ λμν κΉ?
Mapμ μ μ₯λ μλ£λ₯Ό entrySet()μΌλ‘ λλ©΄μ λΉλ²νκ² μ΄λ²€νΈκ° λ°μνλ€λ©΄ λ§€λ² O(N)μΌλ‘ νμμ μ§νν΄μΌ νλλ° μ±λ₯μ μΈ λ¬Έμ κ° λ°μνμ§ μμκΉ?
HTTP 2.0μ μ μ© νλκ²μ μ΄λ¨κΉ?
Nginxμμ λ°μνλ λ¬Έμ μ λ€
Last updated