Bootiful Azure:使用 CosmosDB 实现全局数据访问 (3/6)

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

这是包含 6 个部分的系列文章的第 3 部分,每周一和周四发布新文章,介绍面向 Spring 开发人员的 Microsoft Azure。如果没有微软的 Asir Vedamuthu Selvasingh、Yitao Dong、Bruno Borges、Brian Benz 和 Theresa Nguyen 的投入,我无法完成这篇文章。您可以在 Github 上找到此系列文章的代码。在阅读各部分内容时,欢迎您在 Twitter (@starbuxman) 上与我联系,提供任何反馈或问题。您还可以在我的 Spring Tips (@SpringTipsLive) 部分中了解有关 Microsoft Azure 的更多信息,Bootiful Azure

以下是所有部分

我能听到你在想什么——是的,你最细微的想法也逃不过我的耳朵!——虽然你像其他开发者一样喜欢 Microsoft SQL Server,但它是你可以在任何平台(云或其他)上运行的东西。你并不 *需要* Microsoft 来为你运行它。对此我只能说,“是的!”(但微软确实为我们运行它,不是吗?)

我承认这一点。Azure 能为你做什么?你不需要再看其他地方,Microsoft Azure CosmosDB 就是答案。CosmosDB 指的是一套技术。它描述了一个可以以多种方式使用的单一产品。它是一个单一的、多模型、多模式数据库,支持文档数据、SQL 查询、图形数据访问等等。

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

项和容器

在内部,CosmosDB 将“项”存储在“容器”中。但你并不一定需要处理项或容器,因为这些概念将在你用来使用数据的DataModel的语言中体现出来。例如,如果你将其用作文档存储(如 MongoDB),则项将映射到集合中的文档。

容器被分组到数据库中,数据库是容器之上的某种命名空间。容器强制执行唯一键约束以确保数据的完整性。但容器的功能远不止于此。你可以向每个容器请求已更改内容的馈送;你可以使用此馈送为更改数据捕获 (CDC) 方案提供支持。你可以将馈送用于事件溯源。馈送本身是持久化的,因此你可以根据需要 *重放* 更改。

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

多模型、多范式数据存储

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

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

你可以使用 MongoDB API 与 CosmosDB 交谈,支持将集合作为容器,将文档作为项。

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

你可以使用 Cassandra API 与 CosmosDB 交谈,支持将表作为容器,将行作为项。Cassandra API 甚至支持 Cassandra 查询语言 (CQL)。

你还可以使用 Azure 表存储 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

你需要记下先前命令中生成的 primaryMasterKey 属性的值,以便稍后连接到 CosmosDB。

在 Spring 应用程序中引入 CosmosDB

让我们看看 CosmosDB 在 Spring 应用程序中的使用。理论上,你可以通过上述技术的适当抽象(如 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,因为我们使用资源组名称作为数据库名称)。密钥指的是 az cosmosdb list-keys 命令中的 primaryMasterKey 值。请将属性值替换为相关且适当的字符串值。

我在基于 macOS 的系统上顺利运行了此项目,但在基于 Ubuntu 18.10 的系统上遇到了一个奇怪的问题。CosmosDB 的 Spring 客户端库收集遥测的方式存在奇怪之处,导致出现 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 社区中所有即将举行的活动。

查看全部