Azure Funcion App with Hybrid Connection To OnPrem Server

Simulate Function App -> HTTPClient request -> Hybrid Connection Manager -> OnPremIIS

  1. Create a Azure VM and install IIS

  2. Create a Web API project by Visual Studio and deploy to IIS server

  3. Create a function with Standard plan

  4. Config Hybrid connection manager

  5. Install Hybrid connection manager connector in OnPrem Server (step 1)

  6. Create a Function App project simulate HTTPClient to send request to IIS Server via Hybrid Connection Manager

  7. Deploy Function App to Azure Function

  8. Execute request -> OnPremIIS (Thread sleep 50 seconds) -> get response

    Function log

     
    1
    2
    3
    4
    5
    6
    7
    2019-11-07T02:46:21.684 [Information] Executing 'Function1' (Reason='This function was programmatically called via the host APIs.', Id=efdfa4cb-446d-4102-b7ce-3d3eea05b04c)
    2019-11-07T02:46:21.806 [Information] C# HTTP trigger function processed a request.
    2019-11-07T02:46:22.289 [Information] Start client.SendAsync
    2019-11-07T02:47:12.848 [Information] End client.SendAsync
    2019-11-07T02:47:12.849 [Information] 200
    2019-11-07T02:47:12.849 [Information] "50 seconds, you can see it."
    2019-11-07T02:47:12.903 [Information] Executed 'Function1' (Succeeded, Id=efdfa4cb-446d-4102-b7ce-3d3eea05b04c)

    HTTP 200 with Output:

     
    1
    {"Timestamp":"2:46:22 AM","FileName":"YourFileName: 3","FileSize":"ProvideFileSize","ReturnCode":"0","ReturnMessage":null}
  9. Execute request -> OnPremIIS (Thread sleep 150 seconds) -> get exception

    Function Log:

    1
    2
    3
    4
    5
    6
    2019-11-07T02:31:43.647 [Information] Executed 'Function1' (Succeeded, Id=61383988-8e65-4cf8-9995-3cd718ed289e)
    2019-11-07T02:32:50 No new trace in the past 1 min(s).
    2019-11-07T02:32:50.030 [Information] One or more errors occurred. (A task was canceled.)
    2019-11-07T02:32:50.035 [Information] A task was canceled.
    2019-11-07T02:32:50.035 [Information] [null]
    2019-11-07T02:32:50.036 [Information] Executed 'Function1' (Succeeded, Id=3961b106-3f59-4909-90d3-4d16a1f7bb53)

    HTTP 200 with Output:

    1
    {"Timestamp":"2:31:10 AM","FileName":"YourFileName: 1","FileSize":"ProvideFileSize","ReturnCode":"9","ReturnMessage":"Type: System.AggregateException, Msg: One or more errors occurred. (A task was canceled.)"}
  10. Reviewed Exception in Application Insights

Root cause:

Responding to requests
The receiver MUST respond. Repeated failure to respond to requests while maintaining the connection might result in the listener getting blacklisted.
Responses may be sent in any order, but each request must be responded to within 60 seconds or the delivery will be reported as having failed. The 60-second deadline is counted until the response frame has been received by the service. An ongoing response with multiple binary frames cannot become idle for more than 60 seconds or it is terminated.

For more information: https://docs.microsoft.com/en-us/azure/service-bus-relay/relay-hybrid-connections-protocol

Sample Code:

My Function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System.Diagnostics;
using System.Threading;
using System.Net.Http;
using System.Text;

namespace TestAyncFunApp
{
public static class Function1
{
[FunctionName("Function1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");

MyDemoReturn returnValue = new MyDemoReturn();

string name = req.Query["name"];

string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
name = name ?? data?.name;
try
{
// give timestemp
returnValue.Timestamp = DateTime.Now.ToLongTimeString();
returnValue.FileName = "YourFileName: " + name;
returnValue.FileSize = "ProvideFileSize";
returnValue.ReturnCode = "1"; //1 => Start

var requestData = JsonConvert.SerializeObject("test");

var requestURI = "http://OnPremIIS/api/values/" + name;
using (var client = new HttpClient())
{
var httpContent = new StringContent(requestData, Encoding.UTF8, "application/json");
var request = new HttpRequestMessage
{
RequestUri = new Uri(requestURI),
Method = HttpMethod.Get
};
log.LogInformation("Start client.SendAsync");
var response = client.SendAsync(request).Result;
log.LogInformation("End client.SendAsync");
int statusCode = (int)response.StatusCode;
log.LogInformation(statusCode.ToString());
var responseContent = response.Content;
var responseString = responseContent.ReadAsStringAsync().Result;

log.LogInformation(responseString);
returnValue.ReturnCode = "0"; //0 => Success
}
}
catch (Exception ex)
{
returnValue.ReturnCode = "9"; //9 => exception, you can add more return code
returnValue.ReturnMessage = string.Format("Type: {0}, Msg: {1}", ex.GetType().FullName, ex.Message);
log.LogInformation(ex.Message);
if(ex.InnerException != null)
{
log.LogInformation(ex.InnerException.Message);
log.LogInformation(ex.InnerException.StackTrace);
}
}

return name != null
? (ActionResult)new OkObjectResult(Newtonsoft.Json.JsonConvert.SerializeObject(returnValue))
: new BadRequestObjectResult("Please pass a name on the query string or in the request body");
}

public class MyDemoReturn
{
public string Timestamp { get; set; }
public string FileName { get; set; }
public string FileSize { get; set; }
public string ReturnCode { get; set; }

public string ReturnMessage { get; set; }
}
}
}

############################

My Web API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public string Get(int id)
{
string result = "value";
if(id.Equals(1))
{
System.Threading.Thread.Sleep(150000);
result = "150 seconds, you should not get it.";
}
else
{
System.Threading.Thread.Sleep(50000);
result = "50 seconds, you can see it.";
}

return result;
}

######################################

Function app timeout duration
The timeout duration of a function app is defined by the functionTimeout property in the host.json project file. The following table shows the default and maximum values in minutes for both plans and in both runtime versions:
More information: https://docs.microsoft.com/en-us/azure/azure-functions/functions-scale

HTH. By Jacky 2019-11-19