풀스택서비스네트워킹(7), gRPC

2022. 12. 10. 21:10강의 내용 정리/풀스택서비스네트워킹

728x90
반응형

gRPC

1. 함수 호출 방법(들)

0) 실행파일 구조

프로그램이 실행되면 윈도우즈의 경우에는 위와 같은 형태가 된다.

 

text는 기계어로 번역된 코드가 작성되어있다.

그 위에 초기화가 된 데이터, 그 위에는 초기화가 되지 않은 데이터를 쌓는다.

high address에 가면 stack이 있다. 이는 call stack을 의미한다.

heap은 동적으로 할당하는 메모리가 차지하는 영역이다. 클래스를 통해 객체를 만들 경우 heap에 저장된다.

 

우리가 만든 함수도 컴퓨터 메모리 어딘가에 위치하게 된다. 함수를 호출한다는 것은 프로그램의 실행을 프로그램의 메모리 첫번째부분으로 옮긴다. 이후 코드를 읽어서 cpu에게 전달하고 코드를 실행할 수 있도록한다.

 

dll을 사용해서 다이나믹 링킹을 하는 경우에는 컴파일은 하지만 실행파일에는 포함되지 않는다. 실행파일을 만들 때에는 이를 뺀다. dll은 해당 파일이 저장되어있는 주소를 알고 있다면 필요할 때마다 이를 불러서 사용하도록 한다. 따라서 필요할 때 함수를 디스크에서 읽어서 기계어 코드를 heap에 저장하여 호출할 수 있도록 하고, 필요 없다면 지워서 실행파일의 크기를 줄인다. 따라서 exe 파일의 실행파일은 작지만 다른 dll은 파일의 크기가 크다. 과거에 컴퓨터 메모리가 적었을 때 최대한 메모리 효율적으로 사용해야했기에 이러한 과정을 거쳤다. Zephyr와 같이 매우 작은 RTOS는 이와 같이 메모리를 작게 잡아서 사용해야한다.

 

1) 정적 링킹과 동적 링킹

(1) 정적 링킹(Static Linking)

링킹과정을 통해 라이브러리를 포함한다.

 

(2) 동적 링킹(Dynamic Linking)

실행 파일에 포함하지 않고, 하나의 메모리 공간에 매핑 후 여러 프로그램에서 공유해서 사용한다. 즉, 필요한 것만 실행파일에 넣고, 모든 기능이 100% 다 실행파일에 들어가있지는 않다. 항상 사용하지 않는 것은 실행파일을 실행할 때 연동해서 사용한다.

 

파일 크기가 훨씬 작고, 실행 시에도 적은 메모리를 차지한다. 하지만 지연시간이 발생할 수 있다.

 

2) 원격 함수 호출(RPC: Remote Procedure Call)

내가 가지고 있지않은 함수를 네트워크를 통해 호출해서 사용한다.

 

caller는 stub을 호출한다. stub은 클라이언트가 전달한 내용을 RPC 프로토콜에 전달한다. 이후 서버의 RPC 프로토콜은 전달받고, 이를 stub에게 전달한다. 이후 서버는 응답하고, 

 

이떄 주요한 특징 중 하나는 client와 server는 네트워크로 연결되어서 함수를 호출하고 호출되는지는 알 수 없다는 점이다. stub은 client와 server가 네트워크를 사용해 통신하는 지를 알지 못하게 하는 것이 매우 중요했다. 그 다음으로 중요한 것은 아규먼트, 리턴값 등을 전달하는 게 중요해졌다.

 

RPC를 통해 함수를 호출하고 호출되는 과정이 있다가 이후 웹의 발전에 따라 xml 파일을 전달하는 SOAP이 등장했다. 그리고나서 REST로 발전해갔다. 이는 api로 발전해갔다.

 

cf) stub: 비어있는 것을 채워하는 역할

IDL: 서로 다른 컴퓨터가 정보를 주고 받기 위한 언어

 

 


2. gRPC 개요

HTTP2 위에서 돌아가고 구글이 RPC를 기반으로 만들었다. gRPC는 HTTP2에서의 제 1의 어플리케이션으로 고려된다. 프로토콜 버퍼 IDL, 단방향/양방향 스트리밍을 모두 지원한다. 그 외의 특징은 HTTP2의 특징이다.

 

REST를 gRPC로 많이 구현한다. 앱에서도 많이 구현된다. 실제로 Microservice architecture에서 많이 사용된다.

 

 

gRPC Remote Procedure Call (gRPC)

gRPC는 오픈소스이며 성능이 좋다. 여러 언어를 함께 사용할 때 많이 사용된다.

 

