简体   繁体   English

从另一个云 Function 调用云 Function

[英]Calling a Cloud Function from another Cloud Function

I am using a Cloud Function to call another Cloud Function on the free spark tier.我正在使用 Cloud Function 在免费 spark 层调用另一个 Cloud Function。

Is there a special way to call another Cloud Function?有没有特殊的方法可以调用另一个云Function? Or do you just use a standard http request?或者您只是使用标准的 http 请求?

I have tried calling the other function directly like so:我试过像这样直接调用另一个 function :

exports.purchaseTicket = functions.https.onRequest((req, res) => {    
  fetch('https://us-central1-functions-****.cloudfunctions.net/validate')
    .then(response => response.json())
    .then(json => res.status(201).json(json))
})

But I get the error但我得到了错误

FetchError: request to https://us-central1-functions- ****.cloudfunctions.net/validate failed, reason: getaddrinfo ENOTFOUND us-central1-functions-*****.cloudfunctions.net us-central1-functions-*****.cloudfunctions.net:443 FetchError:对https: //us-central1-functions-****.cloudfunctions.net/validate 的请求失败,原因:getaddrinfo ENOTFOUND us-central1-functions-*****.cloudfunctions.net us-central1-functions -*****.cloudfunctions.net:443

Which sounds like firebase is blocking the connection, despite it being a google owned, and therefore it shouldn't be locked这听起来像 firebase 正在阻止连接,尽管它是谷歌拥有的,因此它不应该被锁定

the Spark plan only allows outbound network requests to Google owned services. Spark 计划仅允许对 Google 拥有的服务的出站网络请求。

How can I make use a Cloud Function to call another Cloud Function?如何使用 Cloud Function 调用另一个 Cloud Function?

You don't need to go through the trouble of invoking some shared functionality via a whole new HTTPS call.您无需经历通过全新的 HTTPS 调用来调用某些共享功能的麻烦。 You can simply abstract away the common bits of code into a regular javascript function that gets called by either one.您可以简单地将代码的常见部分抽象为一个常规的 javascript 函数,该函数由任一函数调用。 For example, you could modify the template helloWorld function like this:例如,您可以像这样修改模板 helloWorld 函数:

var functions = require('firebase-functions');

exports.helloWorld = functions.https.onRequest((request, response) => {
  common(response)
})

exports.helloWorld2 = functions.https.onRequest((request, response) => {
  common(response)
})

function common(response) {
  response.send("Hello from a regular old function!");
}

These two functions will do exactly the same thing, but with different endpoints.这两个函数将做完全相同的事情,但具有不同的端点。

It's possible to invoke another Google Cloud Function over HTTP by including an authorization token.通过包含授权令牌,可以通过 HTTP 调用另一个 Google Cloud Function。 It requires a primary HTTP request to calculate the token, which you then use when you call the actual Google Cloud Function that you want to run.它需要一个主要的 HTTP 请求来计算令牌,然后在调用要运行的实际 Google Cloud 函数时使用该令牌。

https://cloud.google.com/functions/docs/securing/authenticating#function-to-function https://cloud.google.com/functions/docs/securing/authenticating#function-to-function

const {get} = require('axios');

// TODO(developer): set these values
const REGION = 'us-central1';
const PROJECT_ID = 'my-project-id';
const RECEIVING_FUNCTION = 'myFunction';

// Constants for setting up metadata server request
// See https://cloud.google.com/compute/docs/instances/verifying-instance-identity#request_signature
const functionURL = `https://${REGION}-${PROJECT_ID}.cloudfunctions.net/${RECEIVING_FUNCTION}`;
const metadataServerURL =
  'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=';
const tokenUrl = metadataServerURL + functionURL;

exports.callingFunction = async (req, res) => {
  // Fetch the token
  const tokenResponse = await get(tokenUrl, {
    headers: {
      'Metadata-Flavor': 'Google',
    },
  });
  const token = tokenResponse.data;

  // Provide the token in the request to the receiving function
  try {
    const functionResponse = await get(functionURL, {
      headers: {Authorization: `bearer ${token}`},
    });
    res.status(200).send(functionResponse.data);
  } catch (err) {
    console.error(err);
    res.status(500).send('An error occurred! See logs for more details.');
  }
};

To answer the question, you can do an https request to call another cloud function:要回答这个问题,你可以做一个 https 请求来调用另一个云函数:

export const callCloudFunction = async (functionName: string, data: {} = {}) => {
    let url = `https://us-central1-${config.firebase.projectId}.cloudfunctions.net/${functionName}`
    await fetch(url, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
        },
        body: JSON.stringify({ data }),
    })
}

(Note we are using the npm package 'node-fetch' as our fetch implementation.) (请注意,我们使用 npm 包“node-fetch”作为我们的 fetch 实现。)

And then simply call it:然后简单地调用它:

callCloudFunction('search', { query: 'yo' })

