gRPC(1)

지인호·2022년 2월 9일
2

TIL

목록 보기
21/28
post-thumbnail

gRPC 이전의 기술들

gRPC 이전, 통신기술들은 과연 어떻게 발전하였을까?

Server - Client 모델

PC 나 워크스테이션같은 소형 컴퓨터에대한 개념이 없던시절, 프로그램은 하나의 메인프레임에서 동작하는 모놀리식 구조로 설계되었다. 즉, 모든기능이 한 단말에서 구동되었기에, 네트워크 통신이 그리 중요하지 않았다

하지만, PC나 워크스테이션 같은 소형컴퓨터가 등장하자, 이러한 메인 프레임에서 구동되던 모놀리식 구조의 프로그램을 워크스테이션 서버로 이전하고자 하였다. 하지만 기존의 거대한 모놀리식 구조의 프로그램을 비교적 저사양인 워크스테이션 서버가 감당하긴 힘들었고, 이에 프로그램을 여러 모듈로 분할하여 여러 워크스테이션 서버에 분산시켜 동작하도록 하였다.

또한, 분산된 각 서버를 네트워크 연결로 서비스하였는데, 이것이 바로 Server-Client 모델이다.

이처럼, Server to Server, Server to PC 네트워크 연결/통신 이 중요해지면서 OSI 7계층이나 각종 네트워크 프로토콜등, 네트워크 관련 구조나 규약이 정의되고 발전하기 시작한다.

IPC (프로세스간 통신)

프로세스는 기본적으로 상호독립적으로 가동된다. 즉,

  • 메모리를 공유하지 않고,

  • 각자 자신의 일만 하며

  • 서로 간섭하지 않는다.

    하지만 필요에 따라 이러한 프로세스간의 정보교환이 이루어져야할 때가 있는데 이러한 프로세스간의 정보교환 통신에 관련한 방법론IPC 라고 한다.

IPC 의 종류로는 Socket, RPC, REST, 파이프 등이 있다.

TCP/IP Socket

Socket 자체는 그저 추상화된 개념이다. 여기서 다루는 내용은 TCP/IP 소켓에 대한 설명이다

어플리케이션 계층에서 전송계층 내의 TCP, UDP 를 이용하여 통신하기위한 수단으로, 통신에 대한 접속점(창구) 역할을 하는 통로이다.

  • 통신이 컴퓨터 내부가 아닌 네트워크 범위로 이루어져, 네트워크간 통신에 속하지만,
  • 로컬컴퓨터와 리모트 컴퓨터의 각 프로세스가 정보교환을 한다는 관점에서 IPC 에도 속한다.

소켓은 대부분의 언어에서 API 형태로 제공하는 기술이기때문에 현재까지도 많이 사용되고있다.

하지만 이러한 소켓에도 아래와 같은 한계가 존재한다.

  • 하지만, 일련의 통신과정 전반을 개발자가 직접 구현해야하므로,
    통신관련 장애를 핸들링하는것 또한 개발자가 구현해야한다.
  • 서비스가 고도화되고 복잡해질수록, 전달되는 데이터의 종류는 계속 많아지는데,
    이에따른 data formatting 또한 어려워진다.

RPC

소켓의 한계를 극복하기 위해 개발된 기술로, 이번 아티클의 주제인 gRPC 또한 RPC 에 속한다.

Remote Procedure Call 즉, 네트워크로 연결된 서버상의 프로시저를 원격으로 호출할 수 있는 기술이다. 네트워크 통신이나 호출작업에 신경쓰지 않고, 원격지의 자원을 로컬자원처럼 활용할 수 있다.

언어 중립적 인터페이스인 IDL(Interface Definication Language) 에 기반한 기술이므로, 다양한 기술스택(언어)를 사용하는 환경에서도 쉽게 확장할 수 있다.

C++, Java, Python, Ruby, Node.js, C#, Go, PHP 등 많은 major 한 언어들에서 지원하는 기술이다.

RPC 에는 Stub 이라는 핵심개념이 존재하는데, 서버에 저장되어있는 자원의 주소와 클라이언트에 저장되어있는 자원의 주소가 다를 수 있으므로, 함수 호출에 사용한 매개변수를 변환해야한다.