홈페이지에서 정의한 특징

RPC에 비해 서비스 구현이 쉽다.

프로토콜 버퍼는 함수나 객체 등을 전달하는데 사용한다.

설치가 용이하고 다양한 개발 플랫폼에서 사용 가능하다.

스트리밍이 가능하다. -> 이는 gRPC의 주요 특징이다.

http2 기반으로 이뤄진다.

디양한 언어에서 제공한다. 

 

어디서 사용하고 있는가

대표적으로 컨테이너 기반의 기술에서 gRPC를 사용한다.

 

gRPC vs REST API (HTTP + JSON)

REST API는 CRUD에 맞춰 get put delete post만 할 수 있으면 됐기에 HTTP 프로토콜 버전이 중요하진 않았다.  또한 구체적인 규정이 없다. 하지만 gRPC는 함수를 호출하고 받아오는 규칙이 매우 Strict하다. 이때 Proto를 사용한다.  Streaming은 간단히 클라이언트와 서버와의 통신을 체크하는 식으로 진행한다.

 

또한 TLS라는 보안을 사용한다.

 

gRPC는 매우 strict하게 규정을 만들어서 사용한다. 이를 .proto?라는 파일에 적게 된다. gRPC는 HTTP/2 위에서만 가능하다. 아직 HTTP3에서는 올라가지 않았다. protobuf로 리턴값, 입력값 등을 실어나른다. 이때 텍스트 베이스가 아닌 0과 1을 주고받는다. 이에 따라 주고받는 데이터의 양을 적게 만들었다. 덕분에 지연도 줄일 수 있다.

 

또한 이는 표준 문서가 있다. 주고받는 데이터가 정수인데 이를 어떻게 표현할 건지 등에 대해 정해져있다. 클라이언트와 서버가 서로 끊임없이 내용을 보낼 수 있다는 특징이 있다. gRPC는 웹브라우저에서 사용하는 방식이 아니기에 서버와 클라이언트들 간의 통신만 지원한다.

 

이해하는 것은 어려울 수 있으나 틀을 따라가면 바로 사용할 수 있다.

 

언어별 gRPC와 툴을 설치가 필요하다. 설치하면 gRPC 본체가 설치되고, 이는 툴이 필요하게 된다. proto라는 파일을 활용해 클라이언트에는 stub function, 서버에도 servicer function을 자동으로 만들어준다.

 


3. gRPC 예제(4가지 타입)

4가지 타입에 의해 쿠버네티스에서 통신을 할 때에는 gRPC를 사용한다.

 

1) Unary RPC

Streaming이 아닌, 단순한 함수 호출과 응답을 의미하는 RPC이다.

위에 있는 내용은 proto 파일에 정의한다. proto 파일은 protocol buffer에서 따온 것이다. 이는 client에서 사용한다.

 

returns 뒤에 있는 값은 return 값의 값과 데이터 타입을 알려준다.

 

프로토 파일은 프로그래밍 랭귀지와 독립적으로 존재한다.

 

제곱 함수를 gRPC를 통해 다루는 과정

가장 먼저 서버는 호출 당할 함수를 작성한다.

 

이는 gRPC와는 관계없이 만든다. 클라이언트는 네트워크를 통해서 my_func에 접근할 것이고, 서버는 이를 계산해서 돌려준다. 

 

위와 같이 최소 세가지 내용이 필요하다. 이는 protocol buffer 언어의 규칙을 따른다.

 

서버는 본인이 작성한 함수를 service하고, 통신에서의 단위는 메세지이기에 메세지를 입력 파라미터와 리턴값으로 정의했다.

syntax

프로토 버퍼의 버전을 의미한다.

 

service

입력 파라미터를 MyNumber로 받아서, 리턴 값을 MyNumber로 하나 리턴한다라는 의미를 가지고 있다.

rpc로 호출되는 함수를 임의로 MyFunction으로 정의했다.

프로토 파일은 프로그래밍 랭귀지와 독립적으로 존재하기에 파이썬 코드와는 살짝 다르게 보일 수 있다.

 

message

함수에서 주고받는 입력값과 출력값을 message로 정의한다.

rest는 json 파일을 주고받지만 gRPC는 아니다. 파이썬의 경우에는 입력 파라미터의 데이터타입을 적어줄 필요는 없지만 다른 언어의 경우에는 입력 파라미터의 데이터타입을 적어줘야한다. 이에 따라 모든 프로그래밍 언어에서도 사용할 수 있게하기위해 위와 같이 데이터 타입을 사용한다. C나 C++은 CPU나 운영체제에 따라 int의 비트 수가 달라질 수 있다. 이때 int32와 같이 비트수를 고정할 수 있다. 

