여기서는 HTTP의 Stateless에 대해서 다루는 것은 아니고, HTTP의 Stateless와 로드 밸런싱이 연관 관계가 있는지 궁금해서 정리해보는 글임을 알리고 시작합니다.
이 부분에 관련되서는 ChatGPT를 통해 참고를 했습니다.
HTTP 커넥션은 보통 TCP 커넥션을 기반으로 합니다. HTTP는 인터넷에서 데이터를 주고받을 때 사용되는 프로토콜 중 하나로, TCP/IP 프로토콜 스택 위에서 동작합니다. HTTP 클라이언트는 일반적으로 TCP/IP를 사용하여 HTTP 서버와 연결하고, 데이터를 주고받습니다. TCP/IP는 IP(Internet Protocol)와 TCP(Transmission Control Protocol)로 구성되어 있으며, IP는 데이터를 라우팅하는 역할을 하고, TCP는 안정적인 데이터 전송을 담당합니다. HTTP는 TCP 커넥션 위에서 데이터를 전송하기 때문에, 데이터 손실이나 중복 전송등을 방지할 수 있습니다.
여기서 생긴 의문점은 HTTP는 Stateless 특징을 가지고 있습니다. 이것이 TCP/IP 기반으로 데이터 전송이 끝나고 나서 커넥션을 끊어 버리게 되면서 전달하는 데이터에 관련된 내용을 저장하지 이러한 특징을 가지게 된 것인지 궁금했습니다.
이 내용과 관련되서는 HTTP의 Stateless 특징과 TCP/IP 기반으로 데이터 전송이 끝나고 커넥션을 끊는 것은 별개의 개념이라는 답변을 받았습니다.
즉, TCP/IP를 기반으로 데이터를 전송하는 것은 HTTP가 Stateless이든 Stateful이든 상관 없다는 것입니다.
HTTP의 Stateless 특징은 클라이언트와 서버 간의 각각의 요청/응답 사이에 상태 정보를 유지하지 않는 것을 의미합니다. 즉, 이전 요청과 현재 요청 간에는 상태 정보가 공유되지 않습니다.
이것은 서버 측에서 각 요청을 별개의 것을 처리할 수 있도록 하며, 로드 밸런싱과 같은 기술을 적용하여 서버의 확장을 향상시키는 데 도움을 줍니다.
반면 TCP/IP 기반으로 데이터 전송이 끝나고 커넥션을 끊는 것은, 데이터 전송이 끝나면 클라이언트와 서버 간의 연결을 종료하는 것을 의미합니다. 이것은 일반적으로 HTTP/1.1 이하 버전에서 사용되며, HTTP/2 와 같은 최신 버전에서는 하나의 커넥션을 유지하면서 다수의 요청과 응답을 처리할 수 있도록 설계되었습니다.
따라서, 둘 사이에는 직접적인 연관성이 없습니다.
여기서 Stateless와 로드 밸런싱(Load Balancing) 도대체 어떤 연관이 있는 것일까요?
상황 하나를 가정해 봅시다.
만약 HTTP가 Stateful인 특징을 가지고 있을 때 대규모 트래픽이 들어오면 어떻게 될 것인가?
세 가지로 나누어서 볼 수 있습니다.
1번의 경우 사용자의 정보를 저장하기 위해서 메모리를 사용하기 때문에 당연한 얘기입니다.
2번의 경우 메모리를 쓸 때 어디에 저장해두는 가를 생각해보면 이것도 당연합니다. 서버에서 데이터를 저장함으로써 같은 사용자의 요청은 같은 서버로만 보내야 되며 요청이 많아서 서버를 확장하려고 하는 경우에 이는 방해되는 요인입니다.
3번의 경우 이러한 프로세스 과정이 추가됨으로써 추가적으로 넣어야 되는 코드가 증가하고, 이것으로 인해 발생하는 여러 상황도 예측해야 됩니다.
위에서 특징을 얘기하면서 로드 밸런싱이 언급이 되었습니다.
HTTP의 Stateless 특징과 로드 밸런싱은 상당히 잘 맞는 부분이 있습니다.
로드 밸런싱은 여러 대의 서버를 사용해서 네트워크 트래픽을 분산시키는 기술입니다. 이를 통해 서버의 처리 능력을 확장할 수 있으며, 클라이언트의 요청을 처리하는 데 있어서 성능을 개선할 수 있습니다. 예를 들어, 하나의 서버에서 처리할 수 없는 많은 양의 트래픽이 발생하는 경우, 로드 밸런서는 이 트래픽을 여러 대의 서버에 분산시켜 처리할 수 있도록 합니다. 이를 통해 트래픽이 분산되어 서버 부하를 분산시키므로, 서버의 성능을 향상시키고 안전성을 확보할 수 있습니다.
즉, HTTP의 Stateless 특징은 서버에서 각 요청을 독립적으로 처리할 수 있도록 하는 것이고, 이를 활용하여 로드 밸런싱과 같은 기술이 잘 맞는 것입니다.
결국, Stateless와 Stateful의 가장 큰 차이점은 서버에서 유지하는 상태 정보의 유무입니다.
Stateless에서는 각 요청이 독립적으로 처리됩니다. 클라이언트의 이전 요청과 현재 요청 사이에는 서버에서 유지하는 상태 정보가 없습니다. 따라서 서버는 각 요청을 독립적으로 처리하고, 클라이언트의 상태 정보를 유지할 필요가 없습니다.
반면에 Stateful에서는 서버는 클라이언트의 상태 정보를 유지해야 합니다. 클라이언트가 서버로 요청을 보낼 때, 서버는 이전 요청에서 클라이언트의 상태 정보를 유지하고 있는지 확인한 후, 상태 정보가 없는 경우, 클라이언트는 서버에 새로운 상태 정보를 보내 요청을 처리할 수 있도록 해야 합니다.
Stateless에서는 상태 정보가 없기 때문에 서버는 각 요청을 독립적으로 처리할 수 있습니다. 반면에 Stateful에서는 상태 정보가 있기 때문에 각 요청을 처리할 때, 이전 요청에서 유지하던 상태 정보를 확인하고 이를 이용하여 요청을 처리해야 합니다. 따라서 Stateful에서는 서버의 부하가 많이 걸리고, 처리 시간이 더 길어질 수 있습니다.
그렇다면, Stateless에서는 클라이언트의 상태 정보를 유지하지도 않고, 각 요청을 독립적으로 수행하니깐, 이를 처리하기 위해서 데이터베이스 등의 외부 저장소에 접근하지 않아도 된다는 것이네요?
하지만 이는 매우 단순한 상황일 때만 가능합니다. 요즘 대부분의 웹 애플리케이션은 사용자의 세션, 인증 정보, 쇼핑카트 등을 유지해야 하기 떄문에 데이터베이스 등의 외부 저장소에 접근할 필요가 있습니다.
따라서 Stateless 웹 서버에서도 외부 저장소에 접근할 필요가 있는 경우가 많습니다.
여기서 이제 주목해야할 점은 로드 밸런싱기법이 Stateless 특징과는 잘 맞는데 과연 Stateful인 상황에서는...?
Stateless의 요청은 DB에 접근할 필요가 없으므로 로드 밸런싱 기법과 매우 잘 맞습니다. 트래픽을 분산해서 처리한다고 해도 외부 저장소에 대한 접근이 늘어나는 것은 아니니까요.
하지만, Stateful한 요청이 들어왔을 경우 얘기는 달라집니다.
이러한 요청이 하나의 서버에 너무 많이 들어와서 이것을 처리하기에 버거워서 로드 밸런싱 기법을 사용해서 다른 서버에게 트래픽을 분산해줘도 DB에 접근할 수 있는 트래픽이기 때문에 문제가 발생합니다.
이러한 경우에는 로드 밸런싱 기법이 부적절합니다.
하지만, 그렇다고 문제점이 있다고 방치를 해둘 수는 없죠.
그래서 일반적인 로드밸런싱 기법이 아닌 추가적인 기법을 같이 사용합니다.
Stateful한 서비스를 로드 밸런싱 할 때는 보통 세션 어피니티(Session Affinity)라는 기법을 사용합니다. 이 기법은 로드 밸런서가 요청을 분산할 때, 클라이언트의 요청이 이전에 접속했던 서버로 전달되도록 하는 것입니다. 이렇게 함으로써 DB에 대한 요청이 한 서버에 집중되어 발생하는 일을 줄일 수 있습니다.
하지만 세션 어피니티도 여러 가지 문제점이 있기 때문에, DB 접근 트래픽이 많은 서비스의 경우에는 다른 방식의 아키텍쳐나 데이터베이스 샤딩(sharding)과 같은 기술을 사용하는 것이 좋습니다.
여기서 새로운 두 개념이 등장합니다.
이 두 가지 모두 로드 밸런싱 기법의 일종으로, 트래픽을 여러 대의 서버에 분산하여 처리하는 방법입니더,
세션 어피니티 기법은 클라이언트 요청이 발생했을 때, 해당 클라이언트 요청을 항상 같은 서버로 전송하는 방법입니다. 이 방법은 로드 밸런서가 클라이언트와 서버 사이의 세션을 추적하여, 한 번 연결된 클라이언트와 서버의 연결을 계속 유지할 수 있도록 합니다.
세션 어피니티 기법은 일반적으로 세션이 유지되어야 하는 경우, 즉 로그인 정보나 장바구니 등 사용자 개인의 정보가 필요한 경우에 사용됩니다. 하지만 세션 어피니티 기법을 사용하면 로드 밸런서와 서버 간의 부하 분산이 불균형해질 수 있으므로, 서버의 수를 늘리거나 세션 타임 아웃 시간을 늘리는 등의 대응이 필요합니다.
여기서 왜 불균형이 발생하는 것일까?
서버간의 처리 능력에 차이가 있을 수 있기 때문입니다.
일부 서버의 부하가 많아져 다른 서버에 비해 처리 속도가 느려질 수 있습니다. 이 경우, 세션 어피니티를 사용한 로드 밸런싱에서는 부하가 적은 서버에는 거의 요청이 오지 않는 반면, 부하가 많은 서버는 연속적인 요청이 몰리게 되어 부하 분산이 불균형해지게 됩니다.
이러한 문제점을 해결하기 위해서 세션 언어피니티를 사용하는 로드 밸런서가 요구됩니다.
세션 언어피니티는 요청을 처리할 서버를 무작위로 선택하는 방식으로 부하를 분산시키기 때문에 부하 분산이 불균형해지는 문제를 해결할 수 있습니다.
데이터베이스 샤딩은 데이터베이스를 여러 조각으로 분할하여, 조각마다 다른 서버에 저장하고 조회하는 방법입니다. 데이터베이스 샤딩은 대규모 서비스에서 발생하는 대량의 트래픽을 처리하기 위해 사용됩니다. 데이터베이스 샤딩은 하나의 데이터베이스에 모든 데이터를 저장하는 것이 아니라, 분산된 데이터베이스에 데이터를 저장하므로 데이터베이스 전체가 장애가 발생해도 전체적인 서비스에 영향을 미치지 않습니다. 하지만 데이터베이스 샤딩을 구현하기 위해서는 데이터베이스 구조를 재설계하고, 복제 및 분산 처리에 대한 기술 지식이 필요합니다.
샤딩을 적용하는 방법에는 수직 분할과 수평 분할이 있습니다.
수직 분할은 데이터베이스의 테이블을 논리적으로 분리하여 각각의 테이블을 다른 데이터베이스에 저장하는 방식입니다. 예를 들어, 고객 정보와 주문 정보를 각각 다른 데이터베이스에 저장하는 것입니다.
수평 분할은 테이블의 레코드를 분할하여 각각의 레코드를 다른 데이터베이스에 저장하는 방식입니다. 예를 들어, 한 테이블의 레코드를 고객의 지역에 따라 분할하여 각각 다른 데이터베이스에 저장하는 것입니다.
이러한 샤딩 방식은 데이터베이스의 부하 분산을 효과적으로 처리할 수 있지만, 데이터의 일관성을 유지하는 것이 중요합니다. 따라서 샤딩을 적용할 때는 데이터베이스 간에 데이터를 동기화하는 방법을 고려해야 합니다. 또한, 샤딩을 적용할 때는 각각의 샤드가 독립적으로 동작할 수 있도록 설계해야 하며, 데이터 접근 방법 등에 고민도 필요합니다.