클라이언트

클라이언트 (Client)

HBase 클라이언트가 관심 있는 행 범위를 서빙하는 RegionServer를 찾는 방법부터, Cluster Connections, 비동기 클라이언트/Admin, Master Registry, Rpc Connection Registry, Connection URI까지 살펴볼게요. 클라이언트는 메타데이터를 캐싱해 성능을 높이고, 어떤 연결 레지스트리를 쓰느냐에 따라 연결 설정 방식이 달라져요.

출처: 문서

본문

HBase 클라이언트는 관심 있는 특정 행 범위를 서빙하는 RegionServer를 찾아요. 이를 위해 hbase:meta 테이블을 조회해요. 자세한 내용은 hbase:meta를 참고해 주세요. 필요한 region을 찾은 후에는 마스터를 거치지 않고 그 region을 서빙하는 RegionServer에 직접 접촉해 읽기/쓰기 요청을 보내요. 이 정보는 클라이언트에 캐시되어 이후 요청은 조회 과정을 거치지 않아도 돼요. region이 마스터 로드 밸런서에 의해 재할당되거나 RegionServer가 죽어서 재할당되면, 클라이언트는 사용자 region의 새 위치를 알아내기 위해 카탈로그 테이블을 다시 조회해요.

Master가 HBase 클라이언트 통신에 미치는 영향에 대한 자세한 내용은 Runtime Impact를 참고해 주세요.

관리 기능은 Admin 인스턴스를 통해 수행돼요.

클러스터 연결 (Cluster Connections)

API는 HBase 1.0에서 변경되었어요. 연결 구성 정보는 HBase 클러스터에 연결하는 Client configuration and dependencies를 참고해 주세요.

HBase 1.0.0 이후의 API (API as of HBase 1.0.0)

정리되었고, 사용자는 특정 타입이 아니라 작업할 Interface를 반환받아요. HBase 1.0에서는 ConnectionFactory에서 Connection 객체를 얻고, 이후 필요에 따라 Table, Admin, RegionLocator 인스턴스를 얻어요. 사용이 끝나면 얻은 인스턴스를 닫아요. 마지막으로 종료하기 전에 Connection 인스턴스를 정리하는 것을 잊지 마세요. Connection은 무거운 객체이지만 스레드 안전하므로 애플리케이션에서 하나를 만들어 유지할 수 있어요. Table, Admin, RegionLocator 인스턴스는 가볍고, 필요할 때 만들고 닫아서 바로 버려요. 새 HBase 1.0 API 사용 예시는 Client Package Javadoc Description을 참고해 주세요.

HBase 1.0.0 이전의 API (API before HBase 1.0.0)

HTable 인스턴스는 1.0.0보다 이전의 HBase 클러스터와 상호작용하는 방식이에요. Table 인스턴스는 스레드 안전하지 않아요. 한 번에 한 스레드만 Table 인스턴스를 사용할 수 있어요. Table 인스턴스를 만들 때는 같은 HBaseConfiguration 인스턴스를 사용하는 것이 좋아요. 이렇게 하면 보통 원하는 대로 RegionServer에 대한 ZooKeeper와 소켓 인스턴스가 공유돼요. 예를 들어, 다음이 선호돼요.

HBaseConfiguration conf = HBaseConfiguration.create();
HTable table1 = new HTable(conf, "myTable");
HTable table2 = new HTable(conf, "myTable");

반면 이건 접근하지 않아요.

HBaseConfiguration conf1 = HBaseConfiguration.create();
HTable table1 = new HTable(conf1, "myTable");
HBaseConfiguration conf2 = HBaseConfiguration.create();
HTable table2 = new HTable(conf2, "myTable");

HBase 클라이언트에서 연결이 어떻게 처리되는지에 대한 자세한 내용은 ConnectionFactory를 참고해 주세요.

연결 풀링 (Connection Pooling)

높은 수준의 멀티스레드 접근이 필요한 애플리케이션(예: 단일 JVM에서 많은 애플리케이션 스레드를 서빙하는 웹 서버나 애플리케이션 서버)은 다음 예시처럼 Connection을 미리 만들 수 있어요.

Example 24. Pre-Creating a Connection

// Create a connection to the cluster.
Configuration conf = HBaseConfiguration.create();
try (Connection connection = ConnectionFactory.createConnection(conf);
     Table table = connection.getTable(TableName.valueOf(tablename))) {
  // use table as needed, the table returned is lightweight
}

HTablePool은 Deprecated