아무런 값이 주어지지 않으면 1로 디폴트값을 두었다.

 

프로토콜 버퍼에 대한 설명뿐만아니라 각 프로그래밍 언어별로 설명도 나와있다.

 

protoc를 사용해 결과 파일(클래스 파일)을 만들 수 있다. 즉, 언어별로 protobuf compiler를 실행한다. 이를 통해  클래스 파일을 만들 수 있다.

 

2개의 파이썬 파일이 만들어진다. 

message class 파일

메세지의 이름으로 사용했던 것이 prefix 형태로 붙는 것을 알 수 있다. (MyNumber)

 

client/server class 파일

service의 이름으로 사용했던 것이 prefix 형태로 붙는 것을 알 수 있다.(MyService)

server를 위해서는 Servicer가 만들어진다.

client를 위해서는 Stub가 만들어진다.

 

위의 단계를 준수해서 코드를 작성하면 된다.

futures는 파이썬이 비동기적으로 동작할 때 대용량에 대해 처리할 때 사용하는 패키지이다.

 

protoc가 생성한 Servicer 클래스를 base class로 삼아서 derived class를 만들어서 서버를 만든다.

프로토 함수에서 사용했던 이름인 MyFunction을 사용한다. 이때 동일한 이름으로 처리해야한다. 이를 오버라이드하고 있다. MyFunction의 입력파라미터는 request가 된다. request로도 MyNumber()의 객체가 들어오기에 해당 객체의 value를 받고, response로 돌려준다.

 

Thread.PoolExecutor를 통해 쓰레드를 10개 만들어서 grpc 서버에게 전달한다. 이후 해당 서버와 proto 파일에서 만든 서버용 클래스를 함께 전달해 grpc 서버를 open한다.

[::]는 local ip address를 의미한다.

50051번 포트를 열어줬다. insecure는 암호화를 하지 않았다. secure를 하면 처리해야하는 작업이 많아지기에 이를 사용하지 않는다. 

 

 

 

channel을 통해 채널을 뚫는다.

MyServiceStub의 객체를 생성하면 stub을 만들 수 있다. 이를 사용해서 프로토 파일에 있는 함수를 호출할 수 있다.

 

stub를 만든 뒤, message를 stub의 함수에 담아서 전달하고 response를 받는다.

 

 

2) bidirectional streaming gRPC

stream은 list와 같이 여러 데이터를 가지고 반복문을 통해 꺼낼 수 있는 타입이다.

이때 iterator를 사용해 리스트에 있는 자료를 반복적으로 하나씩 전달하는 역할을 한다.

리스트는 한번에 모든 데이터를 전달하지만 스트림으로 보내는 경우에는 리스트의 값을 띄엄띄엄 전달하는 것이다. 즉, 집합 데이터의 엘리먼트를 하나씩 보내는 것을 stream이라 한다.

 

Proto 버퍼 구성

 

서버 개발

iterator와 같이 이를 처리한다.

 

리턴값을 list로 보내는 것과 stream 타입(iterator)으로 전달하는 것은 차이가 있다.

list로 보내는 것은 list의 문법을 사용해서 이를 처리할 수 있다. 이때는 한번에 데이터를 보낸다. 하지만 stream을 사용해 이를 처리하면 여러번에 걸쳐서 보내야한다. 이때 yield 문법을 사용한다. 

 

yield

엘리먼트를 하나하나 돌려줄 때 yield를 사용한다. 모든 값을 한번에 리턴하기 위해선 return을 사용하면 된다. 이를 사용해서 stream하게 끊임없이 값을 전달할 수 있다는 것을 보여준다.

 

stream으로 값을 보내기에 끊임없이 무언가를 생성하면서 값을 보낼 수 있다.

 

클라이언트 개발

proto 파일에서의 service 이름이 Bidirectional이다. 이를 클라이언트에서 사용하기에 Stub이 뒤에 붙는다.

 

generate_messages 함수를 호출한다. 이때 yield 키워드를 사용해 값을 리턴한다. 따라서 반복적으로 리스트처럼 처리할 수 있으나 생성되는 시점이 끊임없이 값을 만들어내서 전달하게 된다. 

 

run 함수를 통해 통신 채널을 연다. 이후 bididrectionalStub에 연결해준다. 이후 stub을 통해 메세지를 보낸다. 클라이언트는 5개의 문자열을 리스트에서 하나씩 꺼내서 yield로 서버에게 데이터를 계속 전달한다. 라이브로 이를 보낸다면 현재 카메라에서 받은 것을 끊임없이 값으로 전달할 수 있다.

 

 grpc에서 스트림은 iterator을 통해 전달받은 것을 양방향으로 전달하는 것을 의미한다.

 

