Bootiful Azure: 使用 CosmosDB 进行全球规模数据访问 (3/6)

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

这是分为六部分系列文章的第三部分,新的文章会在每周一和周四发布,旨在向 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 CosmosDB 就够了。CosmosDB 指的是一系列技术。它是一个单一的产品,可以以多种方式使用。它是一个单一的、多模型的、多模态的数据库,支持文档数据、SQL 查询、图数据访问等等。

根据产品网页:CosmosDB 从一开始就将全球分发和横向扩展作为其核心。它保证了读写操作在第99个百分位上的单位数毫秒延迟,并通过全球任意位置的多宿主(multi-homing)保证了 99.999% 的高可用性——所有这些都由行业领先的、全面的服务级别协议(SLAs)提供支持。

条目 (Items) 和容器 (Containers)

在内部,CosmosDB 将“条目”(items) 存储在“容器”(containers) 中。但您不一定需要直接处理条目或容器,因为这些概念会呈现在您用来使用数据的特定数据模型的语言中。例如,如果您将其用作文档存储(如 MongoDB),则条目将被映射到集合中的文档。

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

您还可以为容器指定生存时间 (TTL) 值,让 CosmosDB 在一定时间后自动清除现有记录。您也可以为特定条目覆盖 TTL 设置。

多模型、多范式数据存储

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

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

您可以使用 MongoDB API 与 CosmosDB 通信,将集合作为容器,文档作为条目。

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

您可以使用 Cassandra API 与其通信,将表作为容器,行作为条目。Cassandra API 甚至支持 Cassandra 查询语言 (CQL)。

还可以使用 Azure Table Storage API 与其通信,将表作为容器,将条目作为……嗯……条目

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

在 Microsoft Azure 上配置 CosmosDB

您首先需要创建一个(可能是地理上分布式的)CosmosDB 实例,然后在其中创建一个数据库实例。接着,您需要创建一个集合来存储记录。这是一个脚本。唯一需要注意的是,我们不必像在 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 门户中方便地完成此操作。只需点击一个区域,剩下的就都交给它了!

另外,请记下随后产生的 $adminlogin 值,以便后续使用。

现在,您需要获取必要的配置字符串,以便将您的应用程序连接到新的数据库及其数据。您可以仔细查看之前命令的输出,但下面的命令要简单得多。

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

为了稍后连接到 CosmosDB,您需要记下上一个命令产生的 primaryMasterKey 属性值。

将 CosmosDB 引入您的 Spring 应用程序

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

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

然后您必须配置相关的连接信息。您可以将以下内容添加到您的应用程序的 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 属性指的是逻辑 CosmosDB 实例(bootiful-cosmosdb,这是我们在脚本中用 $adminlogin 指定的)内的数据库(bootiful,因为我们将资源组名称用作数据库名称)。key 指的是 az cosmosdb list-keys 命令中的 primaryMasterKey 值。请将属性值替换为相关且适当的字符串值。

我在基于 macOS 的系统上运行这个项目没有任何问题,但在我基于 Ubuntu 18.10 的系统上运行它时遇到了一个奇怪的问题。Spring CosmosDB 客户端库收集遥测数据的方式存在一个异常,导致了 NPE。如果您遇到此问题,请将以下内容添加到 src/main/resources/application.properties 中以禁用遥测功能。

cosmosdb.telemetryAllowed=false 

接下来,您应该定义一个 Spring Data 实体,将其映射到 CosmosDB 集合 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 CosmosDB 模块中的 @Document 注解来指定此实体映射到的 reservations 集合。该实体使用一个 CosmosDB 特定的注解 @PartitionKey 来向数据库指示在决定如何分区(逻辑上或物理上)容器中可能相关的数据时应使用哪个字段。对于分区键,使用 String 类型是一个很好的实践。在行星规模的分布式系统中,单调递增的主键不是一个好主意!

现在,定义基于 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 社区所有即将举办的活动。

查看全部