-
Notifications
You must be signed in to change notification settings - Fork 2k
/
ServiceRuntimeWrapper.cs
235 lines (200 loc) · 9.43 KB
/
ServiceRuntimeWrapper.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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Reflection;
using Microsoft.Extensions.Logging;
using Orleans.Streams;
namespace Orleans.Runtime.Host
{
/// <summary>
/// Interface exposed by ServiceRuntimeWrapper for functionality provided
/// by Microsoft.WindowsAzure.ServiceRuntime.
/// </summary>
public interface IServiceRuntimeWrapper
{
/// <summary>
/// Deployment ID of the hosted service
/// </summary>
string DeploymentId { get; }
/// <summary>
/// Name of the role instance
/// </summary>
string InstanceName { get; }
/// <summary>
/// Name of the worker/web role
/// </summary>
string RoleName { get; }
/// <summary>
/// Update domain of the role instance
/// </summary>
int UpdateDomain { get; }
/// <summary>
/// Fault domain of the role instance
/// </summary>
int FaultDomain { get; }
/// <summary>
/// Number of instances in the worker/web role
/// </summary>
int RoleInstanceCount { get; }
/// <summary>
/// Returns IP endpoint by name
/// </summary>
/// <param name="endpointName">Name of the IP endpoint</param>
/// <returns></returns>
IPEndPoint GetIPEndpoint(string endpointName);
/// <summary>
/// Returns value of the given configuration setting
/// </summary>
/// <param name="configurationSettingName"></param>
/// <returns></returns>
string GetConfigurationSettingValue(string configurationSettingName);
/// <summary>
/// Subscribes given even handler for role instance Stopping event
/// </summary>
/// /// <param name="handlerObject">Object that handler is part of, or null for a static method</param>
/// <param name="handler">Handler to subscribe</param>
void SubscribeForStoppingNotification(object handlerObject, EventHandler<object> handler);
/// <summary>
/// Unsubscribes given even handler from role instance Stopping event
/// </summary>
/// /// <param name="handlerObject">Object that handler is part of, or null for a static method</param>
/// <param name="handler">Handler to unsubscribe</param>
void UnsubscribeFromStoppingNotification(object handlerObject, EventHandler<object> handler);
}
/// <summary>
/// The purpose of this class is to wrap the functionality provided
/// by Microsoft.WindowsAzure.ServiceRuntime.dll, so that we can access it via Reflection,
/// and not have a compile-time dependency on it.
/// Microsoft.WindowsAzure.ServiceRuntime.dll doesn't have an official NuGet package.
/// By loading it via Reflection we solve this problem, and do not need an assembly
/// binding redirect for it, as we can call any compatible version.
/// Microsoft.WindowsAzure.ServiceRuntime.dll hasn't changed in years, so the chance of a breaking change
/// is relatively low.
/// </summary>
internal class ServiceRuntimeWrapper : IServiceRuntimeWrapper, IDeploymentConfiguration
{
private readonly ILogger logger;
private Assembly assembly;
private Type roleEnvironmentType;
private EventInfo stoppingEvent;
private MethodInfo stoppingEventAdd;
private MethodInfo stoppingEventRemove;
private Type roleInstanceType;
private dynamic currentRoleInstance;
private dynamic instanceEndpoints;
private dynamic role;
public ServiceRuntimeWrapper(ILoggerFactory loggerFactory)
{
logger = loggerFactory.CreateLogger<ServiceRuntimeWrapper>();
Initialize();
}
public string DeploymentId { get; private set; }
public string InstanceId { get; private set; }
public string RoleName { get; private set; }
public int UpdateDomain { get; private set; }
public int FaultDomain { get; private set; }
public string InstanceName
{
get { return ExtractInstanceName(InstanceId, DeploymentId); }
}
public int RoleInstanceCount
{
get
{
dynamic instances = role.Instances;
return instances.Count;
}
}
public IList<string> GetAllSiloNames()
{
dynamic instances = role.Instances;
var list = new List<string>();
foreach(dynamic instance in instances)
list.Add(ExtractInstanceName(instance.Id,DeploymentId));
return list;
}
public IPEndPoint GetIPEndpoint(string endpointName)
{
try
{
dynamic ep = instanceEndpoints.GetType()
.GetProperty("Item")
.GetMethod.Invoke(instanceEndpoints, new object[] {endpointName});
return ep.IPEndpoint;
}
catch (Exception exc)
{
var endpointNames = (string)string.Join(", ", instanceEndpoints);
logger.LogError(
(int)ErrorCode.SiloEndpointConfigError,
exc,
"Unable to obtain endpoint info for role {RoleName} from role config parameter {EndpointName} -- Endpoints defined = [{EndpointNames}]",
RoleName,
endpointName,
endpointNames);
throw new OrleansException(
$"Unable to obtain endpoint info for role {RoleName} from role config parameter {endpointName} -- Endpoints defined = [{endpointNames}]",
exc);
}
}
public string GetConfigurationSettingValue(string configurationSettingName)
{
return (string) roleEnvironmentType.GetMethod("GetConfigurationSettingValue").Invoke(null, new object[] {configurationSettingName});
}
public void SubscribeForStoppingNotification(object handlerObject, EventHandler<object> handler)
{
var handlerDelegate = handler.GetMethodInfo().CreateDelegate(stoppingEvent.EventHandlerType, handlerObject);
stoppingEventAdd.Invoke(null, new object[] { handlerDelegate });
}
public void UnsubscribeFromStoppingNotification(object handlerObject, EventHandler<object> handler)
{
var handlerDelegate = handler.GetMethodInfo().CreateDelegate(stoppingEvent.EventHandlerType, handlerObject);
stoppingEventRemove.Invoke(null, new[] { handlerDelegate });
}
private void Initialize()
{
assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(
a => a.FullName.StartsWith("Microsoft.WindowsAzure.ServiceRuntime", StringComparison.Ordinal));
// If we are runing within a worker role Microsoft.WindowsAzure.ServiceRuntime should already be loaded
if (assembly == null)
{
const string msg1 = "Microsoft.WindowsAzure.ServiceRuntime is not loaded. Trying to load it with Assembly.LoadWithPartialName().";
logger.LogWarning((int)ErrorCode.AzureServiceRuntime_NotLoaded, msg1);
// Microsoft.WindowsAzure.ServiceRuntime isn't loaded. We may be running within a web role or not in Azure.
#pragma warning disable 618
assembly = Assembly.Load(new AssemblyName("Microsoft.WindowsAzure.ServiceRuntime, Version = 2.7.0.0, Culture = neutral, PublicKeyToken = 31bf3856ad364e35"));
#pragma warning restore 618
if (assembly == null)
{
const string msg2 = "Failed to find or load Microsoft.WindowsAzure.ServiceRuntime.";
logger.LogError((int)ErrorCode.AzureServiceRuntime_FailedToLoad, msg2);
throw new OrleansException(msg2);
}
}
roleEnvironmentType = assembly.GetType("Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment");
stoppingEvent = roleEnvironmentType.GetEvent("Stopping");
stoppingEventAdd = stoppingEvent.GetAddMethod();
stoppingEventRemove = stoppingEvent.GetRemoveMethod();
roleInstanceType = assembly.GetType("Microsoft.WindowsAzure.ServiceRuntime.RoleInstance");
DeploymentId = (string) roleEnvironmentType.GetProperty("DeploymentId").GetValue(null);
if (string.IsNullOrWhiteSpace(DeploymentId))
throw new OrleansException("DeploymentId is null or whitespace.");
currentRoleInstance = roleEnvironmentType.GetProperty("CurrentRoleInstance").GetValue(null);
if (currentRoleInstance == null)
throw new OrleansException("CurrentRoleInstance is null.");
InstanceId = currentRoleInstance.Id;
UpdateDomain = currentRoleInstance.UpdateDomain;
FaultDomain = currentRoleInstance.FaultDomain;
instanceEndpoints = currentRoleInstance.InstanceEndpoints;
role = currentRoleInstance.Role;
RoleName = role.Name;
}
private static string ExtractInstanceName(string instanceId, string deploymentId)
{
return instanceId.Length > deploymentId.Length && instanceId.StartsWith(deploymentId, StringComparison.Ordinal)
? instanceId[(deploymentId.Length + 1)..]
: instanceId;
}
}
}