# 六、.NET 和 Stargate 来连接

Stargate 是 HBase REST API 的名称，它使数据可以通过 HTTP 进行读写。Stargate 公开来自与表结构匹配的 URL 的数据(例如，/access-logs/rk1 将从 access-logs 表中获取带有关键字 rk1 的行)。

HTTP 动词 GET、POST 和 DELETE 用于处理作为资源的数据，这给了 Stargate 一个很好的 RESTful 接口。您可以在 JSON 中处理行和单元格，但该应用编程接口的缺点是所有数据都表示为 Base64 字符串，这些字符串是从 HBase 中的原始字节数组编码而来的。如果您只想使用像 Postman 或 cURL 这样的 REST 客户端进行浏览，这将使应用编程接口变得很尴尬。

像节俭应用编程接口一样，星际之门是一个独立的服务，你可以从 hbase-daemon.sh 开始休息。默认情况下，它监听端口 8080(并且已经在 hbase-简洁地说是 Docker 映像上运行)。

在生产环境中，您可以在区域服务器上运行 Stargate，但是如果您想将它用作 HBase 的主要接口，您应该考虑使用单独的服务器进行负载平衡。

![](img/00012.jpeg)提示:在[这篇博文](https://blog.sixeyed.com/using-nginx-as-a-load-balancing-proxy-for-stargate)中，我用 Nginx 构建了一个负载平衡反向代理。

您可以使用任何带有 HTTP 客户端的框架来与 Stargate 对话。在本章中，我们将使用 cURL 来查看原始的 HTTP 数据，并使用. NET 客户端库在更高的抽象级别上工作。

代码清单 48 显示了对根 Stargate URL 的 GET 请求(cURL 中的默认动词)。响应与 HBase Shell 中的列表命令相同:

 48:用 cURL 列出表格

```java
          $ curl http://127.0.0.1:8080
          access-logs
          social-usage

```

HTTP GET 请求相当于 HBase 外壳中的 GET 命令。您可以添加一个接受头来指定您想要的响应格式，但是 Stargate 提供的数据量有一些限制。

如果您试图请求整个表，例如 http://127 . 0 . 0 . 1:8080/access-logs，您将得到一个错误响应，状态代码为 405，这意味着该方法不被允许。你不能在 HBase 中获取整个表，405 是 Stargate 的实现。

您可以通过在表名后添加行键来获取一整行，但是如果您的行格式中有任何对 HTTP 不友好的字符，您将需要在 URL 中对它们进行转义。您也不能获得一行的纯文本表示；您需要用像 JSON 这样的以数据为中心的格式发出请求。

代码清单 49 显示了从星际之门读取的一整行。请注意，行键中的管道字符已被转义为%7C，响应中的数据值(行键、列限定符和单元格值)都是 Base64 编码的字符串(我已经对响应应用了格式；星际之门没有返回空白)。

 49:用 cURL 读取一行

```java
          $ curl -H accept:application/json http://127.0.0.1:8080/access-logs/elton%7Cjericho%7C201511

          {
              "Row": [{
                  "key": "ZWx0b258amVyaWNob3wyMDE1MTE=",
                  "Cell": [{
                      "column": "dDoxMTA2",
                      "timestamp": 1447228701460,
                      "$": "MTIw"
                  }, {
                      "column": "dDoxMTA3",
                      "timestamp": 1447228695240,
                      "$": "NjUw"
                  }]
              }]
          }

```

响应是一个包含行对象数组的 JSON 对象。每行都有一个关键字字段和一个单元格对象数组。每个单元格都有一个列限定符、一个单元格值(存储在$字段中)和一个时间戳。时间戳是 HBase 存储的唯一具有解释的值；它们是长整数，存储行更新时的 UNIX 时间戳。

其他值是 Base64 字符串，这意味着您需要解码 GET 响应中的字段。表 6 显示了响应中一列的解码值:

| 田 | 价值 | 解码值 |
| --- | --- | --- |
| Row.key | zwx 0x 258 amvyawnob 3 wymde 1 mte = | 埃尔顿&#124;杰里科&#124;201511 |
| 单元格.列 | dDoxMTA2 | t:1106 |
| 手机。$ | MTIw | One hundred and twenty |

 6:编码和解码的 Stargate 值

请注意，单元格中的列值是全名(列族加上限定符，用冒号分隔)，数值单元格值实际上是字符串。

您还可以从 Stargate 获取一行中的单列族(使用 URL 格式/{ table }/{ row-key }/{ column-family })，或者单个单元格值。对于单个单元格，您可以以纯文本方式获取它们，Stargate 将在响应中解码该值，如**错误！未找到参考源。**:

代码清单 50:获取单个单元格值

```java
          $ curl http://127.0.0.1:8080/access-logs/elton%7Cjericho%7C201511/t:1106
          120

```

星际之门的 put 请求的语义与 HBase Shell 中的 PUT 命令非常相似。您可以在请求中指定所需的结束状态，剩下的工作由 HBase 来完成—如果行或列不存在，则创建行或列，并设置值。

你必须使用数据格式通过星际之门进行更新；否则你会得到一个 415 错误，“不支持的媒体类型。”使用 JSON，您可以镜像 GET 响应的格式，因此您可以在一个请求中为多行发送多个单元格值。

网址格式仍然需要一个行键和一个表，但是使用 PUT，网址中的键被忽略，取而代之的是请求数据中的键。代码清单 51 显示了更新我的行中两个单元格的 PUT 请求:

 51:用 Stargate 更新细胞

```java
          $ curl -X PUT -H "Content-Type: application/json" -d '{
              "Row": [{
                  "key": "ZWx0b258amVyaWNob3wyMDE1MTE=",
                  "Cell": [{
                      "column": "dDoxMTA2",
                      "timestamp": 1447228701460,
                      "$": "MTMw"
                  }, {
                      "column": "dDoxMTA3",
                      "timestamp": 1447228695240,
                      "$": "NjYw"
                  }]
              }]
          }' 'http://127.0.0.1:8080/access-logs/FAKE-KEY'

```

![](img/00012.jpeg)提示:Stargate 对于临时请求非常方便，但是使用 Base64 可能会很困难。我在博客上写了一些简单的工具让它变得更简单。

该 PUT 请求增加了实际上是我的行中的字符串的数值。通过 Stargate 不能使用 incr 命令自动增加计数器列，所以如果你需要增加值，那么你必须先读取它们，然后进行更新。

您可以使用 cURL 做更多的事情，比如发送删除请求来删除数据，以及创建扫描仪来获取多行，但是语法变得很麻烦。在星际之门中使用 RESTful API 的包装器是一个更好的选择。

NuGet 是的包管理器。NET 应用，还有几个开源包是访问星际之门的包装器。微软有一个专门针对运行在 Azure 云上的 HBase 集群的包，还有一个来自作者“部落”的第三方包，用于一般地使用 Stargate。

这个包很好地抽象了星际之门的内部，让你可以直观地处理 HBase 数据。它是 IoC 感知的(默认情况下使用自动调整)，因此您可以轻松地调整 HTTP 设置并构建一个数据访问层，您可以模拟出来进行测试。

将该包及其依赖项添加到。NET 应用中，您可以使用代码清单 52 中的 NuGet 包管理器控制台命令:

 52:为 Stargate 添加一个 NuGet 引用

```java
          Install-Package "HBase.Stargate.Client.Autofac"

```

在本书的 GitHub 存储库中，有一个. NET 控制台应用，它使用部落的客户端连接到 Stargate，运行在简洁的 Docker 容器中。

要设置 Stargate 客户端，您需要配置服务器网址并构建容器。代码清单 53 展示了如何使用 Autofac 实现这一点:

 53:配置 Stargate 客户端

```java
          var builder = new ContainerBuilder();
          builder.RegisterModule(new StargateModule(new StargateOptions
          {
                ServerUrl = "http://127.0.0.1:8080"
          }));

          var container = builder.Build();
          var stargate = container.Resolve<IStargate>();

```

星之门选项对象包含星之门(或代理)网址，星之门模块包含所有其他容器注册。从容器中获得的 IStargate 接口提供了对所有 Stargate 客户端操作的访问，具有简洁的抽象，并且所有数据项都编码为字符串。

星际之门客户端有两种读取数据的方法。最简单的是 ReadValue()，它获取一个特定的单元格值，将表名、行键、列族和限定符传递给它。这在功能上等同于带有包含表名、行键和列名的 URL 的 GET 请求，它返回编码为字符串的单个单元格值，如代码清单 54 所示:

 54:用 IStargate 读取单元格

```java
          var value = stargate.ReadValue("access-logs", "elton|jericho|201511", "t", "1106");
          //value is "120"

```

或者，可以使用 FindCells()方法获取 CellSet 对象，该方法返回行和单元格的集合。这就像对一整行或一行中的列族发出 GET 请求。CellSet 是一个可枚举的集合，您可以用 LINQ 查询它，如代码清单 55 所示:

 55:查找带有 IStargate 的单元格

```java
          var cellSet = stargate.FindCells("access-logs", "elton|jericho|201511");
          var value = cellSet
                       .First(x => x.Identifier.CellDescriptor.Qualifier == "1106").Value;
          //value is "120"

```

注意，FindCells()调用向 Stargate 发出 GET 请求，Stargate 返回 CellSet 中的所有数据，LINQ 查询在内存中的 CellSet 上运行。NET 客户端。

ReadValue()调用将很快返回，因为它获取的是单个数据，FindCells()调用将很快为 Stargate 服务，但是如果有许多包含大量数据的单元格，客户端可能需要更长时间才能收到。

从 Stargate 获取数据的另一种方法是创建一个行扫描器，它类似于服务器端的游标，您可以使用它来读取多行，并且扫描器可以选择有一个过滤器来限制返回的单元格数量。

用 Stargate 扫描行有两个部分。首先，创建运行在服务器上的扫描仪。星际之门给你一个扫描仪的参考，你可以用它来获取行。在。NET 客户端，您可以使用 ScannerOptions 对象来指定扫描的开始和结束行。

代码清单 56 显示了如何创建一个扫描器，从 2015 年 10 月开始为一个系统检索一个用户的所有访问日志:

 56:使用 IStargate 创建扫描仪

```java
          var options = new ScannerOptions
          {
                TableName = "access-logs",
                StartRow = "elton|jericho|201510",
                StopRow = "elton|jericho|x",
          };
          var scanner = stargate.CreateScanner(options);

```

当 Stargate 返回时，扫描器正在服务器上运行，您可以从开始到结束行键循环遍历行，使用 MoveNext()方法从 Stargate 获取下一组单元格，如代码清单 57 所示:

 57:使用 IStargate 遍历扫描仪

```java
          var totalUsage = 0;
          while (scanner.MoveNext())
          {
                var cells = scanner.Current;
                foreach (var cell in cells)
                {
                      totalUsage += int.Parse(cell.Value);
                }
          }
          //totalUsage is 850

```

![](img/00015.jpeg)注意:当您通过扫描仪迭代时返回的单元格可能来自多行，因此不要假设 MoveNext(或其他客户端中的等效方法)移动到下一行—它会移动到下一组单元格，这些单元格可能来自多行。

按行键扫描是从 Stargate 读取数据的最快方法，但是如果需要通过列中的数据(限定符或单元格值)来额外限制结果，则可以在创建扫描仪时指定过滤器。

过滤器也在 Stargate 中运行服务器端，所以它比在客户端获取整行和提取某些列更快，但是它确实使用了额外的服务器资源(如果您在区域服务器上运行 Stargate，这很重要)。

星际之门应用编程接口有各种各样的过滤器类型，但最有用的有:

key only filter–仅返回扫描仪中的行键

qualifier filter–返回与指定列限定符匹配的单元格

column prefixfilter–返回列名与指定前缀匹配的单元格

在 access-logs 表中，行键中的句点指定使用记录的年和月，列限定符包含日和小时。在代码清单 58 中，我向扫描器添加了一个列前缀过滤器，它过滤结果，以便只返回列名以提供的前缀开头的单元格。在这种情况下，只有每月第 11 天的单元格才会包含在结果中:

 58:使用 IStargate 创建过滤扫描仪

```java
          var options = new ScannerOptions
          {
                TableName = "access-logs",
                StartRow = "elton|jericho|201510",
                StopRow = "elton|jericho|x",
                Filter = new ColumnPrefixFilter("11")
          };
          var scanner = stargate.CreateScanner(options);

```

IStargate 接口有两种写入数据的方法。最简单的是 WriteValue()，它相当于对一行中特定单元格的 PUT 请求。如果需要，Stargate 会创建行和/或列，并设置值，如代码清单 59 所示:

 59:用 IStargate 更新单元格值

```java
          stargate.WriteValue("100", "access-logs", "elton|jericho|201510", "t", "2908");
          //cell value is now "100"

```

一个更复杂更灵活的方法是 WriteCells()，它采用一个 CellSet 对象，可以通过一次 API 调用更新多个值。这种混合值可以包括不同行的更新和插入，但所有行必须在同一个表中。

代码清单 60 显示了对现有单元格值的更新，以及在对 WriteCells()的一次调用中插入一个新行:

 60:用 IStargate 更新单元格值

```java
          var update = new Cell(new Identifier
          {
                Row = "elton|jericho|201510",
                CellDescriptor = new HBaseCellDescriptor
                {
                      Column = "t",
                      Qualifier = "2908"
                }
          }, "120");

          var insert = new Cell(new Identifier
          {
                Row = "elijah|jericho|201511",
                CellDescriptor = new HBaseCellDescriptor
                {
                      Column = "t",
                      Qualifier = "1117"
                }
          }, "360");

          var cells = new CellSet(new Cell[] { update, insert});
          cells.Table = "access-logs";

          stargate.WriteCells(cells);

```

星际之门 API 是无状态的(即使是扫描器也运行在 Region Server 上，Region Server 不一定是星际之门服务器)，客户端也是无状态的，所以本地没有数据缓存，除非你自己在内存中保留单元格。每次调用 Stargate 客户端读取或写入数据都会导致对 Stargate 的 REST 调用。

在这一章中，我们看了星际之门，HBase 提供的 REST API。它将行中的数据作为资源公开，使用描述数据路径的 URL 格式，始终包括表名和行键，以及可选的列族和限定符。

该 API 支持不同的数据格式，包括 JSON 和 Google 的 Protocol Buffers，对于简单的读写，可以使用纯文本(虽然功能集比较有限)。星际之门通过了 cURL 测试——如果你能用一个带有 cURL 的 API，那么它就有一个可用的 RESTful 设计。

由于星际之门提供了一种访问数据的标准化方法，它可以很容易地打包到一个客户端库中，我们用. NET NuGet 包介绍了一个选项。Stargate 提供了其他客户端的大部分功能(包括我们没有空间的 DDL 函数，比如创建表)，但是在编写本文时不支持的一个特性是递增计数器列。

现在，我们已经很好地了解了您可以存储在 HBase 中的内容以及访问它的客户端选项。在下一章中，我们将回顾一下 HBase 的体系结构。