There are legitimate reasons to do this.这样做是有正当理由的。 We used this to ping our search cloud function every minute and keep it running.我们用它每分钟 ping 我们的搜索云功能并保持它运行。 This greatly lowers response latency for a few dollars a year.这大大降低了每年几美元的响应延迟。

Despite of the question tag and other answers concern the javascript I want to share the python example as it reflects the title and also authentification aspect mentioned in the question.尽管问题标签和其他答案与javascript有关,但我想分享python示例,因为它反映了问题中提到的标题和身份验证方面。

Google Cloud Function provide REST API interface what incluse call method that can be used in another Cloud Function. Google Cloud Function 提供了REST API 接口,包括可以在另一个 Cloud Function 中使用的调用方法。 Although the documentation mention using Google-provided client libraries there is still non one for Cloud Function on Python.尽管文档中 提到使用 Google 提供的客户端库,但 Python 上的 Cloud Function 仍然没有。

And instead you need to use general Google API Client Libraries.相反,您需要使用通用的 Google API 客户端库。 [This is the python one]. [这是蟒蛇一个]。 3 3

Probably, the main difficulties while using this approach is an understanding of authentification process.使用这种方法的主要困难可能是对身份验证过程的理解。 Generally you need provide two things to build a client service: credentials ans scopes .通常,您需要提供两件事来构建客户端服务:凭据作用域

The simpliest way to get credentials is relay on Application Default Credentials (ADC) library.获取凭据的最简单方法是在应用程序默认凭据 (ADC) 库上进行中继。 The rigth documentation about that are:有关此的正确文档是:

  1. https://cloud.google.com/docs/authentication/production https://cloud.google.com/docs/authentication/production
  2. https://github.com/googleapis/google-api-python-client/blob/master/docs/auth.md https://github.com/googleapis/google-api-python-client/blob/master/docs/auth.md

The place where to get scopes is the each REST API function documentation page.获取范围的位置是每个 REST API 函数文档页面。 Like, OAuth scope: https://www.googleapis.com/auth/cloud-platform像, OAuth 范围:https://www.googleapis.com/auth/cloud-platform

The complete code example of calling 'hello-world' clound fucntion is below.调用 'hello-world' 云函数的完整代码示例如下。 Before run:运行前:

  1. Create default Cloud Function on GCP in your project.在您的项目中的 GCP 上创建默认 Cloud Function。
  • Keep and notice the default service account to use保留并注意要使用的默认服务帐户
  • Keep the default body.保留默认正文。
  1. Notice the project_id , function name , location where you deploy function.请注意project_id函数名称、您部署函数的位置
  2. If you will call function outside Cloud Function environment (locally for instance) setup the environment variable GOOGLE_APPLICATION_CREDENTIALS according the doc mentioned above如果您将在 Cloud Function 环境之外调用函数(例如本地),请根据上述文档设置环境变量 GOOGLE_APPLICATION_CREDENTIALS
  3. If you will call actualy from another Cloud Function you don't need to configure credentials at all.如果您要从另一个 Cloud Functions 实际调用,则根本不需要配置凭据。
from googleapiclient.discovery import build
from googleapiclient.discovery_cache.base import Cache
import google.auth

import pprint as pp

def get_cloud_function_api_service():
    class MemoryCache(Cache):
        _CACHE = {}

        def get(self, url):
            return MemoryCache._CACHE.get(url)

        def set(self, url, content):
            MemoryCache._CACHE[url] = content

    scopes = ['https://www.googleapis.com/auth/cloud-platform']

    # If the environment variable GOOGLE_APPLICATION_CREDENTIALS is set,
    # ADC uses the service account file that the variable points to.
    #
    # If the environment variable GOOGLE_APPLICATION_CREDENTIALS isn't set,
    # ADC uses the default service account that Compute Engine, Google Kubernetes Engine, App Engine, Cloud Run,
    # and Cloud Functions provide
    #
    # see more on https://cloud.google.com/docs/authentication/production
    credentials, project_id = google.auth.default(scopes)

    service = build('cloudfunctions', 'v1', credentials=credentials, cache=MemoryCache())
    return service


google_api_service = get_cloud_function_api_service()
name = 'projects/{project_id}/locations/us-central1/functions/function-1'
body = {
    'data': '{ "message": "It is awesome, you are develop on Stack Overflow language!"}' # json passed as a string
}
result_call = google_api_service.projects().locations().functions().call(name=name, body=body).execute()
pp.pprint(result_call)
# expected out out is:
# {'executionId': '3h4c8cb1kwe2', 'result': 'It is awesome, you are develop on Stack Overflow language!'}

I found a combination of two of the methods works best我发现两种方法的组合效果最好

const anprURL = `https://${REGION}-${PROJECT_ID}.cloudfunctions.net/${RECEIVING_FUNCTION}`;
const metadataServerURL =
  'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=';
const tokenUrl = metadataServerURL + anprURL;

