Azure函数响应中的流处理输出

时间:2018-10-21 20:25:01

标签: c# .net

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

通常,一切正常,但似乎没有输出发送到客户端。如果可能,我想刷新每一行,尽管我知道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;
        }
    }
}

1 个答案:

答案 0 :(得分:1)

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

我没有 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