실행 결과

클라이언트는 촬영한 이미지를 스트리밍을 통해 서버에게 전달하고, 서버는 프로세싱을 한 뒤 클라이언트에게 전달한다. 이렇게 진행을 하면 클라이언트의 부하가 줄어들 수 있다.

로봇이 이 예시이다. 로봇은 입력, 출력만 할 수 있도록 경량화되어있고, 처리는 서버에서 진행한다. 스트리밍 게임도 이와 유사하다. 최근에는 use case를 많이 볼 수 있다.

 

3) Client Streaming gRPC

클라이언트가 스트리밍을 진행하는 경우

서버에게 전달하는 입력 파라미터를 stream으로 정의한다.

 

Proto buffer 구성

 

서버

클라이언트가 stream으로 전달했기에 이를 센 뒤, 리턴하는 작업을 한다.

 

 

클라이언트

위와 유사하다. 이때 달라진 점은 서버로부터 전달받은 정보를 리턴값으로 받아 바로 출력하는 부분이다.

 

클라이언트와 서버의 입장에서 주고받는 데이터의 유형에 집중한 것이 gRPC이다.

cf) zeroMQ는 클라이언트와 서버의 관계에 집중한 것이다.

 

4) Server Streaming gRPC

클라이언트가 request를 하나 보내면 server에서 response를 줄 때 stream으로 전달한다.

 

Proto 버퍼 구성

 

서버

반복문을 사용해 이를 처리했다. 숫자를 전달받으면 그만큼 메세지를 만들어서 처리할 수 있다.

(위에는 내용이 없음)

 

클라이언트 개발

 

cf) stream: 유닉스에서 여러 명령어를 입력할 때 하나의 프로그램의 결과는 다른 프로그램의 결과로 전달할 수 있다. 이때 |를 사용해서 전달할 수 있다. 이를 stream이라 한다. 이는 명령어 뿐만 아니라 i/o 장치에 대해 표현할 때도 등장한다.

ex) ls -al | grep 'go'

 


4. Protobuf 이해

1) Protobuf 개요

프로토 버퍼는 객체데이터를 상대방에게 보내는 툴 중 하나이다. 즉, 단독 프로그램처럼 여길 수 있다. 또한 이는 gRPC에서 사용하기에 이에 대해 이해할 필요가 있다. 네트워크 입장에서는 정보를 주고 받는 두 컴퓨터가 사용하는 언어가 뭐든지 간에 상관없이 전달할 수 있도록 만들었다.

 

Protocol Buffer

A라는 프로그램이 B라는 프로그램에게 데이터를 전달할 때 A에는 데이터를 빼고, B에서는 데이터를 집어 넣는다. protobuf는 전통적인 컴퓨터 네트워크 방식에 맞춰 데이터를 전송할 수 있도록 만들었다. 이에 따라 bit operation을 지원한다. json은 사람이 데이터를 읽고 처리할 수 있도록 했다.  또한 protobuf는 언어나 운영체제에 독립적으로 사용할 수 있다. 

 

serialization

복잡한 데이터 형태를 운영체제나 언어와 상관없이 데이터를 보내도 한줄의 비트로 만드는 것을 의미한다. 이는 json도 가지고 있는 특징이다.

 

IDL compiler

프로토버퍼는 언어와 상관없이 만들 수 있도록 인터페이스를 제공하는데, 이때 사용하는 컴파일러이다.

 

2) Protobuf 기능

구조화된 데이터를 serialize하고, 오픈소스로 제공한다. 언어나 OS에 상관없이 사용할 수 있다.

서로 다른 환경에서 네트워크를 사용해 작업을 처리할 수 있도록 돕는다.

프로그래밍 언어와 상관없이 데이터를 표현(IDL)하고, 이후 네트워킹을 하는 작업이 Protobuf의 역할이다. 

OS/언어와 독립적으로 데이터를 저장하고 읽을 때도 사용한다.

 

3) Protobuf 동작 원리

TLV(Type Length Value)의 형태로 데이터를 전달한다. 프로토 버퍼는 os/언어에 독립적으로 동작시키기 위해서 하드웨어적으로 처리해야하는 부분까지 모두 고려한다. 개발자는 이를 크게 고려하지 않아도 되지만 하드웨어적으로 모두 이를 고려하기에 grpc는 매우 간단하게 이를 처리할 수 있게 된다.

 

 