그러지 않으면 메모리 매개변수에 대한 포인터가 다른 데이터를 가리킬 가능성이 높다.

이러한 매개변수의 변환을 담당하는것이 Stub 이다.

Stub 은 client stubserver stub 으로 나뉜다.

  • client stub 은 마샬링(함수호출에 사용되는 파라미터의 변환) 과 함수 실행 후 서버에서 전달된 결과의 변환을 담당한다.
  • server stub 은 언마샬링(클라이언트가 전달한 매개변수의 역변환) 및 함수 실행 결과의 변환을 담당한다.

stub 을 통한 기본적인 RPC 통신과정

  1. IDL 을 사용하여 호출규약을 정의한다

    함수명, 인자, 반환값에대한 데이터형이 정의된 IDL 파일을 rpcgen 으로 컴파일하여 Stub Code를 생성한다.

  2. StubCode 에 명시된 프로시저는 원시코드(컴파일 이전의 코드)의 형태로 작성되며,
    상세기능은 server 에서 구현된다. (StubCode 는 클라이언트/서버에 함께 빌드한다)

  3. client 에서 stub 에 정의된 프로시저(함수 등)를 사용한다.

  4. client stub 이 RPC runtime 을 통해 프로시저를 호출한다.

  5. server 에 수신된 프로시저 호출에 대한 로직을 처리하고, 결과값을 반환한다.

최종적으로 Client 는 Server 의 결과값을 반환받아, 원격지(Server) 에 존재하는 함수를 Local 에 있는 함수처럼 사용할 수 있다.

이러한 RPC 는 그당시 상당히 획기적인 방법론이었고, 분산환경과 함께 꾸준히 발전해온 기술이다.

따라서 구현체 또한 SOAP, CORBA 등 여러가지가 존재한다.

하지만, 구현난이도가 높고, 지원기능에 한계가 있어 제대로 활용되지 못하였는데,

이때 WEB 을 활용해 데이터 통신을 해보려는 시도가 이어졌고 이는 REST 아키텍처 스타일로 발전하며, 기존 RPC 의 자리를 차지하게 된다.

REST

HTTP 1.1 기반의 정보 교환 아키텍처 스타일이다.

  • URI 를 통해 모든 자원을 명시하고
  • HTTP Method 를 통해 자원에 관한 여러 Operation 을 처리하는 아키텍처이다.

자원자체를 표현하기때문에 직관적이며, HTTP 자체의 여러 개념을 사용하여 별도의 작업 없이도 쉽게 도입/사용이 가능하다.

  • 하지만, REST 는 아키텍처 스타일이지, 표준이 아니기 때문에 parameter, response 등이 명시적이지 않다.
  • 또한 HTTP Method 의 형태가 제한적이기 때문에, 세부기능 구현에 제약이 존재한다.

또한 WEB 을 통한 데이터 전달 format 으로 xml 이나 json 을 주로사용하였는데, 이 또한 여러 문제가 존재하였다.

  • XML 은 tag 기반의 데이터 형식이나, 미리 정의된 태그가 없어, 높은 확장성을 가지지만,
    복잡하고 비효율적인 데이터 구조속도가 느리다는 단점이 존재하였다.
  • JSON 은 이러한 효율성을 간단한 Key - Value 구조를 통해 해결하는듯 하였지만,
    제공되는 자료형에 한계가 존재하여, 파싱 후 추가적인 형변환이 필요한 경우가 많았다.
  • 또한 XML 과 JSON 모두 문자열 기반의 데이터 전달 format 이기때문에 사람이 읽기는 편하였지만, 데이터 전송과 처리 과정에서 별도의 직렬화 처리가 필요하였고, 이는 속도의 저하로 이어졌다.

gRPC 의 등장

REST 아키텍처스타일의 한계가 드러나던 와중 나온 구글의 오픈소스 RPC 프레임워크

기존에는 RPC 기능을 지원하지 않고, 전술한 JSON 같은 데이터형식의 메세지를 직렬화하는 Protocol Buffer 만을 제공하는 프레임워크였으나, Protocol Buffer 기반의 SerializerHTTP 2.0 을 결합한 새로운 RPC 프레임워크로 변모하였다.