// Fetch the token
const tokenResponse = await fetch(tokenUrl, {
    method: "GET"
    headers: {
      'Metadata-Flavor': 'Google',
    },
});

const token = await tokenResponse.text();

const functionResponse = await fetch(anprURL, {
    method: 'POST',
    headers: {
        "Authorization": `bearer ${token}`,
        'Content-Type': 'application/json',
    },
    body: JSON.stringify({"imageUrl": url}),
});

// Convert the response to text
const responseText = await functionResponse.text();

// Convert from text to json
const reponseJson = JSON.parse(responseText);

These suggestions don't seem to work anymore.这些建议似乎不再有效。

To get this to work for me, I made calls from the client side using httpsCallable and imported the requests into postman.为了让它对我有用,我使用 httpsCallable 从客户端拨打电话并将请求导入邮递员。 There were some other links to https://firebase.google.com/docs/functions/callable-reference there were helpful.还有一些其他指向https://firebase.google.com/docs/functions/callable-reference 的链接很有帮助。 But determining where the information was available took a bit of figuring out.但是确定信息的可用位置需要一些弄清楚。

I wrote everything down here as it takes a bit of explaining and some examples.我在这里写下了所有内容,因为它需要一些解释和一些示例。

https://www.tiftonpartners.com/post/call-google-cloud-function-from-another-cloud-function https://www.tiftonpartners.com/post/call-google-cloud-function-from-another-cloud-function

Here's an inline version for the 'url' might expire.这是“url”的内联版本可能会过期。

This 'should' work, it's not tested but based off of what I wrote and tested for my own application.这“应该”有效,它没有经过测试,而是基于我为自己的应用程序编写和测试的内容。

module.exports = function(name,context) {
    const {protocol,headers} = context.rawRequest;
    const host = headers['x-forwardedfor-host'] || headers.host;
    // there will be two different paths for
    // production and development
    const url = `${protocol}://${host}/${name}`;
    const method = 'post';    
    const auth = headers.authorization;
    
    return (...rest) => {
        const data = JSON.stringify({data:rest});
        const config = {
            method, url, data,
            headers: {
               'Content-Type': 'application/json',
               'Authorization': auth,
               'Connection': 'keep-alive',
               'Pragma': 'no-cache,
               'Cache-control': 'no-cache',
            }
        };
        try {
            const {data:{result}} = await axios(config);
            return result;        
        } catch(e) {
            throw e;
        }
    }
}

This is how you would call this function.这就是您调用此函数的方式。

const crud = httpsCallable('crud',context);
return await crud('read',...data);

context you get from the google cloud entry point and is the most important piece, it contains the JWT token needed to make the subsequent call to your cloud function (in my example its crud)你从谷歌云入口点获得的上下文是最重要的部分,它包含对你的云函数进行后续调用所需的 JWT 令牌(在我的例子中是它的 crud)

To define the other httpsCallable endpoint you would write an export statement as follows要定义另一个 httpsCallable 端点,您将编写如下导出语句

exports.crud = functions.https.onCall(async (data, context) => {})

It should work just like magic.它应该像魔术一样工作。

Hopefully this helps.希望这会有所帮助。

扩展 Shea Hunter Belsky 的回答我很想通知您,调用 google 的元数据服务器以获取授权令牌在本地机器上不起作用

Since fetch is not readily available in Node.JS and my project was already using the axios library, I did it like this:由于fetch在 Node.JS 中不容易使用,而且我的项目已经在使用axios库,所以我这样做了:

const url = `https://${REGION}-${PROJECT_ID}.cloudfunctions.net/${FUNCTION_NAME}`;
const headers = {
  'Content-Type': 'application/json',
};
const response = await axios.post(url, { data: YOUR_DATA }, { headers });

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 从 Blazor WASM 调用 Google Cloud Function - Calling Google Cloud Function from Blazor WASM Python3 中的 Cloud Function - 从 Google Cloud Bucket 复制到另一个 Google Cloud Bucket - Cloud Function in Python3 - copy from Google Cloud Bucket to another Google Cloud Bucket 从云调度程序调用谷歌云 function 时获取权限被拒绝错误 - Getting permission denied error when calling Google cloud function from Cloud scheduler 调用 Google Cloud 时出错 function - 超出限制 - Error calling Google Cloud function - limits exceeded 使用 Firebase 云从 Cloud Firestore 读取数据 function - Read data from Cloud firestore with Firebase cloud function 保护从 gcp 云函数到 Cloud run Api 的 api 调用 - Protect api call from gcp cloud function to Cloud run Api 从云端在 GKE 中运行作业 function - Run Job in GKE from a cloud function 从谷歌云返回的错误请求 function - Bad request returned from google cloud function 通过授权从 Internet 以编程方式触发云功能 - Trigger cloud function programatically from internet with Authorization 云 function 从特定应用程序发送 FCM - cloud function to send FCM from specific app
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM