
สารบัญ
- ทำไมต้อง Push Notification แบบ Realtime?
- รู้จัก SSE และ WebSocket
- สแตกเทคโนโลยีที่ใช้
- เขียน SSE Backend (Spring WebFlux)
- เขียน WebSocket Backend (Spring WebSocket)
- โค้ด Frontend รับข้อความ
- การ Deploy & Scaling
- เปรียบเทียบ SSE กับ WebSocket
- Security & Best Practices
- Monitoring & Benchmark
- FAQ
- สรุป
1. ทำไมต้อง Push Notification แบบ Realtime?
ประสบการณ์ผู้ใช้ยุคปัจจุบันคาดหวังข้อมูล “เด้ง” มาทันทีไม่ต้องรีเฟรช ไม่ว่าจะเป็น ออเดอร์ใหม่, ข้อความแชท, ราคาหุ้น หรือ สถานะงาน หากยังใช้วิธี polling (ไถวนถามเซิร์ฟเวอร์ทุก X วินาที) นอกจากสิ้นเปลืองแบนด์วิธยังเพิ่มโหลด CPU แบบไร้สาระ Server-Sent Events (SSE) และ WebSocket จึงกลายเป็นรากฐานของแอป Realtime สมัยใหม่

2. รู้จัก SSE และ WebSocket
2.1 Server-Sent Events (SSE)

- เชื่อมต่อ HTTP ที่ฝั่ง Client เปิดค้างไว้ (long-lived connection)
- เหมาะกับ “one-way stream” – server ➜ client
- เบา (ใช้ text/event-stream) ไม่ต้องทำ handshake พิเศษ
- Reconnect อัตโนมัติด้วย header
Last-Event-ID
2.2 WebSocket

- เปลี่ยน HTTP/1.1 เป็น
wss://บน TCP ซึ่งรับ-ส่งสองทาง - Frame binary/text ส่งได้ทั้ง server➜client และ client➜server
- เหมาะกับแอปแชท เกมออนไลน์ หรือ collaborative editing
- ต้องมี load balancer ที่รองรับ sticky-session / L4
3. สแตกเทคโนโลยีที่ใช้
- Java 17+ + Spring Boot 3.5.x
- Spring WebFlux (Reactive) สำหรับ SSE
- Spring WebSocket + STOMP สำหรับ WebSocket
- Frontend สาธิตด้วย Vanilla JS & Vite
- Database ทดลอง: Redis Stream เป็น event-source
4. เขียน SSE Backend (Spring WebFlux)
4.1 Controller + Event Flux
@RestController
@RequestMapping("/api")
@RequiredArgsConstructor
public class SseController {
private final Sinks.Many<ServerSentEvent<String>> sink =
Sinks.many().multicast().onBackpressureBuffer();
@GetMapping(path = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<String>> stream() {
return sink.asFlux();
}
@PostMapping("/notify")
public Mono<Void> send(@RequestBody String message) {
sink.tryEmitNext(ServerSentEvent.builder(message).build());
return Mono.empty();
}
}
4.2 ทดสอบส่ง cURL
curl -N http://localhost:8080/api/stream
# แท็บใหม่
curl -XPOST http://localhost:8080/api/notify \
-H "Content-Type:text/plain" -d "Hello SSE!"
5. เขียน WebSocket Backend (Spring WebSocket + STOMP)
5.1 WebSocket Config
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").setAllowedOrigins("*").withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic");
registry.setApplicationDestinationPrefixes("/app");
}
}
5.2 Controller ส่งข้อความ
@Controller
@RequiredArgsConstructor
public class WsController {
private final SimpMessagingTemplate template;
@MessageMapping("/chat")
public void onChat(String body) {
template.convertAndSend("/topic/messages", body);
}
}
6. โค้ด Frontend รับข้อความ
6.1 Client SSE
const evtSource = new EventSource("/api/stream");
evtSource.onmessage = (e) => {
const li = document.createElement("li");
li.textContent = "📩 " + e.data;
document.querySelector("#messages").append(li);
};
6.2 Client WebSocket ผ่าน STOMP
const socket = new SockJS("/ws");
const stomp = Stomp.over(socket);
stomp.connect({}, () => {
stomp.subscribe("/topic/messages", (msg) => {
const li = document.createElement("li");
li.textContent = "💬 " + msg.body;
document.querySelector("#messages").append(li);
});
// ส่งข้อความ
document.querySelector("#send").onclick = () => {
const txt = document.querySelector("#txt").value;
stomp.send("/app/chat", {}, txt);
};
});
7. การ Deploy & Scaling
- เปิด
server.tomcat.max-threadsสูงไว้สำหรับ WebSocket blocking-IO - SSE สามารถใช้ Netty & Reactive Pool ขนาดเล็กรับได้หมื่น connection
- ถ้า Kubernetes ➜ ใช้ NGINX Ingress v1.9+ เปิด
use-http2 - เพิ่ม
proxy_read_timeoutใหญ่กว่า idle timeout ของ Browser
8. เปรียบเทียบ SSE กับ WebSocket

| คุณสมบัติ | SSE | WebSocket |
|---|---|---|
| ทิศทางข้อมูล | Server ➜ Client | Full-Duplex |
| โปรโตคอล | HTTP text/event-stream | ws/wss Binary or Text |
| Browser Support | Chrome, Edge, Firefox, Safari | เหมือนกัน |
| ฟีเจอร์ Reconnect | ในตัว | ต้องเขียนเอง |
| ใช้ Proxy/LB ทั่วไป | ✓ | อาจต้อง sticky session |
| เหมาะกับ | Event feed, IoT metrics | Chat, Game, Collab |
9. Security & Best Practices
- ส่ง JWT ใน
AuthorizationHeader สำหรับ SSE / WebSocket Handshake - ใส่ CSRF-token ซ่อนใน query param เพื่อป้องกัน WS Hijack
- จำกัดขนาดข้อความ (
maxFrameSize) เพื่อกัน big payload attack - แยก Topic ตาม user-id เพื่อกันข้อมูลรั่ว
10. Monitoring & Benchmark
Spring Boot Actuator มี metric เช่น:
reactor.netty.connection.activeจำนวน Connection SSEtomcat.sessions.activeสำหรับ WS in-memory
ทดสอบด้วย wrk ส่ง 50 kpps SSE พบ CPU ใช้เพียง 25 %
11. FAQ
Q: Safari บน iOS ไม่รับ SSE?
A: ใช้ Fetch + ReadableStream Polyfill หรือ Upgrade เป็น iOS 13+
Q: WebSocket บน AWS ALB ตั้งอย่างไร?
A: เปิด idle-timeout = 4000 s และใช้ protocol: HTTP2
12. สรุป
ทั้ง SSE และ WebSocket ทำให้แอปของคุณ พูดคุย กับผู้ใช้แบบเรียลไทม์ เลือกให้เหมาะ: ถ้าเพียง “ส่งแจ้งเตือน” ใช้ SSE ง่าย เบา เสถียร, ถ้า “โต้ตอบสองทาง” เช่น Chat หรือ Game ให้ไป WebSocket อย่าลืมวัดผล & ปรับแต่ง timeout , buffer และ security ให้รอบด้าน!
โค้ดตัวอย่างเต็ม ดาวน์โหลดได้ที่ GitHub: realtime-demo
© 2025 poolsawat.com • ขอบคุณที่ติดตาม หากบทความนี้เป็นประโยชน์ฝาก Share ต่อให้เพื่อน 👍