Server-Client Model
IPC
프로세스들은 상호독립적이므로 서로 메모리를 공유하지 않는다. 하지만, 여러 프로세스가 협업하여 일을 처리하려면 상호 메모리에 존재하는 데이터를 공유할 필요가 생긴다. 이를 해결하기 위해 IPC라는 통신 방법론이 등장
이러한 IPC 기법에는 크게 socket, RPC라는 2가지 방법이 존재
Socket
Socket이란, 앞서 언급한 OSI 7 layer 구조의 Application Layer(L7)에서 Transport Port(L4)의 TCP 또는 UDP를 이용하기 위한 수단이다.
Socket은 protocol, IP주소, port 번호로 정의된다.
Socket은 역할에 따라 Server socket, Client socket 으로 구분된다.
Socket 통신 흐름
HTTP 통신 vs Socket 통신
RPC
Remote Procedure Call의 약자
Socket의 한계를 극복하기 위해 등장
네트워크로 연결된 서버 상의 프로시저(함수, 메서드 등)를 원격으로 호출할 수 있는 기능이다.
통신 방식은 신경쓰지 않고 원격지의 자원을 내 것처럼 사용할 수 있게 해주는 기술이다.
RPC의 핵심 개념은 stub이다. 서버와 클라이언트는 서로 다른 주소 공간을 사용 하므로, 함수 호출에 사용된 매개 변수를 꼭 변환해줘야 한다. 안그러면 메모리 매개 변수에 대한 포인터가 다른 데이터를 가리키게 된다. 이 변환을 담당하는게 스텁이다.
client stub은 함수 호출에 사용된 파라미터의 변환(Marshalling, 마샬링) 및 함수 실행 후 서버에서 전달 된 결과의 변환을, server stub은 클라이언트가 전달한 매개 변수의 역변환(Unmarshalling, 언마샬링) 및 함수 실행 결과 변환을 담당한다.
Stub를 이용한 RPC 통신 과정
RPC 구조 (출처 :https://middlewares.files.wordpress.com/2008/04/17.jpg)
① IDL(Interface Definition Language)을 사용하여 호출 규약 정의합니다.
② Stub Code에 명시된 함수는 원시코드의 형태로, 상세 기능은 server에서 구현됩니다.
③ client에서 stub 에 정의된 함수를 사용할 때,
④ client stub은 RPC runtime을 통해 함수 호출하고
⑤ server는 수신된 procedure 호출에 대한 처리 후 결과 값을 반환합니다.
⑥ 최종적으로 Client는 Server의 결과 값을 반환받고, 함수를 Local에 있는 것 처럼 사용할 수 있습니다.
REST
- HTTP/2
- http/1.1은 기본적으로 클라이언트의 요청이 올때만 서버가 응답을 하는 구조로 매 요청마다 connection을 생성해야만 한다. cookie 등 많은 메타 정보들을 저장하는 무거운 header가 요청마다 중복 전달되어 비효율적이고 느린 속도를 보여준다. 이에 http/2에서는 한 connection으로 동시에 여러 개 메시지를 주고 받으며, header를 압축하여 중복 제거 후 전달하기에 version1에 비해 훨씬 효율적이다. 또한, 필요 시 클라이언트 요청 없이도 서버가 리소스를 전달할 수도 있기 때문에 클라이언트 요청을 최소화 할 수 있다.
2. ProtoBuf (Protocol Buffer, 프로토콜 버퍼)
- Protocol Buffer는 google 사에서 개발한 구조화된 데이터를 직렬화(Serialization)하는 기법이다.
- 직렬화란, 데이터 표현을 바이트 단위로 변환하는 작업을 의미한다. 아래 예제처럼 같은 정보를 저장해도 text 기반인 json인 경우 82 byte가 소요되는데 반해, 직렬화 된 protocol buffer는 필드 번호, 필드 유형 등을 1byte로 받아서 식별하고, 주어진 length 만큼만 읽도록 하여 단지 33 byte만 필요하게 된다.
- Proto File
1)Message and Field
Proto File에서는 주고 받는 data들을 message 라는 것으로 정의한다. 이 메시지는 여러가지 타입의 필드로 구성된다. 아래 예시로 query, page_number, result_per_page 라는 필드를 가지는 SearchRequest 라는 메시지가 정의되어있다.
▶ Naming
message 이름은 CamelCase 형태, field 이름은 under_bar 형태로 사용할 것을 권장하고 있다. 유의할 것은 field 이름은 숫자로 시작할 수 없다는 점이다. 숫자를 표기해야 할 경우 꼭 문자 뒤에 표기해주어야한다.
ex) query_1 (o) / 1_query (x)
▶ Field Tag (= Field number)
메시지에 정의된 필드들은 각각 고유한 번호를 가지게되고 이는 Enconding 이후 binary data에서 필드를 식별하는데 사용된다. Field Tag는 최소 1, 최대 536,870,911(=229–1) 로 지정 가능하며, 19000 ~ 19999는 프로토콜 버퍼 구현을 위해 reserved 된 값이므로 사용할 수 없다.
필드 번호가 1~15일 때는 1byte, 16~2047은 2byte를 Tag로 가져간다. 때문에 자주 호출되는 필드에 대해선 1~15로 지정해두는 것이 좋다.
▶ proto2 VS proto3
위 예제에서는 첫 줄에 syntax = “proto3”을 지정해줌으로써 proto version 3의 규약을 따르겠다고 선언했다. 이를 명시하지 않으면 default로 version2 문법을 따르게 된다. 아래와 같이 지원 언어도 다르지만, message 작성 시 field rule 지정 등 문법에도 차이가 나타난다.
▶ Proto File Field Rule
2) Package
package는 message type 이름을 중첩없이 구분할 때 사용한다. 메시지 사용 시 package를 명시함으로써 필드와 명확히 구분한다. 아래 예제에서는 Open이라는 message를 타입으로 하는 field 이름을 open으로 주어 모호한 정의를 package로 구분한다. 사실 foo.bar라는 package를 굳이 쓰지 않는다고 사용이 불가한 것은 아니지만, 구성 메시지가 많다면 명확하게 구분될 수 있게 명시해 주는 것이 좋다.
3) Service
Service는 RPC를 통해 서버가 클라이언트에게 제공할 함수의 형태를 정의한다. 서비스명과 RPC 메소드명 모두 CamelCase 형태를 권장한다. 옵션을 주지 않으면 단일 요청/응답으로 동작하지만, stream 옵션을 주면 RPC를 구현할 수 있다.