Bootiful Azure: CosmosDB 全球规模的数据访问 (3/6)

工程 | Josh Long | 2019年1月10日 | ...

这是 6 部分系列文章的第 3 部分,新文章将在每周一和周四发布,介绍面向 Spring 开发者的 Microsoft Azure。如果没有 Microsoft 的 Asir Vedamuthu Selvasingh、Yitao Dong、Bruno Borges、Brian Benz 和 Theresa Nguyen 的建议,我无法完成这篇文章。您可以在 Github 上找到该系列的源代码。阅读这些文章时,欢迎随时通过 Twitter (@starbuxman) 与我联系,提出您的反馈意见或问题。您也可以在我主持的 Spring Tips (@SpringTipsLive) 系列中的文章 Bootiful Azure 中了解更多关于 Microsoft Azure 的信息。

以下是所有安装内容

我能听到您在想——是的,即使是您最微弱的想法也逃不过我的眼睛!——您可能认为,虽然您和其他开发者一样喜欢 Microsoft SQL Server,但您可以在任何平台(云端或非云端)上自行运行它!您并不需要 Microsoft 为您运行它。对此,我只想说,“没错!”(但 Microsoft 为我们运行它确实很好,不是吗?)

我承认这一点。Azure 能为您做什么?您无需再进一步,看看 Microsoft Azure Cosmos DB 就知道了。Cosmos DB 指的是一套技术。它描述了一个可以以多种方式使用的单一产品。它是一个单一的、多模型、多模态的数据库,支持文档数据、SQL 查询、图数据访问等。

根据 产品网页:Cosmos DB 从一开始就以全局分发和水平扩展为核心构建。它保证在 99% 的百分位上具有个位数毫秒的读写延迟,并保证在全球任何地方都具有 99.999% 的高可用性,并且支持多宿主——所有这些都由行业领先的、全面的服务级别协议 (SLA) 提供支持。

项和容器

在内部,Cosmos DB 将“项”存储在“容器”中。但您不一定需要将项或容器作为您正在使用的、用于消费数据的模型语言所暴露的概念来处理。如果您将其用作文档存储,例如 MongoDB,那么项将被映射到集合中的文档。

容器被分组到数据库中,数据库是容器之上的一种命名空间。容器强制执行唯一键约束以确保数据的完整性。但容器的功能远不止于此。您可以让每个容器提供更改的流;您可以使用此流来实现变更数据捕获 (CDC) 方案。您也可以使用此流来实现事件溯源。此流本身是持久化的,因此您可以重放更改,如果您愿意的话。

您还可以为容器指定生存时间 (TTL) 值,让 Cosmos DB 在特定时间段后自动删除现有记录。您也可以为特定项覆盖 TTL。

多模型、多范式的数据存储

首先,Cosmos DB 是无模式的。在使用它时请牢记这一点——如果您没有准备好,它可能会产生一些重要的影响。

Cosmos DB 支持多模型、多范式的方法来构建应用程序。客户端可以通过 HTTP REST API 与之交互,并使用类 SQL 的语言进行查询。您也可以使用 SQL API 创建、更新和删除容器。

您可以通过 MongoDB API 与 Cosmos DB 进行交互,将集合作为容器,将文档作为项。

您可以通过 Gremlin API 与之交互,支持将图、容器、节点和边作为项。根据 Gremlin 网站的说法,“Gremlin 是 Apache TinkerPop 的图遍历语言。Gremlin 是一种函数式、数据流语言,使用户能够简洁地表达对其应用程序属性图的复杂遍历(或查询)。” 因此,它基本上是一种遍历图数据的方式。

您可以通过 Cassandra API 与 Cosmos DB 进行交互,支持将表作为容器,将行作为项。Cassandra API 甚至支持 Cassandra 查询语言 (CQL)。

还可以通过 Azure Table Storage API 与之交互,支持将表作为容器,将项作为……好吧……

Cosmos DB 还嵌入了一个 JavaScript 引擎,因此您可以使用 JavaScript 定义触发器、可以从 SQL 查询语言调用并增强 SQL 查询语言的用户定义函数以及存储过程。存储过程可以在单个 ACID 兼容事务中管理多个操作。

在 Microsoft Azure 上配置 Cosmos DB

您需要首先创建一个(可能是地理分布的)Cosmos DB 实例,然后在其中创建一个数据库实例。然后,您需要创建一个集合来存储记录。这是一个脚本。唯一值得注意的是,我们不必像在 SQL Server 示例中那样指定防火墙例外。它(TM)就是这样工作的。

#!/bin/bash

# the name of the resource group
export rg=$1
export adminlogin=${rg}-cosmosdb

location='southcentralus'
accountname=${adminlogin}
databasename=bootiful
containername=reservations

# Create a SQL API Cosmos DB account with session consistency and multi-master enabled
az cosmosdb create \
    --resource-group $rg \
    --name $adminlogin \
    --kind GlobalDocumentDB \
    --default-consistency-level "Session" 

# Create a database
az cosmosdb database create \
    --resource-group $rg \
    --name $adminlogin \
    --db-name $databasename

