尝试使用LRO运行云功能

问题描述

背景 我正在创建一个自治的Google AutoML end end系统。我创建了一个功能,可以在培训开始时接收云发布/订阅消息。云功能使用操作ID获取培训的操作状态。如果模型训练完成(操作元数据= true),则该函数会将模型ID发送给部署函数,并发送带有要在其上进行预测的模型的modelID的发布/订阅消息。我从这篇文章How to programmatically get model id from google-cloud-automl with node.js client library

中找到了SO的解决方

问题 我遇到的问题是10分钟的云功能超时。我在关于潜在解决方案的reddit上写了这个问题。 https://www.reddit.com/r/googlecloud/comments/jqr213/cloud_function_to_compute_engine/对于主要在云功能环境中编写的系统,计算引擎解决方案似乎不切实际。在尝试实施cron作业解决方案时,我想到了云功能的重试功能。它会保留相同的事件,并将重试该功能长达一周。重试文档为https://cloud.google.com/functions/docs/bestpractices/retries,该如何取消该功能以使其重试直到变为真并完成部署和发布/发布消息?我的想法是在if else语句中包括系统的结尾,我只是在努力寻找有关此文件的记录,如果它确实可以工作。

代码

const {AutoMlClient} = require('@google-cloud/automl').v1;
// Instantiates a client
  const client = new AutoMlClient();
exports.helloPubSub = (event,context) => {
//Imports the Google Cloud AutoML library
  const message = event.data
    ? Buffer.from(event.data,'base64').toString()
    : 'Hello,World';
  const model = message;
  console.log(model);
  const modelpath = message.replace('"','');
  const modelID = modelpath.replace('"','');
  const message1 = model.replace('projects/170974376642/locations/us-central1/operations/','');
  const message2 = message1.replace('"','');
  const message3 = message2.replace('"','');
  console.log(`Operation ID is: ${message3}`)
  getoperationStatus(message3,modelID);
  
}
  // [START automl_vision_classification_deploy_model_node_count]
async function getoperationStatus(opId,message) {
  
  console.log('Starting operation status');
  const opped = opId;
  const data = message; 
  const projectId = '170974376642';
  const location = 'us-central1';
  const operationId = opId;
  // Construct request
  const request = {
    name: `${message}`,};
  console.log('Made it to the response');
  const [response] = await client.operationsClient.getoperation(request);

  console.log(`Name: ${response.name}`);
  console.log(`Operation details:`);
  
  var apple = JSON.stringify(response);
  console.log(apple);
  
  console.log('Loop until the model is ready to deploy');

  if (apple.includes('True')) { 
     const appleF = apple.replace((/projects\/[a-zA-Z0-9-]*\/locations\/[a-zA-Z0-9-]*\/models\//,''));
     deployModelWithNodeCount(appleF);
     pubSub(appleF);
} else {
     getoperationStatus(opped,data);
}
  

}
  async function pubSub(id) {
    const topicName = 'modelID';
    const data = JSON.stringify({foo: `${id}`});
    async function publishMessage() {
    // Publishes the message as a string,e.g. "Hello,world!" or JSON.stringify(someObject)
    const dataBuffer = Buffer.from(data);

    try {
      const messageId = await pubSubClient.topic(topicName).publish(dataBuffer);
      console.log(`Message ${messageId} published.`);
    } catch (error) {
      console.error(`Received error while publishing: ${error.message}`);
      process.exitCode = 1;
    }
  }
    publishMessage();
  // [END pubsub_publish_with_error_handler]
  // [END pubsub_quickstart_publisher]


    process.on('unhandledRejection',err => {
    console.error(err.message);
    process.exitCode = 1;
});
  }
  async function deployModelWithNodeCount(message) {
    
    
    
    const projectId = 'ireda1';
    const location = 'us-central1';
    const modelId = message;

    // Construct request
    const request = {
      name: client.modelPath(projectId,location,modelId),imageClassificationModelDeploymentMetadata: {
        nodeCount: 1,},};

    const [operation] = await client.deployModel(request);

    // Wait for operation to complete.
    const [response] = await operation.promise();
    console.log(`Model deployment finished. ${response}`);
  }
  // [END automl_vision_classification_deploy_model_node_count]

解决方法

您可以考虑对代码进行一些改进。首先,重要的是要了解云功能是短暂的。 9 minutes is the maximum,您的功能将处于活动状态。如果您正在寻找一种可以在后台执行并且需要最少基础架构的解决方案,那么Cloud Functions并不是用于后台操作的,我建议您看看Cloud Run

现在,让我们看一下代码的某些部分,以及如何使用以Cloud Functions和PubSub为骨干的不同架构来改进代码。

等待模型部署

您使用的代码是:

  if (apple.includes('True')) { 
     const appleF = apple.replace((/projects\/[a-zA-Z0-9-]*\/locations\/[a-zA-Z0-9-]*\/models\//,''));
     deployModelWithNodeCount(appleF);
     pubSub(appleF);
} else {
     getOperationStatus(opped,data);
}

首先,我强烈建议不要在此处使用递归,因为a)可以通过一个简单的循环来处理,b)您在没有任何超时或退避策略的情况下轰炸该服务。后者可能会导致您的服务崩溃或端点开始拒绝您的请求。

例如,可以至少设置timeout函数来改进代码,例如:

setTimeout(getOperationStatus(opped,data),1000)

出于可读性考虑,我还建议以后再使用循环,因为无论如何您都在使用async模式:

status = getOperationStatus(opped,data);
while(!status){
 await new Promise(t => setTimeout(t,1000));
 status = getOperationStatus(opped,data);
} 

在这种情况下,您需要将其分为两个功能-1)getOperationStatus,实际上仅返回状态,以及2)waitForDeployment,其轮询状态并将其与预期的比较结果,并决定a)等待并重试或b)放弃并返回

这可能会使您的代码更好,但不能解决系统设计的根本问题。为了理解这一点,让我们看一下责任划分和以不同的方式构建系统。附带说明,指南here不适用于Cloud Function应用程序。 enter image description here

一些解释:

  • 激活功能初始化整个过程,它调用Vision Auto ML开始部署。它仅获取操作的ID并将其推入队列
  • Cloud Scheduler 每隔X分钟/秒将触发器推送到PubSub(或者也可以将函数调用为终结点),表示该是检查进度的时候了
  • 轮询功能一旦触发,要求下一个ID进行检查,查询Cloud AutoML,如果完成,确认消息并写入结果,否则退出。您需要注意此处的确认配置。有用的信息是here

状态查询 我注意到的次要问题是您如何轮询状态。为什么不只查询此URL GET https://automl.googleapis.com/v1/projects/project-id/locations/us-central1/operations/operation-id并获取完成状态(请查看here了解详情)

结论:云功能是短暂的,一次只能处理一项操作,无需等待。如果您想要一个简单的循环来等待结果,请使用Cloud Run

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...