이 가이드의 이전 버전은 HTablePool에 대해 다뤘는데, 이는 HBase 0.94, 0.95, 0.96에서 폐기되고 0.98.1에서 HBASE-6580에 의해 제거되었으며, HConnection은 HBase 1.0에서 Connection으로 대체되어 폐기됐어요. Connection을 대신 사용해 주세요.

WriteBuffer와 배치 메서드 (WriteBuffer and Batch Methods)

HBase 1.0 이상에서 HTable은 Table을 위해 폐기됐어요. Table은 autoflush를 사용하지 않아요. 버퍼링된 쓰기를 하려면 BufferedMutator 클래스를 사용해 주세요.

HBase 2.0 이상에서 HTable은 Put 연산을 실행하는 데 BufferedMutator를 사용하지 않아요. 자세한 내용은 HBASE-18500을 참고해 주세요.

쓰기 내구성에 대한 추가 정보는 ACID semantics 페이지를 참고해 주세요.

Put 또는 Delete의 배치에 대한 세밀한 제어는 Table의 batch 메서드를 참고해 주세요.

비동기 클라이언트 (Asynchronous Client)

HBase 2.0에서 도입된 새 API로, HBase에 비동기적으로 접근할 수 있는 능력을 제공하는 것이 목표예요.

ConnectionFactory에서 AsyncConnection을 얻고, 여기서 비동기 테이블 인스턴스를 얻어 HBase에 접근할 수 있어요. 사용이 끝나면 AsyncConnection 인스턴스를 닫아요(보통 프로그램이 종료될 때).

비동기 테이블의 대부분 메서드는 이전 Table 인터페이스와 같은 의미지만, 보통 반환 값이 CompletableFuture로 감싸져 있어요. 여기에는 버퍼가 없어서 비동기 테이블에는 close 메서드가 없고 닫을 필요가 없어요. 그리고 스레드 안전해요.

scan에는 몇 가지 차이점이 있어요.

  • 여전히 ResultScanner를 반환하는 getScanner 메서드가 있어요. 이전 방식으로 사용할 수 있으며 이전 ClientAsyncPrefetchScanner처럼 동작해요.
  • 모든 결과를 한 번에 반환하는 scanAll 메서드가 있어요. 보통 전체 결과를 한 번에 얻고 싶은 작은 스캔을 위한 더 간단한 방법을 제공하려는 것이에요.
  • Observer 패턴. ScanResultConsumer를 매개변수로 받는 scan 메서드가 있어요. 결과를 컨슈머에 전달해요.

AsyncTable 인터페이스는 템플릿화되어 있음을 주의해 주세요. 템플릿 매개변수는 스캔에 사용되는 ScanResultConsumerBase의 타입을 지정하며, 즉 observer 스타일 스캔 API가 서로 다르다는 뜻이에요. 두 가지 유형의 스캔 컨슈머는 ScanResultConsumer와 AdvancedScanResultConsumer예요.

ScanResultConsumer는 반환된 CompletableFuture에 등록된 콜백을 실행하는 데 사용되는 별도의 스레드 풀이 필요해요. 별도 스레드 풀을 사용하면 RPC 스레드가 해방되므로 콜백은 무엇이든 자유롭게 할 수 있어요. 콜백이 빠르지 않거나 확신이 없을 때 사용해 주세요.

AdvancedScanResultConsumer는 프레임워크 스레드 안에서 콜백을 실행해요. 콜백에서 시간이 오래 걸리는 작업을 하는 것은 허용되지 않아요. 그렇지 않으면 프레임워크 스레드를 막아 매우 나쁜 성능 영향을 줄 가능성이 있어요. 이름 그대로 고성능 코드를 쓰고 싶은 고급 사용자를 위해 설계된 것이에요. 그것으로 완전히 비동기적인 코드를 쓰는 방법은 org.apache.hadoop.hbase.client.example.HttpProxyExample을 참고해 주세요.

비동기 Admin (Asynchronous Admin)

ConnectionFactory에서 AsyncConnection을 얻고, 여기서 AsyncAdmin 인스턴스를 얻어 HBase에 접근할 수 있어요. AsyncAdmin 인스턴스를 얻는 getAdmin 메서드가 두 개 있음을 주의해 주세요. 한 메서드는 콜백을 실행하는 데 사용되는 추가 스레드 풀 매개변수가 하나 있어요. 일반 사용자를 위해 설계되었어요. 다른 메서드는 스레드 풀이 필요 없고 모든 콜백이 프레임워크 스레드 안에서 실행되므로 콜백에서 시간이 오래 걸리는 작업을 하는 것은 허용되지 않아요. 고급 사용자를 위해 설계되었어요.

