无法使用预定的 firebase 功能从外部服务接收大 xml

Can not receive large xml from external service with scheduled firebase function

我正在使用 Typescript 作为后端语言,并安排了一个函数来从外部服务请求大 xml 文件 (75MB) 并对其进行解析。我必须设置一个 soap 客户端来接收 xml.

export const updateList: Function =
    functions.region('europe-west3').pubsub.schedule('1 0 1 * *').onRun((_context) => {

const url = 'https://website.org';
const wsdl_options = {
    ntlm: true,
    username: 'username',
    password: 'password',
    domain: "domain",
    workstation: "workstation"
};

soap.createClient(url, { wsdl_options }, (error, client) => {
    if (error) {
        console.log('Error in making soap client', error);
    }
    else {
        client.setSecurity(new soap.NTLMSecurity(wsdl_options.username, wsdl_options.password, wsdl_options.domain));
        const getXml = client.Service.HttpBinding_Service.GetXML;
        try {
            getXml((error, dataXml) => {
                console.log('Inside xml');
                if (error) {
                    console.log(error);
                }
                else {
                    console.log('...done.')
                    processData(dataXml);
                }
            });
        } catch (error) {
            console.log('Something wrong' + error);
        }
    }
})
                
});

调用 getXml 后停止。当从 PC 在本地调用相同的代码时,它运行良好并且接收 xml 并解析它所花费的时间不超过 20 秒。外部服务有 IP 白名单范围,因此添加了本地 WiFi 网络。我已经为 Firebase 功能设置了静态 IP 地址,并且外部服务提供商已将其添加到白名单 IP。我已经设置了 Cloud NAT、VPC 网络、无服务器 VPC 访问、VPC 连接器等

因此来自计划函数的流量正确地通过静态 IP 地址并且连接建立时没有错误,但它无法接收大 xml。 我试图获取 xml 5MB,它在调用该函数后 5 分钟后收到了它。 (我正在检查 firebase 函数中的控制台日志)

我该如何解决这个问题?有什么想法吗?

更新。在 20.06

上用完整代码更新了代码示例

正如所怀疑的那样,你已经正确地启动了函数,但是因为你没有返回一个 Promise 来“保持函数活动”,执行你的函数的实例被置于“非活动”状态其中网络请求被阻止并且 CPU 处理受到严重限制。因为您的系统不受如此激进的实例管理系统的约束,所以在本地开发时您不会看到类似的性能下降。重要的是,当您的函数处于“非活动”状态时,它可以随时终止,因此请确保在解析返回的 Promise.

之前完成任何工作。

此行为在 in the documentation 及其链接视频中有更详细的介绍。

根据您的代码,您似乎正在使用 soap package,它方便地同时具有基于回调和基于 Promise 的 API。这允许我们替换:

soap.createClient(url, { wsdl_options }, (error, client) => {
    if (error) {
        console.log("Error in making soap client", error);
    } else {
        // do something with client
    }
});

只有

const client = await soap.createClientAsync(url, { wsdl_options });

// do something with client

利用这些基于 Promise 的 API,您的代码将类似于:

import * as functions from "firebase-functions";
import * as soap from "soap";

async function processData(data) {
  // do something with data
}

export const updateList = functions
  .region("europe-west3")
  .pubsub.schedule("1 0 1 * *")
  .onRun(async () => { // <- async added here
    const url = "https://website.org";
    const wsdl_options = {
      ntlm: true,
      username: "username",
      password: "password",
      domain: "domain",
      workstation: "workstation",
    };

    const client = await soap.createClientAsync(url, { wsdl_options });

    client.setSecurity(
      new soap.NTLMSecurity({
        username: wsdl_options.username,
        password: wsdl_options.password,
        domain: wsdl_options.domain
      })
    );

    console.log("SOAP client initialized, obtaining XML data...");

    const xmlData = await client.Service.HttpBinding_Service.GetXMLAsync();
    // if the above isn't available, you could use:
    // const xmlData = new Promise((resolve, reject) =>
    //   client.Service.HttpBinding_Service.GetXML(
    //     (err, result) => err ? reject(err) : resolve(result)
    //   )
    // );

    console.log("obtained XML data, now processing...");

    await processData(xmlData);

    console.log('Success!');
  });

注意: 当我将它们拼凑在一起时,TypeScript 抛出了一个关于 NTLMSecurity 接受 3 个参数的错误,而它应该是一个单一的对象。所以上面改正了。


您可以在 this blog post or by checking out other questions here on Whosebug 中阅读有关 Promise 的更多信息。