0.95 RPC 사양

0.95 RPC 사양

이 문서는 0.95 버전에서 도입된 HBase RPC 와이어 형식과 클라이언트/서버 요청-응답 프로토콜을 설명해요. 0.95부터 모든 클라이언트/서버 통신은 Hadoop Writables 대신 protobuf 메시지로 수행돼요. RPC의 내부 동작을 이해하고 싶은 개발자에게 유용해요.

출처: 문서

본문

0.95에서 모든 클라이언트/서버 통신은 Hadoop Writables 대신 protobuf 메시지로 수행돼요. 그래서 우리 RPC 와이어 형식이 달라져요. 이 문서는 클라이언트/서버 요청/응답 프로토콜과 새로운 RPC 와이어 형식을 설명해요.

0.94와 그 이전의 RPC가 어떤지 보려면 Benoît/Tsuna의 Unofficial Hadoop / HBase RPC protocol documentation을 참고하세요. 이 사양에 어떻게 도달했는지에 대한 더 많은 배경은 HBase RPC: WIP를 참고하세요.

목표

  1. 우리가 진화시킬 수 있는 와이어 형식
  2. 서버 코어를 다시 쓰거나 현재 아키텍처를 급진적으로 바꾸지 않아도 되는 형식(나중을 위해).

TODO

  1. 현재 지정된 형식의 문제 목록과 v2에서 어디로 가고 싶은지. 예를 들어 서버 async로 이동하거나 streaming/chunking을 지원하려면 무엇을 바꿔야 할까.
  2. 어떻게 동작하는지에 대한 다이어그램
  3. 와이어 형식을 간결하게 설명하는 문법. 현재는 이 단어들과 rpc protobuf idl의 내용이 있지만, 왕복(back and forth)에 대한 문법은 rpc를 이해하는 데 도움이 될 거예요. 또한 클라이언트/서버 상호작용에 대한 작은 상태 머신은 이해(와 올바른 구현 보장)에 도움이 될 거예요.

RPC 개요

클라이언트는 연결이 성립될 때 설정 정보를 보내요. 그 후 클라이언트는 원격 서버에 대해 메서드를 호출하며 protobuf Message를 보내고 응답으로 protobuf Message를 받아요. 통신은 동기식이에요. 모든 왕복 앞에는 요청/응답의 총 길이를 담은 int가 옵니다. 선택적으로 Cells(KeyValues)는 뒤따르는 Cell 블록에서 protobuf 밖으로 전달될 수 있어요(메가바이트 단위의 KeyValues나 Cells를 protobuf할 수 없기 때문). 이 CellBlock은 인코딩되고 선택적으로 압축돼요.

관련된 protobuf에 대한 자세한 내용은 master의 RPC.proto 파일을 참고하세요.

연결 설정

클라이언트가 연결을 시작해요.

클라이언트

연결 설정 시 클라이언트는 프리엠블(preamble) 다음에 연결 헤더(connection header)를 보내요.

<preamble>

<MAGIC 4 byte integer> <1 byte RPC Format Version> <1 byte auth type>

여기서 auth method 사양이 필요해요. 그래야 auth가 활성화된 경우 연결 헤더가 인코딩될 수 있어요.

예: HBas 0x00 0x50 — 4바이트 MAGIC HBas' — 더하기 1바이트 버전(이 경우 0)과 1바이트 auth 타입 0x50(SIMPLE).

<Protobuf ConnectionHeader Message>

사용자 정보, "protocol", 그리고 클라이언트가 CellBlock을 보낼 때 사용할 인코더와 압축을 담고 있어요. CellBlock 인코더와 압축기는 연결 수명 동안 유지돼요. CellBlock 인코더는 org.apache.hadoop.hbase.codec.Codec을 구현해요. CellBlock은 또한 압축될 수 있어요. 압축기는 org.apache.hadoop.io.compress.CompressionCodec을 구현해요. 이 protobuf는 writeDelimited를 사용해 작성되므로 직렬화 길이가 담긴 pb varint가 앞에 붙어요.

서버

클라이언트가 프리엠블과 연결 헤더를 보낸 후, 성공적인 연결 설정이라면 서버는 응답하지 않아요. 응답이 없다는 것은 서버가 요청을 받고 응답을 줄 준비가 됐다는 뜻이에요. 프리엠블의 버전이나 인증이 맞지 않거나 서버가 프리엠블을 파싱하는 데 문제가 있다면, 오류를 설명하는 org.apache.hadoop.hbase.ipc.FatalConnectionException을 던지고 연결을 끊어요. 연결 헤더(연결 프리엠블 다음에 오는 protobuf 메시지)에서 클라이언트가 서버가 지원하지 않는 Service나 서버에 없는 codec을 요청하면 역시 설명과 함께 FatalConnectionException을 던져요.

요청

연결이 설정된 후 클라이언트는 요청을 보내고 서버는 응답해요.

요청은 protobuf RequestHeader 다음에 protobuf Message 파라미터로 구성돼요. 헤더는 메서드 이름과 선택적으로 뒤따를 수 있는 CellBlock에 대한 메타데이터를 포함해요. 파라미터 타입은 호출되는 메서드에 맞아요. 즉 getRegionInfo 요청을 한다면 protobuf Message param은 GetRegionInfoRequest 인스턴스가 될 거예요. 응답은 GetRegionInfoResponse가 될 거예요. CellBlock은 RPC 데이터의 대부분(Cells/KeyValues)을 운반하는 데 선택적으로 사용돼요.

요청 파트

<Total Length>

요청 앞에는 뒤따르는 것의 총 길이를 담은 int가 옵니다.