기본 getAdmin 메서드는 기본 구성(default configs)을 사용하는 AsyncAdmin 인스턴스를 반환해요. 일부 구성을 커스터마이즈하려면 getAdminBuilder 메서드로 AsyncAdmin 인스턴스를 만드는 AsyncAdminBuilder를 얻을 수 있어요. 사용자는 자신이 신경 쓰는 구성만 설정해 새 AsyncAdmin 인스턴스를 만들 수 있어요.

AsyncAdmin 인터페이스의 대부분 메서드는 이전 Admin 인터페이스와 같은 의미이지만, 보통 반환 값이 CompletableFuture로 감싸져 있어요.

대부분의 admin 연산에서 반환된 CompletableFuture가 완료되면 admin 연산도 완료된 것을 의미해요. 하지만 compact 연산에서는 compact 요청이 HBase로 전송되었음을 의미하며 compact 연산이 끝나려면 시간이 걸릴 수 있어요. rollWALWriter 메서드도 마찬가지로 rollWALWriter 요청이 region server로 전송되었음을 의미하며 rollWALWriter 연산이 끝나려면 시간이 걸릴 수 있어요.

region 이름의 경우 매개변수 타입으로 byte[]만 받아요. 이는 전체 region 이름일 수도 있고 인코딩된 region 이름일 수도 있어요. server 이름은 ServerName 타입만, table 이름은 TableName 타입만 받아요. list* 연산의 경우 정규식 매칭을 원하면 Pattern 타입만 받아요.

외부 클라이언트 (External Clients)

비-Java 클라이언트와 커스텀 프로토콜에 대한 정보는 Apache HBase External APIs에서 다뤄요.

Master Registry (2.3.0부터 새로 추가)

2.5.0부터 MasterRegistry는 폐기됐어요. 그 기능은 RpcConnectionRegistry로 완전히 대체됐어요. 자세한 내용은 Rpc Connection Registry (new as of 2.5.0)를 참고해 주세요.

클라이언트는 내부적으로 연결에 필요한 메타데이터를 가져오기 위해 연결 레지스트리(connection registry)와 함께 작동해요. 이 연결 레지스트리 구현은 다음 메타데이터를 가져올 책임이 있어요.

  • 활성 master 주소
  • 현재 meta region 위치
  • 클러스터 ID(이 클러스터에 고유)

이 정보는 연결 설정, 스캔, get 등 다양한 클라이언트 연산의 일부로 필요해요. 전통적으로 연결 레지스트리 구현은 ZooKeeper를 진실의 원천으로 사용했고, 클라이언트는 ZooKeeper 쿼럼에서 직접 메타데이터를 가져왔어요. HBase 2.3.0은 Master와 직접 통신하는 새 연결 레지스트리 구현을 도입해요. 이 구현으로 클라이언트는 ZooKeeper에 연결을 유지하는 대신 master RPC 엔드포인트를 통해 필요한 메타데이터를 가져와요. 이 변경은 다음 이유로 이루어졌어요.

  • 클러스터 운영에 중요한 ZooKeeper의 부하를 줄인다.
  • 새 레지스트리가 모든 클라이언트 연산을 HBase rpc 프레임워크 아래로 가져오므로 전반적인 클라이언트 타임아웃·재시도 구성을 통합한다.
  • HBase 클라이언트 라이브러리에서 ZooKeeper 클라이언트 의존성을 제거한다.

이는 다음을 의미해요.

  • 클러스터 연결 설정에는 최소 하나의 활성 또는 대기 master가 필요해요. 자세한 내용은 Runtime Impact를 참고해 주세요.
  • master는 읽기/쓰기 연산의 중요 경로(critical path)에 있을 수 있어요. 특히 클라이언트 메타데이터 캐시가 비어 있거나 낡았을 때 그렇지만.
  • 클라이언트가 ZooKeeper 앙상블 대신 HMaster와 직접 통신하므로 이전보다 master에 더 높은 연결 부하가 걸려요.

단일 master에 대한 hot-spotting을 줄이기 위해 모든 master(활성 및 대기)가 연결 메타데이터를 가져오는 데 필요한 서비스를 노출해요. 덕분에 클라이언트는 (활성뿐 아니라) 어떤 master에도 연결할 수 있어요. ZooKeeper 기반과 Master 기반 연결 레지스트리 구현 모두 2.3+에서 사용 가능해요. 2.x 및 이전에서는 ZooKeeper 기반 구현이 기본 구성으로 남아 있어요. 3.0.0에서는 MasterRegistry의 대안으로 RpcConnectionRegistry가 기본 구성이 돼요.