# Create a SQL API container with a partition key and 1000 RU/s
az cosmosdb collection create \
    --resource-group $rg \
    --collection-name $containername \
    --name $adminlogin \
    --db-name $databasename \
    --partition-key-path /id \
    --throughput 1000

在此脚本中,我们可以指定希望新数据库在哪些区域可用。您也可以通过 Azure Portal 中的一个便捷地图来完成此操作。只需单击一个区域,它就会处理其余的事情!

另外,请记下稍后需要使用的 `$adminlogin` 值。

现在,您需要获取必要的连接字符串,以便将您的应用程序连接到新的数据库及其数据。您可以查看前面命令的输出,但以下命令会更容易得多。

az cosmosdb list-keys --resource-group bootiful --name bootiful-cosmosdb

您需要记下前面命令生成的 `primaryMasterKey` 属性值,以便稍后连接到 Cosmos DB。

将 Cosmos DB 引入您的 Spring 应用程序

让我们看看 Cosmos DB 在 Spring 应用程序中的用法。理论上,您可以通过上述技术的相应抽象(如 MongoDB 和 Cassandra)来与 Cosmos DB 进行交互。我更喜欢使用 Spring Data Cosmos DB 抽象,您需要在构建文件中添加其启动器依赖项。

Cosmos DB 以前被称为 DocumentDB。如果您看到这些名称,它们几乎可以互换使用。出于历史原因,您需要将引用旧项目名称 `com.microsoft.azure` 的 Maven 启动器依赖项 `azure-documentdb-spring-boot-starter` 添加到您的构建文件中。

然后,您必须配置相关的连接信息。您可以将以下内容添加到应用程序的 `application.properties` 文件中。

azure.documentdb.database=bootiful
azure.documentdb.key=THIS_IS_THE_KEY_FROM_BEFORE
azure.documentdb.uri=https://ADMINLOGIN.documents.azure.com:443/

`database` 属性指的是逻辑 Cosmos DB 实例(`bootiful-cosmosdb`,这是我们在脚本中通过 `$adminlogin` 指定的)内的数据库(`bootiful`,因为我们使用了资源组名称作为数据库名称)。`key` 指的是 `az cosmosdb list-keys` 命令中的 `primaryMasterKey` 值。请将属性值替换为相关且适当的字符串值。

我在基于 macOS 的系统上成功运行了此项目,但在基于 Ubuntu 18.10 的系统上遇到了一些奇怪的问题。Spring 客户端库用于 Cosmos DB 的遥测数据收集方式存在一些怪异之处,会导致 NPE。如果您遇到这种情况,请在 `src/main/resources/application.properties` 中添加以下内容来禁用遥测数据。

cosmosdb.telemetryAllowed=false 

接下来,您应该定义一个 Spring Data 实体来映射到 Cosmos DB 集合 `reservations` 中的记录。

package com.example.bootifulazure;

import com.microsoft.azure.spring.data.cosmosdb.core.mapping.Document;
import com.microsoft.azure.spring.data.cosmosdb.core.mapping.PartitionKey;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Document(collection = "reservations")
class Reservation {

        @PartitionKey
        private String id;
        private String name;
}

这在很大程度上看起来就像您见过的任何其他 Lombok 注释的 POJO。特别值得注意的是,该实体使用 Spring Data Cosmos DB 模块中的 `@Document` 来指定映射到此实体的 `reservations` 集合。该实体使用 Cosmos DB 特定的注释 `@PartitionKey`,来告知数据库在分区(逻辑上或物理上)容器中可能相关的数据时使用哪个字段。使用字符串作为分区键是一个好习惯。在行星规模的分布式系统中,单调递增的主键不是一个好主意!

现在,定义一个基于 `DocumentDbRepository` 的 Spring Data 存储库。

package com.example.bootifulazure;

import com.microsoft.azure.spring.data.cosmosdb.repository.DocumentDbRepository;

interface ReservationRepository extends DocumentDbRepository<Reservation, String> {
}

`DocumentDbRepository` 可能对您来说是新的,但其他部分应该都很直接。

package com.example.bootifulazure;

import lombok.extern.log4j.Log4j2;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

import java.util.UUID;
import java.util.stream.Stream;

@Log4j2
@Component
class CosmosDbDemo {

    private final ReservationRepository rr;

    CosmosDbDemo(ReservationRepository rr) {
        this.rr = rr;
    }

    @EventListener(ApplicationReadyEvent.class)
    public void demo() throws Exception {

        this.rr.deleteAll();

        Stream.of("A", "B", "C")
            .map(name -> new Reservation(UUID.randomUUID().toString(), name))
            .map(this.rr::save)
            .forEach(log::info);

    }
}

获取 Spring 新闻通讯

通过 Spring 新闻通讯保持联系

订阅

领先一步

VMware 提供培训和认证,助您加速进步。

了解更多

获得支持

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件,只需一份简单的订阅。

了解更多

即将举行的活动

查看 Spring 社区所有即将举行的活动。

查看所有