<Protobuf RequestHeader Message>

call.id, trace.id, method name 등을 담고, Cell block이 뒤따르는 경우(IFF) 그에 대한 선택적 Metadata를 포함해요. 데이터는 이 pb 메시지에 인라인으로 protobuf되거나 선택적으로 뒤따르는 CellBlock에 들어와요.

<Protobuf Param Message>

호출되는 메서드가 getRegionInfo라면, client-to-regionserver 프로토콜의 Service 디스크립터를 연구하면 이 위치에 GetRegionInfoRequest protobuf Message param이 보내진다는 것을 알 수 있어요.

<CellBlock>

인코딩되고 선택적으로 압축된 Cell 블록.

응답

요청과 마찬가지로 protobuf ResponseHeader 다음에 protobuf Message 응답이 오고, Message 응답 타입은 호출된 메서드에 맞아요. 데이터의 대부분은 뒤따르는 CellBlock에 올 수 있어요.

응답 파트

<Total Length>

응답 앞에는 뒤따르는 것의 총 길이를 담은 int가 옵니다.

<Protobuf ResponseHeader Message>

call.id 등을 담아요. 처리 실패 시 예외를 포함해요. CellBlock이 뒤따르는 경우(IFF) 선택적으로 그 메타데이터를 포함해요.

<Protobuf Response Message>

반환값이거나 예외인 경우 아무것도 아닐 수 있어요. 호출된 메서드가 getRegionInfo라면, Service 디스크립터를 연구하면 이 위치에 GetRegionInfoResponse protobuf Message param이 보내진다는 것을 알 수 있어요.

<CellBlock>

인코딩되고 선택적으로 압축된 Cell 블록.

예외

두 가지 뚜렷한 유형이 있어요. 응답의 응답 헤더 안에 캡슐화된 요청 실패가 있어요. 연결은 새 요청을 받기 위해 열려 있어요. 두 번째 유형인 FatalConnectionException은 연결을 끊어요.

예외는 추가 정보를 실을 수 있어요. ExceptionResponse protobuf 타입을 참고하세요. 재시도 금지(do-no-retry)를 나타내는 플래그와 클라이언트 응답성을 개선하는 데 도움이 되는 기타 잡다한 페이로드가 있어요.

CellBlock

CellBlock은 버전이 없어요. 서버가 codec을 할 수 있거나 없거나예요. 더 타이트한 인코딩 같은 새 버전의 codec이 있으면 새 클래스 이름을 주세요. 오래된 클라이언트가 연결할 수 있도록 Codec은 서버에 영원히 남아 있을 거예요.

참고 사항

제약

일부 부분에서 현재 와이어 형식 — 즉 모든 요청과 응답 앞에 길이가 붙는 방식 — 은 현재 서버 비-async 아키텍처에 의해 결정되었어요.

하나의 뚱뚱한 pb 요청 또는 header+param

지금은 pb header 다음에 pb param이 와서 요청을 구성하고, pb header 다음에 pb response가 오는 방식을 택했어요. header와 param 내용을 모두 담은 단일 protobuf Message 대신 header+param을 하는 이유는:

  1. 현재 우리가 가진 것에 더 가깝기 때문
  2. 단일 fat pb를 만들려면 이미 pb된 param을 fat request pb의 본문에 넣으려면 추가 복사가 필요하기 때문(결과를 만들 때도 마찬가지)
  3. param을 읽기 전에 요청을 받아들일지 결정할 수 있기 때문. 예를 들어 요청이 낮은 우선순위일 수 있어요. 현재 서버 구현상 header+param을 한 번에 읽지만 이것은 TODO예요.

장점은 작아요. 나중에 fat request에 명확한 이점이 생기면 나중에 v2를 내놓을 수 있어요.

RPC 구성

CellBlock Codec

기본 KeyValueCodec 외의 codec을 활성화하려면 hbase.client.rpc.codec를 사용하려는 Codec 클래스의 이름으로 설정하세요. Codec은 hbase의 Codec 인터페이스를 구현해야 해요. 연결 설정 후 전달되는 모든 cellblock은 이 codec으로 보내져요. 서버는 codec이 서버의 CLASSPATH에 있는 한 같은 codec으로 cellblock을 반환해요(그렇지 않으면 UnsupportedCellCodecException을 받게 돼요).

기본 codec을 바꾸려면 hbase.client.default.rpc.codec를 설정하세요.

CellBlock을 완전히 비활성화하고 순수 protobuf로 가려면 기본값을 빈 문자열로 설정하고 Configuration에서 codec을 지정하지 마세요. 즉 hbase.client.default.rpc.codec를 빈 문자열로 설정하고 hbase.client.rpc.codec를 설정하지 마세요. 그러면 클라이언트가 codec 없이 서버에 연결하게 돼요. 서버가 codec을 보지 못하면 모든 응답을 순수 protobuf로 반환해요. 항상 순수 protobuf로 실행하면 cellblock으로 실행하는 것보다 느릴 거예요.

압축

hadoop의 compression codec을 사용해요. 전달되는 CellBlock을 압축하려면 hbase.client.rpc.compressor를 사용하려는 Compressor의 이름으로 설정하세요. Compressor는 Hadoop의 CompressionCodec 인터페이스를 구현해야 해요. 연결 설정 후 전달되는 모든 cellblock은 압축되어 보내져요. 서버는 compressor가 자신의 CLASSPATH에 있는 한 같은 compressor로 cellblock을 압축해 반환해요(그렇지 않으면 UnsupportedCompressionCodecException을 받게 돼요).

더 알아보기 (Learn more)