hbase.client.registry.impl에 구성된 값을 갱신해 연결 레지스트리 구현을 변경해 주세요. ZooKeeper 기반 레지스트리를 명시적으로 활성화하려면 다음을 사용해 주세요.

<property>
  <name>hbase.client.registry.impl</name>
  <value>org.apache.hadoop.hbase.client.ZKConnectionRegistry</value>
</property>

Master 기반 레지스트리를 명시적으로 활성화하려면 다음을 사용해 주세요.

<property>
  <name>hbase.client.registry.impl</name>
  <value>org.apache.hadoop.hbase.client.MasterRegistry</value>
</property>

MasterRegistry RPC hedging

MasterRegistry는 활성 및 대기 master에 걸쳐 연결 레지스트리 RPC의 hedging을 구현해요. 이를 통해 클라이언트는 여러 서버에 같은 요청을 하고, 먼저 응답한 것을 즉시 클라이언트에 반환받아요. 특히 일부 서버가 부하를 받고 있을 때 성능이 개선돼요. hedging fan out 크기는 구성 가능하며, hbase.client.master_registry.hedged.fanout 구성 키로 단일 시도에서 hedging되는 요청 수를 의미해요. 기본값은 2예요. 이 기본값으로 RPC는 2개씩 배치로 시도돼요. hedging 정책은 여전히 원시적이며 어떤 실시간 rpc 성능 지표에도 적응하지 않아요.

추가 참고 (Additional Notes)

  • 클라이언트는 단일 master의 hot-spotting을 피하기 위해 무작위 순서로 요청을 hedge해요.
  • 클러스터 내부 연결(masters ↔ regionservers)은 여전히 ZooKeeper 기반 연결 레지스트리를 사용해요.
  • 클러스터 내부 상태는 여전히 Zookeeper에서 추적되므로 ZK 가용성 요구사항은 이전과 같아요.
  • 구성 관리를 단순화하기 위해 클러스터 간 복제는 여전히 ZooKeeper 기반 연결 레지스트리를 사용해요.

자세한 구현 내용은 design doc과 HBASE-18095를 참고해 주세요.

Rpc Connection Registry (2.5.0부터 새로 추가)

Master Registry (new as of 2.3.0) 섹션에서 말했듯이, MasterRegistry에는 몇 가지 단점과 제한이 있어요. 특히 master를 읽기/쓰기 연산의 중요 경로에 놓는다는 점이에요. 이런 문제를 해결하기 위해 더 일반적인 RpcConnectionRegistry를 도입했어요.

MasterRegistry처럼 이것도 rpc 기반이며, 몇 가지 차이가 있어요.

  • Region server도 필요한 rpc 서비스를 구현하므로, master뿐 아니라 클러스터의 어떤 노드든 bootstrap 노드로 구성할 수 있어요.
  • bootstrap 노드 새로고침을 지원해 클러스터의 노드에 부하를 분산하고, bootstrap 노드에서 죽은 노드를 제거할 수 있어요.

rpc 기반 레지스트리를 명시적으로 활성화하려면 다음을 사용해 주세요.

<property>
  <name>hbase.client.registry.impl</name>
  <value>org.apache.hadoop.hbase.client.RpcConnectionRegistry</value>
</property>

bootstrap 노드를 구성하려면 다음을 사용해 주세요.

<property>
  <name>hbase.client.bootstrap.servers</name>
  <value>server1:16020,server2:16020,server3:16020</value>
</property>

구성하지 않으면 master 주소를 bootstrap 노드로 사용하도록 폴백해요.

RpcConnectionRegistry는 2.5+에서 사용 가능하며, 3.0.0에서 기본 클라이언트 레지스트리 구현이 돼요.

RpcConnectionRegistry RPC hedging

Hedged read는 여전히 지원되며, 구성 키는 이제 hbase.client.bootstrap.hedged.fanout이고 기본값은 여전히 2예요.

RpcConnectionRegistry bootstrap 노드 새로고침

기본적으로 bootstrap 노드를 새로고침하는 이유는 두 가지가 있어요.

  • 주기적으로. 클러스터의 노드에 부하를 분산하기 위한 것이에요. 두 가지 구성이 있어요.

