Google Cloud - instance 间通讯(pubsub + memcache 实现实例间通讯和保证一致性)

GCP - appengine 经过 version 管理应用,你能够在 appengine 上部署多个 version(dev、qa等),而每一个 version 能够有多个 instance,一个 instance 可简单理解为一个基于 Spring Boot 实现的微服务,当有请求到达时 appengine 会根据必定策略选择由哪个 instance 处理该请求,若是现有的 instance 处理的流量已经不少,那么 appengine 会启动新的 instance 来处理这个请求,这个行为主要由 instance 的三种扩缩策略决定:手动、自动和基础。java

背景

应用数据保存在 GCP 的数据存储组件 datastore 中,datastore 是一个 NoSQL 数据库。假设咱们的应用对其中一部分数据 M 的操做的 QPS 要求很高,若是每次都从 datastore 查询则不能知足需求。python

// 更新 M 数据
POST /m

// 查询 M 数据
GET /m
复制代码

解决方案

为了加快请求的处理速度,咱们在应用启动时(即 instance 启动时)先将这部分数据所有加载到内存,以后直接从内存中读取,而不是每次都从 datastore 中查询。这种方式有几个问题须要解决:数据库

  1. 每次请求到达时不肯定 appengine 会将请求路由给哪个 instance,因此当这部分数据有更新(POST /m)时须要通知该 version 的全部 instance 进行数据同步
  2. POST /m 请求中要确保全部 instance 都成功同步了数据(全部 instance 中 M 数据保持一致),才能以请求处理成功的状态返回。

数据同步 - pubsub

instance 间通讯是一个棘手的问题,由于 appengine 没有直接提供 API 或外部组件来完成这件事情,甚至你想将请求发给指定的 instance 都须要牺牲不少灵活性才能实现。固然大多数状况下请求的路由应该由 appengine 控制,只有在实现某些特殊需求时才能够考虑一些特殊作法。缓存

数据同步的基本思路是在 POST /m 时先在 datastore transactions 中更新 M 数据,事务成功提交后再经过 pubsub 通知全部 instance 从 datastore 更新最新数据,全部 instance 都确认成功更新数据后,请求成功。bash

经过查阅 appengine 请求路由说明能够知道经过如下格式的地址能够将请求路由给指定的 instance。 https://[INSTANCE_ID]-dot-[VERSION_ID]-dot-[SERVICE_ID]-dot-[MY_PROJECT_ID].appspot.com 但这种方式须要将扩缩方式设置为手动扩缩,并且 INSTANCE_ID并非 instance 的惟一 id,而是一个下标索引,好比 version test 有 3 个 instance,分别为 A、B、C,那么能够保证的是经过 test[0]、test[1]、test[2] 能够成功访问(遍历)三个 instance,但你没法知道 test[0] 到底是指向 A、B 仍是 C。app

pubsub 是 GCP 的消息组件,消息发送后消费者有两种方式消费消息:pullpush分布式

  1. pull: 经过拉取的方式消费消息,咱们的目的是将“数据同步”这个消息马上通知到每个 instance,pull 的方式须要每个 instance 以轮询的方式检查并拉取消息,这样在资源占用和响应速度(POST /m)上都不能知足要求。
  2. push: 这种方式在消息发到 pubsub 的指定 topic 后,pubsub 会马上把这个消息 push 到订阅了这个 topic 的全部 subscriber (subscriber 指定的 endpoint 处,这里咱们的域须要设置为:https://[INSTANCE_ID]-dot-[VERSION_ID]-dot-[SERVICE_ID]-dot-[MY_PROJECT_ID].appspot.com)。

须要注意的是 pubsub 的 tpoic 和 subscriber 的建立只须要执行一次,能够在 version 启动后经过 appengine-taskqueue 建立 n 个 subscriber,n 为这个 version 的实例数。这意味着实例数量 n 是个"常数"(只有手动扩缩模式才能确保 n 为“常数”)。微服务

到这里,在 POST /m 里通知全部 instance 进行 “数据同步”这个消息能够正确发送并最终通知到全部 instance 了。使用 appengine 预留的 /_ah/push-handlers/.* 路径能够简化 endpoint 的认证和受权,最终的 endpoint 是下面的形式: https://[INSTANCE_ID]-dot-[VERSION_ID]-dot-[SERVICE_ID]-dot-[MY_PROJECT_ID].appspot.com/_ah/push-handlers/your-topic-name pubsub 进行 push 时每一个 instance 的 POST /_ah/push-handlers/your-topic-name controller/handler 就会收到请求,并携带着消息内容。在这个请求中,咱们能够从消息中知道须要怎样更新数据,进而完成数据同步的任务。google

一致性 - memcahce

上面介绍了数据同步的具体流程,在这个过程当中一致性的保证是很重要的,主要体如今数据同步时须要确保全部的 instance 都成功消费“数据同步”消息,POST /m 才能以请求成功处理的状态返回。spa

在这里 version 的 instance 数量 n 是“常量”,那么咱们只需在一个公共的地方维护一个标识 A,标识当前已经成功同步的 instance 数量,当这个 A == n 时也就意味着全部 instance 都成功同步了数据。

appengine-memcache 是主要应用于 appengine 上的分布式缓存服务,咱们能够在上面存储这个惟一标识,POST /m 请求中成功发送“数据同步”消息后,就以轮询的方式从 memcache 中查询 A 的值,同时在 POST /_ah/push-handlers/your-topic-name 中要递增 A 的值,A == n 时就代表同步完成。

相关文章
相关标签/搜索