Compare commits
2 Commits
main
...
davidfowl/
| Author | SHA1 | Date |
|---|---|---|
|
|
0c481e4b8f | 6 years ago |
|
|
1227c5bd1d | 6 years ago |
40 changed files with 500 additions and 136 deletions
@ -1,8 +1,15 @@ |
|||
# tye application configuration file |
|||
# read all about it at https://github.com/dotnet/tye |
|||
name: frontend-backend |
|||
|
|||
ingress: |
|||
name: ingress |
|||
rules: |
|||
- service: frontend |
|||
|
|||
services: |
|||
- name: backend |
|||
project: backend/backend.csproj |
|||
- name: frontend |
|||
project: frontend/frontend.csproj |
|||
replicas: 2 |
|||
|
|||
@ -1,21 +0,0 @@ |
|||
apiVersion: extensions/v1beta1 |
|||
kind: Ingress |
|||
metadata: |
|||
name: ingress-basic |
|||
namespace: default |
|||
annotations: |
|||
kubernetes.io/ingress.class: nginx |
|||
nginx.ingress.kubernetes.io/ssl-redirect: "false" |
|||
nginx.ingress.kubernetes.io/rewrite-target: /$2 |
|||
spec: |
|||
rules: |
|||
- http: |
|||
paths: |
|||
- backend: |
|||
serviceName: vote |
|||
servicePort: 80 |
|||
path: /vote(/|$)(.*) |
|||
- backend: |
|||
serviceName: results |
|||
servicePort: 80 |
|||
path: /results(/|$)(.*) |
|||
@ -0,0 +1,18 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk.Web"> |
|||
|
|||
<PropertyGroup> |
|||
<OutputType>Exe</OutputType> |
|||
<TargetFramework>netcoreapp3.1</TargetFramework> |
|||
</PropertyGroup> |
|||
|
|||
<!-- Include *.deps.json and *.runtimeconfig.json in ContentWithTargetPath so they will be copied to the output folder of projects |
|||
that reference this one. --> |
|||
<Target Name="AddRuntimeDependenciesToContent" Condition=" '$(TargetFrameworkIdentifier)' == '.NETCoreApp'" BeforeTargets="GetCopyToOutputDirectoryItems"> |
|||
<ItemGroup> |
|||
<ContentWithTargetPath Include="$(ProjectDepsFilePath)" CopyToOutputDirectory="PreserveNewest" TargetPath="$(ProjectDepsFileName)" /> |
|||
|
|||
<ContentWithTargetPath Include="$(ProjectRuntimeConfigFilePath)" CopyToOutputDirectory="PreserveNewest" TargetPath="$(ProjectRuntimeConfigFileName)" /> |
|||
</ItemGroup> |
|||
</Target> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,26 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Hosting; |
|||
using Microsoft.Extensions.Configuration; |
|||
using Microsoft.Extensions.Hosting; |
|||
using Microsoft.Extensions.Logging; |
|||
|
|||
namespace Microsoft.Tye.HttpProxy |
|||
{ |
|||
public class Program |
|||
{ |
|||
public static void Main(string[] args) |
|||
{ |
|||
CreateHostBuilder(args).Build().Run(); |
|||
} |
|||
|
|||
public static IHostBuilder CreateHostBuilder(string[] args) => |
|||
Host.CreateDefaultBuilder(args) |
|||
.ConfigureWebHostDefaults(webBuilder => |
|||
{ |
|||
webBuilder.UseStartup<Startup>(); |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
{ |
|||
"iisSettings": { |
|||
"windowsAuthentication": false, |
|||
"anonymousAuthentication": true, |
|||
"iisExpress": { |
|||
"applicationUrl": "http://localhost:54967", |
|||
"sslPort": 44309 |
|||
} |
|||
}, |
|||
"profiles": { |
|||
"IIS Express": { |
|||
"commandName": "IISExpress", |
|||
"launchBrowser": true, |
|||
"environmentVariables": { |
|||
"ASPNETCORE_ENVIRONMENT": "Development" |
|||
} |
|||
}, |
|||
"Microsoft.Tye.HttpProxy": { |
|||
"commandName": "Project", |
|||
"launchBrowser": true, |
|||
"applicationUrl": "https://localhost:5001;http://localhost:5000", |
|||
"environmentVariables": { |
|||
"ASPNETCORE_ENVIRONMENT": "Development" |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,72 @@ |
|||
using System; |
|||
using System.Collections.Concurrent; |
|||
using System.Linq; |
|||
using System.Net; |
|||
using System.Net.Http; |
|||
using System.Net.Sockets; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.Logging; |
|||
|
|||
namespace Microsoft.Tye.HttpProxy |
|||
{ |
|||
public class RoundRobinLoadBalancer : DelegatingHandler |
|||
{ |
|||
private readonly ConcurrentDictionary<string, DnsCache> _dnsCache = new ConcurrentDictionary<string, DnsCache>(); |
|||
private readonly ILogger _logger; |
|||
public RoundRobinLoadBalancer(ILogger logger, HttpMessageHandler innerHandler) : base(innerHandler) |
|||
{ |
|||
_logger = logger; |
|||
} |
|||
|
|||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) |
|||
{ |
|||
var host = request.RequestUri.Host; |
|||
if (!_dnsCache.TryGetValue(host, out var cache)) |
|||
{ |
|||
var addresses = await Dns.GetHostAddressesAsync(host); |
|||
|
|||
_logger.LogInformation("Resolved {Host} to {Addresses}", host, addresses); |
|||
|
|||
cache = new DnsCache(addresses); |
|||
_dnsCache[host] = cache; |
|||
} |
|||
|
|||
// Allocations!
|
|||
request.RequestUri = new UriBuilder(request.RequestUri) |
|||
{ |
|||
Host = cache.GetAddress() |
|||
}.Uri; |
|||
|
|||
try |
|||
{ |
|||
return await base.SendAsync(request, cancellationToken); |
|||
} |
|||
catch (HttpRequestException ex) when (ex.InnerException is SocketException) |
|||
{ |
|||
// Connection error, remove this host (the target might have died)
|
|||
_dnsCache.TryRemove(host, out _); |
|||
|
|||
throw; |
|||
} |
|||
|
|||
} |
|||
|
|||
private class DnsCache |
|||
{ |
|||
private int _index; |
|||
private readonly string[] _addresses; |
|||
|
|||
public DnsCache(IPAddress[] addresses) |
|||
{ |
|||
_addresses = addresses.Select(a => a.ToString()).ToArray(); |
|||
} |
|||
|
|||
public string GetAddress() |
|||
{ |
|||
var next = Interlocked.Increment(ref _index) % _addresses.Length; |
|||
return _addresses[next]; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,83 @@ |
|||
using System; |
|||
using System.Net; |
|||
using System.Net.Http; |
|||
using Microsoft.AspNetCore.Builder; |
|||
using Microsoft.AspNetCore.Hosting; |
|||
using Microsoft.AspNetCore.Http; |
|||
using Microsoft.AspNetCore.Proxy; |
|||
using Microsoft.AspNetCore.Routing; |
|||
using Microsoft.AspNetCore.Routing.Matching; |
|||
using Microsoft.Extensions.Configuration; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Hosting; |
|||
using Microsoft.Extensions.Logging; |
|||
|
|||
namespace Microsoft.Tye.HttpProxy |
|||
{ |
|||
public class Startup |
|||
{ |
|||
public void ConfigureServices(IServiceCollection services) |
|||
{ |
|||
services.AddSingleton<MatcherPolicy, IngressHostMatcherPolicy>(); |
|||
} |
|||
|
|||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
|||
public void Configure(IApplicationBuilder app, ILogger<Startup> logger, IWebHostEnvironment env, IConfiguration configuration) |
|||
{ |
|||
var invoker = new HttpMessageInvoker(new ConnectionRetryHandler(new RoundRobinLoadBalancer(logger, new SocketsHttpHandler |
|||
{ |
|||
AllowAutoRedirect = false, |
|||
AutomaticDecompression = DecompressionMethods.None, |
|||
UseProxy = false |
|||
}))); |
|||
|
|||
if (env.IsDevelopment()) |
|||
{ |
|||
app.UseDeveloperExceptionPage(); |
|||
} |
|||
|
|||
app.UseRouting(); |
|||
|
|||
app.UseEndpoints(endpoints => |
|||
{ |
|||
foreach (var rule in configuration.GetSection("Rules").GetChildren()) |
|||
{ |
|||
var host = rule["Host"]; |
|||
var path = rule["Path"]; |
|||
var preservePath = rule.GetSection("PreservePath").Get<bool>(); |
|||
var service = rule["Service"]; |
|||
var port = rule["Port"]; |
|||
var protocol = rule["Protocol"]; |
|||
|
|||
var url = $"{protocol}://{service}:{port}"; |
|||
|
|||
RequestDelegate del = context => |
|||
{ |
|||
var uri = new UriBuilder(url) |
|||
{ |
|||
Path = preservePath ? context.Request.Path.ToString() : (string)context.Request.RouteValues["path"] ?? "/" |
|||
}; |
|||
|
|||
return context.ProxyRequest(invoker, uri.Uri); |
|||
}; |
|||
|
|||
IEndpointConventionBuilder conventions = null!; |
|||
|
|||
if (path != null) |
|||
{ |
|||
conventions = endpoints.Map(path.TrimEnd('/') + "/{**path}", del); |
|||
} |
|||
else |
|||
{ |
|||
conventions = endpoints.MapFallback(del); |
|||
} |
|||
|
|||
if (host != null) |
|||
{ |
|||
conventions.WithMetadata(new IngressHostMetadata(host)); |
|||
} |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
{ |
|||
"Logging": { |
|||
"LogLevel": { |
|||
"Default": "Information", |
|||
"Microsoft": "Information", |
|||
"Microsoft.Hosting.Lifetime": "Information" |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
{ |
|||
"Logging": { |
|||
"LogLevel": { |
|||
"Default": "Information", |
|||
"Microsoft": "Information", |
|||
"Microsoft.Hosting.Lifetime": "Information" |
|||
} |
|||
}, |
|||
"AllowedHosts": "*" |
|||
} |
|||
Loading…
Reference in new issue