# Apache HBase 協處理器
HBase 協處理器以 Google BigTable 的協處理器實現為模型( [http://research.google.com/people/jeff/SOCC2010-keynote-slides.pdf](http://research.google.com/people/jeff/SOCC2010-keynote-slides.pdf) 第 41-42 頁。)。
協處理器框架提供了直接在管理數據的 RegionServers 上運行自定義代碼的機制。正在努力彌合 HBase 實施與 BigTable 架構之間的差距。有關更多信息,請參閱 [HBASE-4047](https://issues.apache.org/jira/browse/HBASE-4047) 。
本章中的信息主要來源于以下資源并大量重用:
1. 賴明杰的博文[協處理器介紹](https://blogs.apache.org/hbase/entry/coprocessor_introduction)。
2. Gaurav Bhardwaj 的博客文章 [HBase 協處理器的方法](http://www.3pillarglobal.com/insights/hbase-coprocessors)。
> 使用協處理器是您自己的風險
>
> 協處理器是 HBase 的高級功能,僅供系統開發人員使用。由于協處理器代碼直接在 RegionServer 上運行并且可以直接訪問您的數據,因此會帶來數據損壞,中間人攻擊或其他惡意數據訪問的風險。目前,雖然 [HBASE-4047](https://issues.apache.org/jira/browse/HBASE-4047) 正在開展工作,但沒有機制可以防止協處理器的數據損壞。
>
> * 此外,沒有資源隔離,因此善意但行為不當的協處理器會嚴重降低集群性能和穩定性。
## 108.協處理器概述
在 HBase 中,使用`Get`或`Scan`獲取數據,而在 RDBMS 中使用 SQL 查詢。為了僅獲取相關數據,使用 HBase [過濾器](https://hbase.apache.org/apidocs/org/apache/hadoop/hbase/filter/Filter.html)進行過濾,而在 RDBMS 中使用`WHERE`謂詞。
獲取數據后,您可以對其執行計算。這種范例適用于具有幾千行和多列的“小數據”。但是,當您擴展到數十億行和數百萬列時,在網絡中移動大量數據將在網絡層產生瓶頸,客戶端需要足夠強大并且有足夠的內存來處理大量數據和計算。此外,客戶端代碼可能變得龐大而復雜。
在這種情況下,協處理器可能有意義。您可以將業務計算代碼放入在 RegionServer 上運行的協處理器中,與數據位于同一位置,并將結果返回給客戶端。
這只是使用協處理器可以帶來好處的一種情況。以下是一些類比,可能有助于解釋協處理器的一些好處。
### 108.1。協處理器類比
觸發器和存儲過程
Observer 協處理器類似于 RDBMS 中的觸發器,因為它在特定事件(例如`Get`或`Put`)發生之前或之后執行代碼。端點協處理器類似于 RDBMS 中的存儲過程,因為它允許您對 RegionServer 本身而不是客戶端上的數據執行自定義計算。
MapReduce 的
MapReduce 的工作原理是將計算移動到數據的位置。協處理器在相同的主體上運行。
AOP
如果您熟悉面向方面編程(AOP),您可以將協處理器視為通過攔截請求然后運行一些自定義代碼來應用建議,然后將請求傳遞到其最終目標(甚至更改目標)。
### 108.2。協處理器實現概述
1. 你的類應該實現一個協處理器接口 - [協處理器](https://hbase.apache.org/devapidocs/org/apache/hadoop/hbase/Coprocessor.html), [RegionObserver](https://hbase.apache.org/devapidocs/org/apache/hadoop/hbase/coprocessor/RegionObserver.html) , [CoprocessorService](https://hbase.apache.org/devapidocs/org/apache/hadoop/hbase/coprocessor/CoprocessorService.html) - 僅舉幾例。
2. 使用 HBase Shell 靜態(從配置)或動態加載協處理器。有關詳細信息,請參閱[加載協處理器](#cp_loading)。
3. 從客戶端代碼調用協處理器。 HBase 透明地處理協處理器。
框架 API 在[協處理器](https://hbase.apache.org/apidocs/org/apache/hadoop/hbase/coprocessor/package-summary.html)包中提供。
## 109.協處理器的類型
### 109.1。觀察者協處理器
在特定事件發生之前或之后觸發觀察者協處理器。在事件之前發生的觀察者使用以`pre`前綴開頭的方法,例如 [`prePut`](https://hbase.apache.org/devapidocs/org/apache/hadoop/hbase/coprocessor/RegionObserver.html#prePut-org.apache.hadoop.hbase.coprocessor.ObserverContext-org.apache.hadoop.hbase.client.Put-org.apache.hadoop.hbase.wal.WALEdit-org.apache.hadoop.hbase.client.Durability-) 。在事件之后發生的觀察者會覆蓋以`post`前綴開頭的方法,例如 [`postPut`](https://hbase.apache.org/devapidocs/org/apache/hadoop/hbase/coprocessor/RegionObserver.html#postPut-org.apache.hadoop.hbase.coprocessor.ObserverContext-org.apache.hadoop.hbase.client.Put-org.apache.hadoop.hbase.wal.WALEdit-org.apache.hadoop.hbase.client.Durability-) 。
#### 109.1.1。用于觀察者協處理器的用例
安全
在執行`Get`或`Put`操作之前,您可以使用`preGet`或`prePut`方法檢查權限。
參照完整性
HBase 不直接支持 RDBMS 的反射完整性概念,也稱為外鍵。您可以使用協處理器來強制執行此類完整性。例如,如果您有一個業務規則,`users`表的每個插入必須后跟`user_daily_attendance`表中的相應條目,您可以實現協處理器以使用`user`上的`prePut`方法插入記錄到`user_daily_attendance`。
二級索引
您可以使用協處理器來維護二級索引。有關更多信息,請參閱 [SecondaryIndexing](https://wiki.apache.org/hadoop/Hbase/SecondaryIndexing) 。
#### 109.1.2。觀察者協處理器的類型
RegionObserver
RegionObserver 協處理器允許您觀察區域上的事件,例如`Get`和`Put`操作。見 [RegionObserver](https://hbase.apache.org/devapidocs/org/apache/hadoop/hbase/coprocessor/RegionObserver.html) 。
RegionServerObserver
RegionServerObserver 允許您觀察與 RegionServer 操作相關的事件,例如啟動,停止或執行合并,提交或回滾。請參見 [RegionServerObserver](https://hbase.apache.org/devapidocs/org/apache/hadoop/hbase/coprocessor/RegionServerObserver.html) 。
MasterObserver
MasterObserver 允許您觀察與 HBase Master 相關的事件,例如表創建,刪除或架構修改。見 [MasterObserver](https://hbase.apache.org/devapidocs/org/apache/hadoop/hbase/coprocessor/MasterObserver.html) 。
WalObserver
WalObserver 允許您觀察與寫入預寫日志(WAL)相關的事件。見 [WALObserver](https://hbase.apache.org/devapidocs/org/apache/hadoop/hbase/coprocessor/WALObserver.html) 。
[示例](#cp_example)提供了觀察者協處理器的工作示例。
### 109.2。端點協處理器
端點處理器允許您在數據位置執行計算。參見[協處理器類比](#cp_analogies)。一個例子是需要計算跨越數百個區域的整個表的運行平均值或總和。
與您的代碼透明運行的觀察者協處理器相比,必須使用[表](https://hbase.apache.org/devapidocs/org/apache/hadoop/hbase/client/Table.html)或 [HTable](https://hbase.apache.org/devapidocs/org/apache/hadoop/hbase/client/HTable.html) 中提供的 [CoprocessorService()](https://hbase.apache.org/devapidocs/org/apache/hadoop/hbase/client/Table.html#coprocessorService-java.lang.Class-byte:A-byte:A-org.apache.hadoop.hbase.client.coprocessor.Batch.Call-)方法顯式調用端點協處理器。
從 HBase 0.96 開始,端點協處理器使用 Google Protocol Buffers(protobuf)實現。有關 protobuf 的更多詳細信息,請參閱 Google 的[協議緩沖指南](https://developers.google.com/protocol-buffers/docs/proto)。端點以 0.94 版本編寫的協處理器與 0.96 或更高版本不兼容。參見 [HBASE-5448](https://issues.apache.org/jira/browse/HBASE-5448) )。要將 HBase 群集從 0.94 或更早版本升級到 0.96 或更高版本,您需要重新實現協處理器。
協處理器端點不應使用 HBase 內部,只能使用公共 API;理想情況下,CPEP 應僅依賴于接口和數據結構。這并不總是可行,但要注意這樣做會使端點變脆,隨著 HBase 內部發展而易于破損。注釋為私有或演進的 HBase 內部 API 在刪除之前不必遵守語義版本控制規則或關于棄用的一般 Java 規則。雖然生成的 protobuf 文件沒有 hbase 受眾注釋 - 它們是由 protobuf protoc 工具創建的,它不知道 HBase 是如何工作的 - 它們應該被考慮`@InterfaceAudience.Private`因此容易改變。
[示例](#cp_example)提供了端點協處理器的工作示例。
## 110.加載協處理器
要使協處理器可用于 HBase,必須靜態(通過 HBase 配置)或動態(使用 HBase Shell 或 Java API)加載。
### 110.1。靜態加載
請按照以下步驟靜態加載協處理器。請記住,必須重新啟動 HBase 才能卸載已靜態加載的協處理器。
1. 在 _hbase-site.xml_ 中定義協處理器,<property>元素包含<name>和<value>子元素。 <name>應為以下之一:</name></value></name></property>
* RegionObservers 和 Endpoints 的`hbase.coprocessor.region.classes`。
* WALObservers 的`hbase.coprocessor.wal.classes`。
* MasterObservers 的`hbase.coprocessor.master.classes`。
<value>必須包含協處理器實現類的完全限定類名。</value>
例如,要加載協處理器(在類 SumEndPoint.java 中實現),您必須在 RegionServer 的'hbase-site.xml'文件中創建以下條目(通常位于'conf'目錄下):
```
<property>
<name>hbase.coprocessor.region.classes</name>
<value>org.myname.hbase.coprocessor.endpoint.SumEndPoint</value>
</property>
```
如果為加載指定了多個類,則類名必須以逗號分隔。框架嘗試使用默認的類加載器加載所有已配置的類。因此,jar 文件必須駐留在服務器端 HBase 類路徑中。
以這種方式加載的協處理器將在所有表的所有區域上處于活動狀態。這些也稱為系統協處理器。將為第一個列出的協處理器分配優先級`Coprocessor.Priority.SYSTEM`。列表中的每個后續協處理器的優先級值都會增加 1(這會降低其優先級,因為優先級具有整數的自然排序順序)。
當調用注冊的觀察者時,框架以其優先級的排序順序執行其回調方法。關系是任意破壞的。
2. 將您的代碼放在 HBase 的類路徑上。一種簡單的方法是將 jar(包含代碼和所有依賴項)放入 HBase 安裝的`lib/`目錄中。
3. 重啟 HBase。
### 110.2。靜態卸載
1. 從`hbase-site.xml`中刪除協處理器的<property>元素,包括子元素。</property>
2. Restart HBase.
3. (可選)從類路徑或 HBase 的`lib/`目錄中刪除協處理器的 JAR 文件。
### 110.3。動態加載
您也可以動態加載協處理器,而無需重新啟動 HBase。這似乎比靜態加載更好,但動態加載的協處理器是基于每個表加載的,并且只能用于加載它們的表。因此,動態加載的表有時稱為**表協處理器**。
此外,動態加載協處理器充當表上的模式更改,并且必須使表脫機以加載協處理器。
有三種方法可以動態加載協處理器。
> 假設
>
> 以下提到的說明做出以下假設:
>
> * 名為`coprocessor.jar`的 JAR 包含協處理器實現及其所有依賴項。
>
>
> * JAR 在`hdfs://<namenode>:<port>/user/<hadoop-user>/coprocessor.jar`等某些位置可用于 HDFS。
#### 110.3.1。使用 HBase Shell
1. 使用 HBase Shell 禁用表:
```
hbase> disable 'users'
```
2. 使用如下命令加載協處理器:
```
hbase alter 'users', METHOD => 'table_att', 'Coprocessor'=>'hdfs://<namenode>:<port>/
user/<hadoop-user>/coprocessor.jar| org.myname.hbase.Coprocessor.RegionObserverExample|1073741823|
arg1=1,arg2=2'
```
協處理器框架將嘗試從協處理器表屬性值中讀取類信息。該值包含由管道(`|`)字符分隔的四條信息。
* 文件路徑:包含協處理器實現的 jar 文件必須位于所有區域服務器都可以讀取它的位置。您可以將文件復制到每個區域服務器上的本地磁盤上,但建議將其存儲在 HDFS 中。 [HBASE-14548](https://issues.apache.org/jira/browse/HBASE-14548) 允許指定包含 jar 或某些通配符的目錄,例如:hdfs:// <namenode>: <port>/ user / <hadoop-user>/或 hdfs:// <namenode>: <port>/ user / <hadoop-user>/*.jar。請注意,如果指定了目錄,則會添加目錄中的所有 jar 文件(.jar)。它不搜索子目錄中的文件。如果要指定目錄,請不要使用通配符。此增強功能也適用于通過 JAVA API 的用法。</hadoop-user></port></namenode></hadoop-user></port></namenode>
* 類名:協處理器的完整類名。
* 優先級:整數。該框架將使用優先級確定在同一個鉤子上注冊的所有已配置觀察者的執行順序。此字段可以留空。在這種情況下,框架將分配默認優先級值。
* 參數(可選):此字段傳遞給協處理器實現。這是可選的。
3. 啟用表格。
```
hbase(main):003:0> enable 'users'
```
4. 驗證協處理器已加載:
```
hbase(main):04:0> describe 'users'
```
協處理器應列在`TABLE_ATTRIBUTES`中。
#### 110.3.2。使用 Java API(所有 HBase 版本)
以下 Java 代碼顯示如何使用`HTableDescriptor`的`setValue()`方法在`users`表上加載協處理器。
```
TableName tableName = TableName.valueOf("users");
String path = "hdfs://<namenode>:<port>/user/<hadoop-user>/coprocessor.jar";
Configuration conf = HBaseConfiguration.create();
Connection connection = ConnectionFactory.createConnection(conf);
Admin admin = connection.getAdmin();
admin.disableTable(tableName);
HTableDescriptor hTableDescriptor = new HTableDescriptor(tableName);
HColumnDescriptor columnFamily1 = new HColumnDescriptor("personalDet");
columnFamily1.setMaxVersions(3);
hTableDescriptor.addFamily(columnFamily1);
HColumnDescriptor columnFamily2 = new HColumnDescriptor("salaryDet");
columnFamily2.setMaxVersions(3);
hTableDescriptor.addFamily(columnFamily2);
hTableDescriptor.setValue("COPROCESSOR$1", path + "|"
+ RegionObserverExample.class.getCanonicalName() + "|"
+ Coprocessor.PRIORITY_USER);
admin.modifyTable(tableName, hTableDescriptor);
admin.enableTable(tableName);
```
#### 110.3.3。使用 Java API(僅限 HBase 0.96+)
在 HBase 0.96 及更新版本中,`HTableDescriptor`的`addCoprocessor()`方法提供了一種動態加載協處理器的簡便方法。
```
TableName tableName = TableName.valueOf("users");
Path path = new Path("hdfs://<namenode>:<port>/user/<hadoop-user>/coprocessor.jar");
Configuration conf = HBaseConfiguration.create();
Connection connection = ConnectionFactory.createConnection(conf);
Admin admin = connection.getAdmin();
admin.disableTable(tableName);
HTableDescriptor hTableDescriptor = new HTableDescriptor(tableName);
HColumnDescriptor columnFamily1 = new HColumnDescriptor("personalDet");
columnFamily1.setMaxVersions(3);
hTableDescriptor.addFamily(columnFamily1);
HColumnDescriptor columnFamily2 = new HColumnDescriptor("salaryDet");
columnFamily2.setMaxVersions(3);
hTableDescriptor.addFamily(columnFamily2);
hTableDescriptor.addCoprocessor(RegionObserverExample.class.getCanonicalName(), path,
Coprocessor.PRIORITY_USER, null);
admin.modifyTable(tableName, hTableDescriptor);
admin.enableTable(tableName);
```
> 無法保證框架將成功加載給定的協處理器。例如,shell 命令既不保證特定位置存在 jar 文件,也不驗證給定類是否實際包含在 jar 文件中。
### 110.4。動態卸載
#### 110.4.1。使用 HBase Shell
1. 禁用該表。
```
hbase> disable 'users'
```
2. 更改表以刪除協處理器。
```
hbase> alter 'users', METHOD => 'table_att_unset', NAME => 'coprocessor$1'
```
3. Enable the table.
```
hbase> enable 'users'
```
#### 110.4.2。使用 Java API
通過使用`setValue()`或`addCoprocessor()`方法重新加載表定義而不設置協處理器的值。這將刪除附加到表的任何協處理器。
```
TableName tableName = TableName.valueOf("users");
String path = "hdfs://<namenode>:<port>/user/<hadoop-user>/coprocessor.jar";
Configuration conf = HBaseConfiguration.create();
Connection connection = ConnectionFactory.createConnection(conf);
Admin admin = connection.getAdmin();
admin.disableTable(tableName);
HTableDescriptor hTableDescriptor = new HTableDescriptor(tableName);
HColumnDescriptor columnFamily1 = new HColumnDescriptor("personalDet");
columnFamily1.setMaxVersions(3);
hTableDescriptor.addFamily(columnFamily1);
HColumnDescriptor columnFamily2 = new HColumnDescriptor("salaryDet");
columnFamily2.setMaxVersions(3);
hTableDescriptor.addFamily(columnFamily2);
admin.modifyTable(tableName, hTableDescriptor);
admin.enableTable(tableName);
```
在 HBase 0.96 及更高版本中,您可以改為使用`HTableDescriptor`類的`removeCoprocessor()`方法。
## 111.例子
HBase 提供了 Observer Coprocessor 的示例。
下面給出更詳細的例子。
這些示例假設一個名為`users`的表,它有兩個列族`personalDet`和`salaryDet`,包含個人和工資詳細信息。下面是`users`表的圖形表示。
| | personalDet | salaryDet |
| --- | --- | --- |
| jverne | 儒勒 | 凡爾納 | 1828 年 2 月 8 日 | 12000 | 9000 | 3000 |
| **rowkey** | **名稱** | **姓氏** | **dob** | **總** | **net** | **補貼** |
| 管理 | 管理員 | Admin | | |
| cdickens | 查爾斯 | 狄更斯 | 1812 年 2 月 7 日 | 10000 | 8000 | 2000 |
### 111.1。觀察者示例
以下 Observer 協處理器可防止在`users`表的`Get`或`Scan`中返回用戶`admin`的詳細信息。
1. 編寫一個實現 [RegionObserver](https://hbase.apache.org/devapidocs/org/apache/hadoop/hbase/coprocessor/RegionObserver.html) 類的類。
2. 覆蓋`preGetOp()`方法(不推薦使用`preGet()`方法)以檢查客戶端是否已使用值`admin`查詢 rowkey。如果是,則返回空結果。否則,正常處理請求。
3. 將您的代碼和依賴項放在 JAR 文件中。
4. 將 JAR 放在 HDFS 中,HBase 可以在其中找到它。
5. 加載協處理器。
6. 寫一個簡單的程序來測試它。
以下是上述步驟的實施:
```
public class RegionObserverExample implements RegionObserver {
private static final byte[] ADMIN = Bytes.toBytes("admin");
private static final byte[] COLUMN_FAMILY = Bytes.toBytes("details");
private static final byte[] COLUMN = Bytes.toBytes("Admin_det");
private static final byte[] VALUE = Bytes.toBytes("You can't see Admin details");
@Override
public void preGetOp(final ObserverContext<RegionCoprocessorEnvironment> e, final Get get, final List<Cell> results)
throws IOException {
if (Bytes.equals(get.getRow(),ADMIN)) {
Cell c = CellUtil.createCell(get.getRow(),COLUMN_FAMILY, COLUMN,
System.currentTimeMillis(), (byte)4, VALUE);
results.add(c);
e.bypass();
}
}
}
```
覆蓋`preGetOp()`僅適用于`Get`操作。您還需要覆蓋`preScannerOpen()`方法以從掃描結果中過濾`admin`行。
```
@Override
public RegionScanner preScannerOpen(final ObserverContext<RegionCoprocessorEnvironment> e, final Scan scan,
final RegionScanner s) throws IOException {
Filter filter = new RowFilter(CompareOp.NOT_EQUAL, new BinaryComparator(ADMIN));
scan.setFilter(filter);
return s;
}
```
這種方法有效,但有 _ 副作用 _。如果客戶端在其掃描中使用了過濾器,則該過濾器將替換該過濾器。相反,您可以顯式刪除掃描中的任何`admin`結果:
```
@Override
public boolean postScannerNext(final ObserverContext<RegionCoprocessorEnvironment> e, final InternalScanner s,
final List<Result> results, final int limit, final boolean hasMore) throws IOException {
Result result = null;
Iterator<Result> iterator = results.iterator();
while (iterator.hasNext()) {
result = iterator.next();
if (Bytes.equals(result.getRow(), ROWKEY)) {
iterator.remove();
break;
}
}
return hasMore;
}
```
### 111.2。端點示例
仍然使用`users`表,此示例使用端點協處理器實現協處理器以計算所有員工工資的總和。
1. 創建一個定義服務的'.proto'文件。
```
option java_package = "org.myname.hbase.coprocessor.autogenerated";
option java_outer_classname = "Sum";
option java_generic_services = true;
option java_generate_equals_and_hash = true;
option optimize_for = SPEED;
message SumRequest {
required string family = 1;
required string column = 2;
}
message SumResponse {
required int64 sum = 1 [default = 0];
}
service SumService {
rpc getSum(SumRequest)
returns (SumResponse);
}
```
2. 執行`protoc`命令從上面的.proto'文件生成 Java 代碼。
```
$ mkdir src
$ protoc --java_out=src ./sum.proto
```
這將生成一個類調用`Sum.java`。
3. 編寫一個擴展生成的服務類的類,實現`Coprocessor`和`CoprocessorService`類,并覆蓋服務方法。
> 如果從`hbase-site.xml`加載協處理器,然后使用 HBase Shell 再次加載同一個協處理器,它將再次加載。同一個類將存在兩次,第二個實例將具有更高的 ID(因此具有更低的優先級)。結果是有效地忽略了重復的協處理器。
```
public class SumEndPoint extends Sum.SumService implements Coprocessor, CoprocessorService {
private RegionCoprocessorEnvironment env;
@Override
public Service getService() {
return this;
}
@Override
public void start(CoprocessorEnvironment env) throws IOException {
if (env instanceof RegionCoprocessorEnvironment) {
this.env = (RegionCoprocessorEnvironment)env;
} else {
throw new CoprocessorException("Must be loaded on a table region!");
}
}
@Override
public void stop(CoprocessorEnvironment env) throws IOException {
// do nothing
}
@Override
public void getSum(RpcController controller, Sum.SumRequest request, RpcCallback<Sum.SumResponse> done) {
Scan scan = new Scan();
scan.addFamily(Bytes.toBytes(request.getFamily()));
scan.addColumn(Bytes.toBytes(request.getFamily()), Bytes.toBytes(request.getColumn()));
Sum.SumResponse response = null;
InternalScanner scanner = null;
try {
scanner = env.getRegion().getScanner(scan);
List<Cell> results = new ArrayList<>();
boolean hasMore = false;
long sum = 0L;
do {
hasMore = scanner.next(results);
for (Cell cell : results) {
sum = sum + Bytes.toLong(CellUtil.cloneValue(cell));
}
results.clear();
} while (hasMore);
response = Sum.SumResponse.newBuilder().setSum(sum).build();
} catch (IOException ioe) {
ResponseConverter.setControllerException(controller, ioe);
} finally {
if (scanner != null) {
try {
scanner.close();
} catch (IOException ignored) {}
}
}
done.run(response);
}
}
```
```
Configuration conf = HBaseConfiguration.create();
Connection connection = ConnectionFactory.createConnection(conf);
TableName tableName = TableName.valueOf("users");
Table table = connection.getTable(tableName);
final Sum.SumRequest request = Sum.SumRequest.newBuilder().setFamily("salaryDet").setColumn("gross").build();
try {
Map<byte[], Long> results = table.coprocessorService(
Sum.SumService.class,
null, /* start key */
null, /* end key */
new Batch.Call<Sum.SumService, Long>() {
@Override
public Long call(Sum.SumService aggregate) throws IOException {
BlockingRpcCallback<Sum.SumResponse> rpcCallback = new BlockingRpcCallback<>();
aggregate.getSum(null, request, rpcCallback);
Sum.SumResponse response = rpcCallback.get();
return response.hasSum() ? response.getSum() : 0L;
}
}
);
for (Long sum : results.values()) {
System.out.println("Sum = " + sum);
}
} catch (ServiceException e) {
e.printStackTrace();
} catch (Throwable e) {
e.printStackTrace();
}
```
4. Load the Coprocessor.
5. 編寫客戶端代碼以調用協處理器。
## 112.部署協處理器的準則
捆綁協處理器
您可以將協處理器的所有類捆綁到 RegionServer 的類路徑上的單個 JAR 中,以便于部署。否則,將所有依賴項放在 RegionServer 的類路徑中,以便在 RegionServer 啟動期間加載它們。 RegionServer 的類路徑在 RegionServer 的`hbase-env.sh`文件中設置。
自動化部署
您可以使用 Puppet,Chef 或 Ansible 等工具將協處理器的 JAR 發送到 RegionServers 文件系統上的所需位置,然后重新啟動每個 RegionServer,以自動執行協處理器部署。此類設置的詳細信息超出了本文檔的范圍。
更新協處理器
部署新版本的給定協處理器并不像禁用它,更換 JAR 和重新啟用協處理器那么簡單。這是因為除非刪除對它的所有當前引用,否則無法在 JVM 中重新加載類。由于當前 JVM 引用了現有的協處理器,因此必須通過重新啟動 RegionServer 來重新啟動 JVM,以便替換它。預計此行為不會更改。
協處理器日志記錄
協處理器框架不提供用于超出標準 Java 日志記錄的 API。
協處理器配置
如果您不想從 HBase Shell 加載協處理器,可以將其配置屬性添加到`hbase-site.xml`。在[中使用 HBase Shell](#load_coprocessor_in_shell) ,設置了兩個參數:`arg1=1,arg2=2`。這些可以添加到`hbase-site.xml`中,如下所示:
```
<property>
<name>arg1</name>
<value>1</value>
</property>
<property>
<name>arg2</name>
<value>2</value>
</property>
```
然后,您可以使用以下代碼讀取配置:
```
Configuration conf = HBaseConfiguration.create();
Connection connection = ConnectionFactory.createConnection(conf);
TableName tableName = TableName.valueOf("users");
Table table = connection.getTable(tableName);
Get get = new Get(Bytes.toBytes("admin"));
Result result = table.get(get);
for (Cell c : result.rawCells()) {
System.out.println(Bytes.toString(CellUtil.cloneRow(c))
+ "==> " + Bytes.toString(CellUtil.cloneFamily(c))
+ "{" + Bytes.toString(CellUtil.cloneQualifier(c))
+ ":" + Bytes.toLong(CellUtil.cloneValue(c)) + "}");
}
Scan scan = new Scan();
ResultScanner scanner = table.getScanner(scan);
for (Result res : scanner) {
for (Cell c : res.rawCells()) {
System.out.println(Bytes.toString(CellUtil.cloneRow(c))
+ " ==> " + Bytes.toString(CellUtil.cloneFamily(c))
+ " {" + Bytes.toString(CellUtil.cloneQualifier(c))
+ ":" + Bytes.toLong(CellUtil.cloneValue(c))
+ "}");
}
}
```
## 113.限制協處理器使用
在多租戶環境中,限制任意用戶協處理器可能是一個大問題。 HBase 提供了連續的選項,以確保只有預期的協處理器運行:
* `hbase.coprocessor.enabled`:啟用或禁用所有協處理器。這將限制 HBase 的功能,因為禁用所有協處理器將禁用某些安全提供程序。受影響的示例 coproccessor 是`org.apache.hadoop.hbase.security.access.AccessController`。
* `hbase.coprocessor.user.enabled`:啟用或禁用在表(即用戶協處理器)上加載協處理器。
* 可以通過`hbase-site.xml`中的以下可調參數靜態加載協處理器:
* `hbase.coprocessor.regionserver.classes`:由區域服務器加載的以逗號分隔的協處理器列表
* `hbase.coprocessor.region.classes`:RegionObserver 和 Endpoint 協處理器的逗號分隔列表
* `hbase.coprocessor.user.region.classes`:由所有區域加載的以逗號分隔的協處理器列表
* `hbase.coprocessor.master.classes`:由主服務器(MasterObserver 協處理器)加載的以逗號分隔的協處理器列表
* `hbase.coprocessor.wal.classes`:要加載的以逗號分隔的 WALObserver 協處理器列表
* `hbase.coprocessor.abortonerror`:如果協處理器應該出錯而不是`IOError`,是否中止已加載協處理器的守護進程。如果將此設置為 false 并且訪問控制器協處理器應該有致命錯誤,則將繞過協處理器,因此在安全安裝中,這被建議為`true`;但是,可以在每個表的基礎上為用戶協處理器重寫此操作,以確保它們不會中止其運行區域服務器,而是在出錯時卸載。
* `hbase.coprocessor.region.whitelist.paths`:可用于加載`org.apache.hadoop.hbase.security.access.CoprocessorWhitelistMasterObserver`的逗號分隔列表,從而可以使用以下選項列出可以加載協處理器的路徑的白名單。
* 類路徑上的協處理器隱式列入白名單
* `*`到通配符所有協處理器路徑
* 整個文件系統(例如`hdfs://my-cluster/`)
* 由 [FilenameUtils.wildcardMatch](https://commons.apache.org/proper/commons-io/javadocs/api-release/org/apache/commons/io/FilenameUtils.html) 評估的通配符路徑
* 注意:路徑可以指定方案與否(例如`[file:///usr/hbase/lib/coprocessors](file:///usr/hbase/lib/coprocessors)`或所有文件系統`/usr/hbase/lib/coprocessors`)
- HBase? 中文參考指南 3.0
- Preface
- Getting Started
- Apache HBase Configuration
- Upgrading
- The Apache HBase Shell
- Data Model
- HBase and Schema Design
- RegionServer Sizing Rules of Thumb
- HBase and MapReduce
- Securing Apache HBase
- Architecture
- In-memory Compaction
- Backup and Restore
- Synchronous Replication
- Apache HBase APIs
- Apache HBase External APIs
- Thrift API and Filter Language
- HBase and Spark
- Apache HBase Coprocessors
- Apache HBase Performance Tuning
- Troubleshooting and Debugging Apache HBase
- Apache HBase Case Studies
- Apache HBase Operational Management
- Building and Developing Apache HBase
- Unit Testing HBase Applications
- Protobuf in HBase
- Procedure Framework (Pv2): HBASE-12439
- AMv2 Description for Devs
- ZooKeeper
- Community
- Appendix