
GCP-appengine通过version管理应用,你可以在appengine上部署多个version(dev、qa等),而每个version可以有多个instance,一个instance可简单理解为一个基于Spring Boot实现的微服务,当有请求到达时appengine会根据一定策略选择由哪一个instance处理该请求,如果现有的instance处理的流量已经很多,那么appengine会启动新的instance来处理这个请求,这个行为主要由instance的三种扩缩策略决定:手动、自动和基础。
背景
应用数据保存在GCP的数据存储组件datastore中,datastore是一个NoSQL数据库。假设我们的应用对其中一部分数据M的操作的QPS要求很高,如果每次都从datastore查询则不能满足需求。
//更新M数据
POST/m
//查询M数据
GET/m
解决方案
为了加快请求的处理速度,我们在应用启动时(即instance启动时)先将这部分数据全部加载到内存,之后直接从内存中读取,而不是每次都从datastore中查询。这种方式有几个问题需要解决:
1.每次请求到达时不确定appengine会将请求路由给哪一个instance,所以当这部分数据有更新(POST/m)时需要通知该version的所有instance进行数据同步
2.在POST/m请求中要确保所有instance都成功同步了数据(所有instance中M数据保持一致),才能以请求处理成功的状态返回。
数据同步
instance间通信是一个棘手的问题,因为appengine没有直接提供API或外部组件来完成这件事情,甚至你想将请求发给指定的instance都需要牺牲很多灵活性才能实现。当然大多数情况下请求的路由应该由appengine控制,只有在实现某些特殊需求时才可以考虑一些特殊做法。
数据同步的基本思路是在POST/m时先在datastore transactions中更新M数据,事务成功提交后再通过pubsub通知所有instance从datastore更新最新数据,所有instance都确认成功更新数据后,请求成功。
通过查阅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。
pubsub是GCP的消息组件,消息发送后消费者有两种方式消费消息:pull和push
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就会收到请求,并携带着消息内容。在这个请求中,我们可以从消息中知道需要怎样更新数据,进而完成数据同步的任务。
一致性
上面介绍了数据同步的具体流程,在这个过程中一致性的保证是很重要的,主要体现在数据同步时需要确保所有的instance都成功消费“数据同步”消息POST/m`才能以请求成功处理的状态返回。
在这里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时就表明同步完成
相关推荐: 药易购获评“医药商业百强企业”等多项称号药易购获评“医药商业百强企业”等多项称号
3月29日消息,全国工商联医药业商会年度峰会暨青岛高新区医药产业推介会近日举行。会议现场,2021-2022年度中国医药行业最具影响力榜单揭晓,四川合纵药易购医药股份有限公司获评“医药商业百强企业”、“医药行业成长50强企业”、“医药行业守法诚信企业”多项称号…
码刀科技(www.lekshop.cn)是国内知名企业级电商平台提供商,为企业级商家提供最佳的电商平台搭建(多种模式电商平台搭建:B2B/B2B2C/B2C/O2O/新零售/跨境等)、平台管理系统开发及互联网采购解决方案服务, 联系客服了解更多.