hbase.client.bootstrap.refresh_interval_secs: 새로고침 간격(초), 기본 300. 0 이하 값은 새로고침을 비활성화한다는 뜻이에요. hbase.client.bootstrap.initial_refresh_delay_secs: 초기 새로고침 간격(초), 기본값은 hbase.client.bootstrap.refresh_interval_secs의 1/10이에요. 첫 새로고침 지연을 위해 별도 구성을 도입하려는 이유는, 최종 사용자가 클러스터의 어떤 노드든 초기 bootstrap 노드로 구성할 수 있으므로 서로 다른 최종 사용자가 같은 머신을 구성해 그 머신에 과부하가 걸릴 수 있기 때문이에요. 그래서 초기 새로고침 지연을 더 짧게 해 사용자가 우리가 연결하길 원하는 bootstrap 노드로 빠르게 전환하게 해요.

  • 노드를 요청하는 동안 연결 오류가 있으면 죽은 노드를 제거하기 위해 즉시 새로고침해요. 클러스터에 너무 많은 부하를 주지 않기 위해 hbase.client.bootstrap.min_secs_between_refreshes라는 구성이 있어 두 새로고침 사이의 최소 간격을 제어해요. 기본값은 60이지만, hbase.client.bootstrap.refresh_interval_secs를 작은 값으로 바꾸면 hbase.client.bootstrap.min_secs_between_refreshes도 hbase.client.bootstrap.refresh_interval_secs보다 작은 값으로 바꿔야 합니다. 그렇지 않으면 IllegalArgumentException이 발생한다는 점을 주의해 주세요.

(고급) rpc/master 기반 레지스트리에 문제가 있는 경우 다음 구성을 사용해 ZooKeeper 기반 연결 레지스트리 구현으로 폴백해 주세요.

<property>
  <name>hbase.client.registry.impl</name>
  <value>org.apache.hadoop.hbase.client.ZKConnectionRegistry</value>
</property>

Connection URI

2.7.0부터 URI를 통해 HBase 클러스터의 연결 정보를 지정하는 지원(우리가 "connection URI"라고 부르는 것)이 추가됐어요. URI가 지정한 클러스터에 대한 연결을 얻을 수 있도록 ConnectionFactory에 여러 메서드가 추가됐어요. 다음과 같이 보여요.

URI uri = new URI("hbase+rpc://server1:16020,server2:16020,server3:16020");
try (Connection conn = ConnectionFactory.createConnection(uri)) {
  ...
}

지원되는 스킴 (Supported Schemes)

현재 두 가지 스킴이 지원돼요. RpcConnectionRegistry용 hbase+rpc와 ZKConnectionRegistry용 hbase+zk예요. MasterRegistry는 폐기되었으므로 connection URI로 노출하지 않아요.

hbase+rpc는 다음과 같이 보여요.

hbase+rpc://server1:16020,server2:16020,server3:16020

authority 부분 server1:16020,server2:16020,server3:16020은 bootstrap 노드와 그 rpc 포트를 지정해요. 즉 과거 hbase.client.bootstrap.servers의 구성 값이에요.

hbase+zk는 다음과 같이 보여요.

hbase+zk://zk1:2181,zk2:2181,zk3:2181/hbase

authority 부분 zk1:2181,zk2:2181,zk3:2181은 zk 쿼럼, 즉 과거 hbase.zookeeper.quorum의 구성 값이에요. path 부분 /hbase는 znode parent, 즉 과거 zookeeper.znode.parent의 구성 값이에요.

URI 쿼리를 통한 구성 지정 (Specify Configuration through URI Queries)

사용자가 connection URI를 통해 연결 정보를 완전히 지정할 수 있게 하기 위해, URI 쿼리를 통해 구성 값을 지정하는 것을 지원해요. 다음과 같이 보여요.

hbase+rpc://server1:16020?hbase.client.operation.timeout=10000

이런 식으로 operation timeout을 10초로 설정할 수 있어요. connection URI에서 지정한 구성 값이 구성 파일의 값을 덮어쓴다는 점을 주의해 주세요.

나만의 Connection Registry 구현하기 (Implement Your Own Connection Registry)

서로 다른 connection registry 구현을 로드하기 위해 ServiceLoader를 사용하며, 진입점은 org.apache.hadoop.hbase.client.ConnectionRegistryURIFactory예요. 그래서 다른 스킴을 가진 나만의 ConnectionRegistryURIFactory를 구현하고 services 파일에 등록하면 런타임에 로드할 수 있어요.

Connection URI는 여전히 매우 새로운 기능이며 프로덕션에서 광범위하게 사용되지 않았으므로, API가 처음에는 자주 변경될 수 있어서 ConnectionRegistryURIFactory를 커스터마이즈하는 기능을 아직 노출하고 싶지 않아요.

정말로 나만의 connection registry를 구현하고 싶다면 위 방법을 사용할 수 있지만, 책임은 직접 져야 해요.

더 알아보기 (Learn more)

Master Registry, Rpc Connection Registry, Runtime Impact, Client Request Filters 등 HBase 클라이언트와 연결 관련 문서를 이어서 보시길 권해요.