required/optional을 해서 필수적으로 들어가야하는 메세지와 옵션으로 만드는 메세지 정보를 처리한다.

type을 통해 데이터의 유형을 처리한다. ex) 010 -> string, 000 -> 다양하게 처리 가능

length는 데이터의 길이를 의미한다.

value는 0과 1의 조합으로 진행이 되고, 이는 비트를 의미한다. 

 

바이트 수가 많아지면 어떤 식으로 전달할지를 프로토콜 버퍼는 정의한다. 이에 따라 뒤에서부터 전달하기에 컴퓨터에서는 뒤에서부터 데이터를 저장한다. 

 

데이터로 값을 표현하고, 데이터를 만들어서 저장하고, 데이터를 읽는 예시가 나와있다.


5. gRPC 장점 & 단점

1) gRPC 장점

(1) protobuf의 사용

  • 언어와 CPU 등에대해 전혀 신경쓰지 않아도 된다.
  • 바이너리 형태로 전달하기에 데이터를 작게 처리해서 보낼 수 있다. ex) int32로 처리 가능하기에 줄일 수 있다.
  • 서버와 클라이언트에서는 빠르게 동작할 수 있다. 0과 1의 조합으로 처리하기에 컴퓨터 입장에서는 빠르게 처리가 가능하다.

(2) HTTP/2 기반

gRPC는 HTTP/2에서 사용하는 최초의 어플리케이션이다. 이를 사용하면 REST를 사용하지 않고도 처리가 가능하다. HTTP/2의 장점을 가지고 있다. (바이너리 프레임과 압축/ 다중화 한다.) 컴퓨터의 복잡도는 늘어나지만 전송 링크 상에서는 데이터가 줄어든다.

 

(3) Code generation

불러야하는 클래스나 메세지의 클래스 등등에 대해서는 루틴하게 작업만 한다면 코드를 만들 수 있다.

 

(4) Strict specification

룰이 정해져있어 처리해야하는 작업이 많지 않다.

REST, RESTful을 하기 위해서는 HTTP을 알고 있어야한다. 하지만 gRPC는 HTTP/2를 알 필요가 없어진다.또한 프로토버퍼를 알 필요도 없어진다. 따라서 gRPC는 함수 작성법 등등의 룰만 지키면 처리가 가능하다. 덕분에 개발 시간을 줄일 수 있다.

 

(5) Streaming 가능

HTTP 1.1은 동영상을 스트리밍을 하긴 하지만 gRPC는 양방향으로 Streaming을 통해 이를 처리할 수 있다.

 

(6) Deadline/timeouts & cancellation

클라이언트가 서버에게 요청한 작업의 데드라인을 걸 수 있다. 데드라인을 맞추지 못하면 클라이언트가 이를 끝내버릴 수 있다. 서버는 데드라인에 따라 우선순위를 처리할 수 있게 된다.

 

 

2) gRPC 권장 사용 시나리오

(1) Microservices

도커나 쿠버네티스를 사용한 서버들 간의 통신을 진행할 때 많이 사용한다.

 

(2) Point-to-point real time communication

양방향으로 통신을 하는 경우 주기적으로 모니터링을 하기에 polling을 할 필요가 없게 된다.

 

(3) Polyglot

여러 다른 언어를 사용해 통신을 할 수 있다. 

 

(4) Network constrained environments

네트워크 환경이 좋지 않을 때 0과 1의 비트로 보내기에 전송량이 적어진다.

 

(5) Inter-process communication(IPC)

한 컴퓨터 내에 여러 프로세스 간에 통신을 하는 경우에도 유리하다.

 

 

 

3) gRPC의 단점

(1) Limited browser support

서버에 접속할 때에는 브라우저가 알아서 우선순위를 둬서 H/2로 접속할지 HTTP/1.1로 접속할지를 알아서 정한다. 하지만 gRPC는 아직 브라우저 위에서는 동작하지 않는다. 

 

(2) Not human readable

사람이 이를 읽고 검증해야할 때는 디코드해서 처리해야하기에 가독성이 좋지 않다.

 

(3) 성능 문제

y는 메세제의 양을 의미한다.

단순 request, reply에서 grpc를 사용하는 것은 좋은 방법이 아닐 수 있다. 이는 HTTP 2.0에서 보안을 강조했기 때문에 성능이 떨어지게 된다.

 

 

언어별로 사용 목적이 다르듯이 gRPC도 목적에 부합하는가 부합하지 않은가를 따져서 이를 확인한다. 따라서 이에 따라 성능을 정성적/정량적으로 증명해서 gRPC를 사용할 수 있다.

728x90
반응형