领先一步
VMware 提供培训和认证,以加快您的进步。
了解更多嗨,Spring 粉丝们!在这个简短的八部分系列中,我们将了解 Google Cloud Platform 的 Spring Cloud 集成,称为 Spring Cloud GCP。Spring Cloud GCP 代表了 Google 和 Pivotal 之间的共同努力,旨在为使用 Google Cloud Platform 的 Spring Cloud 开发人员提供一流的体验。Pivotal Cloud Foundry 用户将享受更轻松的与 GCP 服务代理集成。我与 Google Cloud 开发者布道者,也是我的朋友,Ray Tsang 一起撰写了这些部分。您还可以在我们的 Google Next 2018 会议上观看 Spring Cloud GCP 的演练,Bootiful Google Cloud Platform。感谢我的朋友!像往常一样,如果您有任何反馈,我很乐意听到您的声音。
本系列共有八篇文章。它们都是:
MySQL 和 PostgreSQL 在陌生的环境中是熟悉的朋友,但它们并不是我们来这里的目的。不不。如果我是你,我会关注像 GCP 这样的平台,并从中汲取精华;那些在其他地方没有类似功能的部分。那些将它与其他平台区分开来的东西。其中一个与众不同的功能是Google Spanner。Spanner……完全是另一回事。在本节中,我们将了解 Google Cloud Spanner。
Google 在推出 F1 时首次展示了 Spanner,F1 是一款 SQL 数据库引擎,Adwords 团队在 2012 年将其从 MySQL 中迁移出来(“但是 Josh!”我听到你大声喊道,“我们不是刚刚部署到 MySQL 吗?”)。Spanner 提供低延迟的全局读取,以及在较小程度上进行全局写入。Google 在2012 年的一篇研究论文中宣布了它,文中称 Spanner 是“第一个在全球范围内分发数据并支持外部一致的分布式事务的系统”。
“Spanner 是对最难的分布式系统问题之一的令人印象深刻的工作——一个全局复制的数据库,可在合理的延迟范围内支持外部一致的事务,”Basho 的首席架构师Andy Gross 说道。
Spanner 能够提供如此广泛的地理冗余,这要归功于 Google 开发的一种方法,该方法能够为应用程序提供精确的时间,以便它们能够在不犯错误的情况下写入、读取和复制数据。Spanner 的“TrueTime”API 依赖于安装在 Google 数据中心中的 GPS 接收器和原子钟,以便应用程序能够在本地获得准确的时间读数,而无需全局同步。
Google 有许多数据库技术,例如Bigtable(列式数据库,非常适合高吞吐量写入)和Megastore(NoSQL 数据库)。Bigtable 只支持跨数据中心的最终一致性复制。根据论文:“Google 内部至少有 300 个应用程序使用 Megastore(尽管其性能相对较低),因为它的数据模型比 Bigtable 的更容易管理,并且因为它支持跨数据中心的同步复制。”当时,Gmail、Picasa、日历、Android Market 和 AppEngine 等应用程序依赖于 Megastore。
Spanner 被设计为“可扩展的、多版本的、全球分布式的和同步复制的数据库”。事务是 Spanner 中的一级概念,部分原因是 Bigtable 中缺少事务。
“Bigtable 中缺乏跨行事务导致了频繁的抱怨;Percolator 部分是为了解决这个缺陷而构建的。一些作者声称,由于它带来的性能或可用性问题,通用两阶段提交过于昂贵而无法支持。我们认为,最好让应用程序程序员处理由于过度使用事务而导致的性能问题,而不是始终绕过缺乏事务的情况。在 Paxos 上运行两阶段提交可以减轻可用性问题。”
每个数据库都有其用例。Bigtable(在 GCP 上称为Cloud Bigtable)非常适合一致的低延迟和高吞吐量工作负载。而 Megastore(在 GCP 上称为Cloud Datastore)可以用作具有 ACID 事务的托管 NoSQL 数据存储。Spanner(在 GCP 上称为Cloud Spanner)旨在用于水平可扩展、高可用且强一致性的 RDBMS 工作负载。
好吧!我同时感到兴趣和害怕!我想要 Spanner,但我不想不得不架设和堆叠服务器以及同步 GPS 接收器和原子钟。但是,某种东西告诉我 Google 会很乐意为我做到这一点,所以让我们试试看。
和以前一样,您需要在使用 Google Cloud GCP Spanner 之前启用其 API。
gcloud services enable spanner.googleapis.com
然后,创建一个新的 Google Cloud Spanner 实例。
gcloud spanner instances create reservations --config=regional-us-central1 \
--nodes=1 --description="Reservations for everybody"
然后,创建数据库实例。
gcloud spanner databases create reservations --instance=reservations
确认 Spanner 实例可用。
gcloud spanner databases list --instance=reservations
实例变为READY
后,就可以创建表了。这是 Spanner DDL。如果这看起来不可思议地像 SQL,那就对了!它应该是。将此 DDL 放入单独的文件中。我将其命名为schema.ddl
。
schema.ddl。
CREATE TABLE reservations (
id STRING (36) NOT NULL,
name STRING (255) NOT NULL
) PRIMARY KEY (id );
向数据库注册模式。
gcloud spanner databases ddl update reservations \
--instance=reservations --ddl="$(./gcp/src/main/resources/db/schema.ddl )"
现在,我们可以在 Spring 应用程序中从 Spanner 读取数据。自动配置需要一些配置才能与正确的数据库通信。
application.properties。
spring.cloud.gcp.spanner.instance-id=reservations-demo
spring.cloud.gcp.spanner.database=reservations
我们将使用全新的 Spring Data Spanner 模块,该模块在使用 Spanner 时支持常见的 Spring Data 习惯用法。将org.springframework.cloud
:spring-cloud-gcp-starter-data-spanner
添加到您的 Maven 构建中。让我们使用 Spring Data 存储库来简化与数据库的读取工作。
package com.example.gcp.spanner;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.cloud.gcp.data.spanner.core.mapping.PrimaryKey;
import org.springframework.cloud.gcp.data.spanner.core.mapping.Table;
import org.springframework.context.event.EventListener;
import org.springframework.data.annotation.Id;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import java.util.UUID;
import java.util.stream.Stream;
@Slf4j
@SpringBootApplication
public class SpannerApplication {
private final ReservationRepository reservationRepository;
SpannerApplication(ReservationRepository reservationRepository) {
this.reservationRepository = reservationRepository;
}
@EventListener(ApplicationReadyEvent.class)
public void setup() {
this.reservationRepository.deleteAll();
Stream
.of("josh", "ray")
.map(name -> new Reservation(UUID.randomUUID().toString(), name))
.forEach(this.reservationRepository::save);
this.reservationRepository.findAll().forEach(r -> log.info(r.toString()));
}
public static void main(String args[]) {
SpringApplication.run(SpannerApplication.class, args);
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "reservations")
class Reservation {
@Id
@PrimaryKey
private String id;
private String name;
}
@RepositoryRestResource
interface ReservationRepository extends PagingAndSortingRepository<Reservation, String> {
}
我们启动应用程序,删除现有数据,然后使用我们基于 Spring Data Spanner 的存储库将一些新数据写入数据库。
我们使用自定义映射注释@Table
和@PrimaryKey
定义 Spring Data Spanner 实体。
我们创建了一个 Spring Data 存储库,该存储库也作为 REST API 使用 Spring Data REST 公开。
如果您曾经使用过 Spring Data,这个示例应该看起来很熟悉。Spring Data Spanner 基于熟悉的概念和模式——模板、存储库和实体——来支持使用非常不同类型的数据库的熟悉数据访问模式。