REST 와는 기반 기술 자체가 다르기때문에 특징 또한 매우 다르다.

그중 가장 두드러지는 차이점은

  • HTTP 2.0 을 사용한다는것과
  • 프로토콜 버퍼로 데이터를 전달한다는 것

이다. 이러한 gRPC 의 특성 덕분에, Proto File 만 배포한다면 환경과 프로그램 언어에 구애받지 않고 서로간의 데이터 통신이 가능하다.

HTTP 2.0

REST 아키텍처 스타일에서 사용하는 HTTP 1.1 과는 달리, HTTP 2.0은 Header 의 압축ServerPush 같은 여러 기술들을 통해 클라이언트의 요청을 최소화 하였다.`

ProtoBuf (프로토콜 버퍼)

google 에서 개발한 구조화된 데이터를 직렬화하는 기법이다.
이때 직렬화란 데이터 표현을 바이트단위로 변환하는 작업을 의미하는데, 아래 예제처럼

  • text 기반 data format 인 json82 바이트가 소요되는데에 반해,
  • 직렬화된 protocol buffer
    필드번호/유형 등을 1바이트 로 받아서 식별하고,
    주어진 length 만큼만 읽음으로서 33바이트만 소모한다.

Proto File

Protocol Buffer 에서 사용하는 데이터 타입에 대한 정의 같은 Protocol Buffer 의 기본 정보를 명세한다.

Message & Field

ProtoFile 에서는 주고받는 data 를 message 로 정의한다. 또한 이 message 는 여러가지 타입의 field 로 구성된다.

  • Naming
    message 의 이름은 CamelCase 형태로, field 의 이름은 snake_case 형태로 사용할것을 권장하고 있다. 단, field 이름은 숫자로 시작할 수 없다
  • Field Tag
    인코딩 이후의 이진데이터에서 field 를 식별할 수 있도록 field 들은 각각의 고유번호를 가지게 된다. (상단 프로토콜 버퍼 설명에서의 필드번호가 이에 해당한다) FieldTag 는 1부터 536,870,911 (약 5억 3천) 까지 지정가능하며, 19000번대 (19000~19999) 는 프로토콜 버퍼 구현을 위해 예약된 값이므로 사용할 수 없다. Tag 는 필드번호가 1~15일 경우 1byte, 16~2047 일경우 2byte 를 사용한다. 따라서 자주 호출되는 필드는 1~15로 지정하는것이 좋다.
  • proto2 vs proto3
    위의 예제는 첫 줄에 syntax = “proto3” 으로 지정함으로서 proto V3 의 규약을 따르겠다고 선언하였다. 만약 이를 명시하지 않을 경우, proto2 문법을 기본값으로 따르게 되며, 이 경우 지원언어 뿐 아니라 문법에도 차이가 생긴다.
  • Proto File Field Rule
    • required : 해당 필드는 필수로 가져야한다 (proto2 에서만 사용가능)

    • optional : 해당 필드를 가지지 않거나 하나만 가진다 (proto2 에서만 사용 가능)

    • repeated : 해당 필드는 임의로반복할 수 있다.필드번호,반복된값의 순서는보존된다. (=배열)

    • [package=true] : 모든 필드는 key-value 로 저장되기떄문에 repeated 를 사용할 경우, 모든 반복값에 key 가 붙게된다 이때, 해당 옵션을 사용하여 value 만 반복하도록 할 수 있다

      proto2 의 경우 required 와 optional 을 필드별로 무조건 명시해주어야한다.

Package
message type 이름을 중첩없이 구분할 때 사용한다 package 를 명시하여 동명의 필드와 명확히 구분할 수 있다.
package 미사용시
package 사용시

Service

RPC 를 통해 서버가 클라이언트에 제공할 함수의 형태를 정의한다.

  • Naming
    서비스명과 RPC 프로시저(메서드) 명 모두 CamelCase 형태를 권장한다.
    stream 옵션을 통해서 단일요청/응답이 아닌 Streaming 또한 적용할 수 있다 (n개의 요청 또는 n개의 응답)
profile
테오의 스프린트 17기 퍼실리테이터

0개의 댓글