首页 > 解决方案 > Azure Function 响应中的流式处理输出

问题描述

我有一个有效的 HTTP 触发器函数,它启动一个new Process. 我想做的是获取标准错误/输出并将它们发送到 Web 客户端,但我还没有弄清楚流程处理和流式传输的正确组合以使其工作......

通常,一切都运行,但似乎没有输出发送到客户端。如果可能的话,我想刷新每一行,尽管我知道网络服务器可能会阻止它。目前我正在通过 AF webjob 主机在本地进行测试。我猜这与对象的生命周期有关,或者这可能是不可能的。

我尝试了各种方法,但这是我目前拥有的代码,它运行并且过程成功完成,输出被记录,但没有输出到 Postman(甚至不是 200 OK):

[FunctionName("Function1")]
public static async Task<HttpResponseMessage> Run(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)]HttpRequestMessage req,
    CancellationToken cancellationToken,
    TraceWriter log
)
{
    using (var memoryStream = new MemoryStream())
    using (var streamWriter = new StreamWriter(memoryStream, Encoding.UTF8, 1, true))
    //using (var streamWriter = new StreamWriter(memoryStream))
    {
        streamWriter.WriteLine($"Starting execution.");

        using (var proc = new Process())
        {
            proc.StartInfo.FileName = @"D:\Runner.exe";
            proc.StartInfo.RedirectStandardError = true;
            proc.StartInfo.RedirectStandardOutput = true;
            proc.StartInfo.UseShellExecute = false;
            //proc.OutputDataReceived += (object sender, DataReceivedEventArgs e) =>
            //{
            //    log.Info(e.Data);
            //    streamWriter.WriteLine(e.Data);
            //};
            //proc.ErrorDataReceived += (object sender, DataReceivedEventArgs e) =>
            //{
            //    log.Error(e.Data);
            //    streamWriter.WriteLine(e.Data);
            //};
            proc.Start();

            /// Need to drain the output otherwise the process will
            /// hang and not stop until the computer is shut down.
            /// It can be killed by Administrator but will remain as
            /// a process until restarted.
            /// taskkill /f /im runner.exe
            /// tasklist /fi "imagename eq runner.exe"
            while (!proc.StandardOutput.EndOfStream)
            {
                log.Info(proc.StandardOutput.ReadLine());
                streamWriter.Flush();
            }

            //proc.WaitForExit();
            streamWriter.Flush();

            var response = new HttpResponseMessage
            {
                StatusCode = HttpStatusCode.OK,
                Content = new StreamContent(memoryStream)
            };

            return response;
        }
    }
}

标签: c#.net

解决方案


我的例子比问题要新一些,但这是你如何做到的。

我没有runner.exe,我认为使用 CliWrap 是使用System.Diagnostics.Process. 我尝试使用不需要同步 IO 的解决方案,但如果您确实需要做一些确实需要的事情,您可以"FUNCTIONS_V2_COMPATIBILITY_MODE": true在函数主机环境变量中设置以使其工作。

using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using CliWrap;

namespace AdamCoulter
{
    public static class StreamBack
    {
        [FunctionName("StreamBack")]
        public static HttpResponseMessage Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequestMessage request)
        {
            var response = new HttpResponseMessage
            {
                RequestMessage = request,
                Content = new PushStreamContent(new Func<Stream, HttpContent, TransportContext, Task>(RunTerraform), "text/event-stream")
            };
            response.Headers.TransferEncodingChunked = true;

            return response;
        }

        public static async Task RunTerraform(Stream stream, HttpContent content, TransportContext context)
        {
            _ = await Cli.Wrap("terraform")
                .WithArguments("init")
                .WithWorkingDirectory("/Volumes/Code/tftest/")
                .WithStandardOutputPipe(PipeTarget.ToStream(stream))
                .ExecuteAsync();
            stream.Close();
        }
    }
}
GET /api/StreamBack HTTP/1.1
Host: localhost:7071

chrome 中的增量流式输出(在生成终端输出时逐位显示):


[0m[1mInitializing the backend...[0m

[0m[1mInitializing provider plugins...[0m
- Reusing previous version of hashicorp/azurerm from the dependency lock file
- Using previously-installed hashicorp/azurerm v2.58.0

[0m[1m[32mTerraform has been successfully initialized![0m[32m[0m
[0m[32m
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.[0m

推荐阅读