/
Program.cs
150 lines (134 loc) · 5.89 KB
/
Program.cs
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
namespace rhino.compute
{
using System;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using CommandLine;
using Serilog;
using Serilog.Events;
using Microsoft.Extensions.Configuration;
using System.IO;
public class Program
{
/// <summary>
/// Command line options for rhino.compute.exe. An example of the syntax is
/// rhino.compute.exe --childcount 8
/// This would launch rhino.compute with 8 child compute.geometry.exe processes
/// </summary>
class Options
{
[Option("childof",
Required = false,
HelpText = @"Process Handle of parent process. Compute watches for the existence
of this handle and will shut down when this process has exited")]
public int ChildOf { get; set; }
[Option("childcount",
Required = false,
HelpText = "Number of child compute.geometry processes to manage")]
public int ChildCount { get; set; } = 4;
[Option("spawn-on-startup",
Required = false,
Default = false,
HelpText = "Determines whether to launch a child compute.geometry process when rhino.compute gets started")]
public bool SpawnOnStartup { get; set; }
[Option("idlespan",
Required = false,
HelpText =
@"Seconds that child compute.geometry processes should remain open between requests. (Default 1 hour)
When rhino.compute.exe does not receive requests to solve over a period of 'idlespan' seconds, child
compute.geometry.exe processes will shut down and stop incurring core hour billing. At some date in the
future when a new request is received, the child processes will be relaunched which will cause a delay on
requests while the child processes are launching.")]
public int IdleSpanSeconds { get; set; } = 60 * 60;
[Option("port",
Required = false,
HelpText = "Port number to run rhino.compute on")]
public int Port { get; set; } = -1;
}
static System.Diagnostics.Process _parentProcess;
static System.Timers.Timer _selfDestructTimer;
public static IConfiguration Configuration { get; } = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
public static void Main(string[] args)
{
Config.Load();
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(Configuration)
.Filter.ByExcluding("RequestPath in ['/healthcheck', '/favicon.ico']")
.Enrich.FromLogContext()
.WriteTo.Console()
.CreateLogger();
int port = -1;
Parser.Default.ParseArguments<Options>(args).WithParsed(o =>
{
ComputeChildren.SpawnCount = o.ChildCount;
ComputeChildren.SpawnOnStartup = o.SpawnOnStartup;
ComputeChildren.ChildIdleSpan = new System.TimeSpan(0, 0, o.IdleSpanSeconds);
int parentProcessId = o.ChildOf;
if (parentProcessId > 0)
_parentProcess = System.Diagnostics.Process.GetProcessById(parentProcessId);
port = o.Port;
});
var host = Host.CreateDefaultBuilder(args)
.UseSerilog()
.ConfigureWebHostDefaults(webBuilder =>
{
var b = webBuilder.ConfigureKestrel((context, options) =>
{
// Handle requests up to 50 MB
options.Limits.MaxRequestBodySize = 52428800;
})
.UseIISIntegration()
.UseStartup<Startup>()
.CaptureStartupErrors(true);
if (port > 0)
{
b.UseUrls($"http://localhost:{port}");
ComputeChildren.ParentPort = port;
}
}).Build();
if(_parentProcess?.MainModule != null)
{
var parentPath = _parentProcess.MainModule.FileName;
if (Path.GetFileName(parentPath) == "Rhino.exe")
{
ComputeChildren.RhinoSysDir = Directory.GetParent(parentPath).FullName;
}
}
Log.Information($"Rhino compute started at {DateTime.Now.ToLocalTime()}");
var logger = host.Services.GetRequiredService<ILogger<ReverseProxyModule>>();
ReverseProxyModule.InitializeConcurrentRequestLogging(logger);
if (_parentProcess != null)
{
_selfDestructTimer = new System.Timers.Timer(1000);
_selfDestructTimer.Elapsed += (s, e) =>
{
if (_parentProcess.HasExited)
{
_selfDestructTimer.Stop();
_parentProcess = null;
Console.WriteLine("self-destruct");
Log.Information($"Self-destruct called at {DateTime.Now.ToLocalTime()}");
host.StopAsync();
}
};
_selfDestructTimer.AutoReset = true;
_selfDestructTimer.Start();
}
host.Run();
}
public static bool IsParentRhinoProcess(int processId)
{
if (_parentProcess != null && _parentProcess.ProcessName.Contains("rhino", StringComparison.OrdinalIgnoreCase))
{
return (_parentProcess.Id == processId);
}
return false;
}
}
}