Skip to content

Commit ab1c73f

Browse files
committed
Add support for SharedFile service registration without the need to run third-party middleware
1 parent a693663 commit ab1c73f

10 files changed

Lines changed: 976 additions & 33 deletions

File tree

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
using Microsoft.Extensions.Configuration;
2+
using Microsoft.Extensions.DependencyInjection;
3+
using Microsoft.Extensions.Logging;
4+
using Surging.Core.CPlatform.Cache;
5+
using Surging.Core.CPlatform.Module;
6+
using Surging.Core.CPlatform.Mqtt;
7+
using Surging.Core.CPlatform.Runtime.Client.HealthChecks.Implementation;
8+
using Surging.Core.CPlatform.Runtime.Client.HealthChecks;
9+
using Surging.Core.CPlatform.Runtime.Client;
10+
using Surging.Core.CPlatform.Runtime.Server;
11+
using Surging.Core.CPlatform.Serialization;
12+
using Surging.Core.CPlatform.Support;
13+
using System;
14+
using System.Collections.Generic;
15+
using System.Linq;
16+
using System.Text;
17+
using System.Threading.Tasks;
18+
19+
namespace Surging.Core.CPlatform.Routing.Implementation
20+
{
21+
public class SharedFileModule : EnginePartModule
22+
{
23+
public override void Initialize(AppModuleContext context)
24+
{
25+
base.Initialize(context);
26+
}
27+
28+
/// <summary>
29+
/// Inject dependent third-party components
30+
/// </summary>
31+
/// <param name="builder"></param>
32+
protected override void RegisterBuilder(ContainerBuilderWrapper builder)
33+
{
34+
35+
base.RegisterBuilder(builder);
36+
var rootpath = AppConfig.ServerOptions.RootPath;
37+
if (string.IsNullOrEmpty(rootpath))
38+
rootpath = AppContext.BaseDirectory;
39+
UseSharedFileRouteManager(builder,System.IO.Path.Combine(rootpath, "reg", $"reg_route.jreg"))
40+
.UseSharedFileCommandManager(builder, System.IO.Path.Combine(rootpath, "reg", $"reg_command.jreg"))
41+
.UseSharedFileCacheManager(builder, System.IO.Path.Combine(rootpath, "reg", $"reg_cahce.jreg"))
42+
.UseSharedFileMqttRouteManager(builder, System.IO.Path.Combine(rootpath, "reg", $"reg_mqtt.jreg"));
43+
}
44+
45+
public SharedFileModule UseSharedFileRouteManager(ContainerBuilderWrapper builder, string filePath)
46+
{
47+
UseRouteManager(builder, provider =>
48+
new SharedFileServiceRouteManager(
49+
filePath,
50+
provider.GetRequiredService<ISerializer<string>>(),
51+
provider.GetRequiredService<IServiceRouteFactory>(),
52+
provider.GetRequiredService<ILogger<SharedFileServiceRouteManager>>()));
53+
return this;
54+
}
55+
56+
public SharedFileModule UseSharedFileCommandManager(ContainerBuilderWrapper builder, string filePath)
57+
{
58+
UseCommandManager(builder,provider =>
59+
new SharedFileServiceCommandManager(
60+
filePath,
61+
provider.GetRequiredService<ISerializer<string>>(),
62+
provider.GetRequiredService<IServiceEntryManager>(),
63+
provider.GetRequiredService<ILogger<SharedFileServiceCommandManager>>()));
64+
return this;
65+
}
66+
67+
public SharedFileModule UseSharedFileCacheManager(ContainerBuilderWrapper builder, string filePath)
68+
{
69+
UseCacheManager(builder,provider =>
70+
new SharedFileServiceCacheManager(
71+
filePath,
72+
provider.GetRequiredService<ISerializer<string>>(),
73+
provider.GetRequiredService<IServiceCacheFactory>(),
74+
provider.GetRequiredService<ILogger<SharedFileServiceCacheManager>>()));
75+
return this;
76+
}
77+
78+
public SharedFileModule UseSharedFileMqttRouteManager(ContainerBuilderWrapper builder, string filePath)
79+
{
80+
UseMqttRouteManager(builder,provider =>
81+
new SharedFileMqttServiceRouteManager(
82+
filePath,
83+
provider.GetRequiredService<ISerializer<string>>(),
84+
provider.GetRequiredService<IMqttServiceFactory>(),
85+
provider.GetRequiredService<ILogger<SharedFileMqttServiceRouteManager>>()));
86+
return this;
87+
}
88+
89+
public ContainerBuilderWrapper UseCommandManager(ContainerBuilderWrapper builder, Func<IServiceProvider, IServiceCommandManager> factory)
90+
{
91+
builder.RegisterAdapter(factory).InstancePerLifetimeScope();
92+
return builder;
93+
}
94+
95+
public ContainerBuilderWrapper UseCacheManager(ContainerBuilderWrapper builder, Func<IServiceProvider, IServiceCacheManager> factory)
96+
{
97+
builder.RegisterAdapter(factory).InstancePerLifetimeScope();
98+
return builder;
99+
}
100+
101+
public ContainerBuilderWrapper UseRouteManager(ContainerBuilderWrapper builder, Func<IServiceProvider, IServiceRouteManager> factory)
102+
{
103+
builder.RegisterAdapter(factory).InstancePerLifetimeScope();
104+
return builder;
105+
}
106+
107+
public ContainerBuilderWrapper UseMqttRouteManager(ContainerBuilderWrapper builder, Func<IServiceProvider, IMqttServiceRouteManager> factory)
108+
{
109+
builder.RegisterAdapter(factory).InstancePerLifetimeScope();
110+
return builder;
111+
}
112+
113+
}
114+
}
115+
Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
using Microsoft.Extensions.Logging;
2+
using Surging.Core.CPlatform.Address;
3+
using Surging.Core.CPlatform.Mqtt;
4+
using Surging.Core.CPlatform.Mqtt.Implementation;
5+
using Surging.Core.CPlatform.Serialization;
6+
using System;
7+
using System.Collections.Generic;
8+
using System.IO;
9+
using System.Linq;
10+
using System.Text;
11+
using System.Threading.Tasks;
12+
13+
namespace Surging.Core.CPlatform.Routing.Implementation
14+
{
15+
public class SharedFileMqttServiceRouteManager : MqttServiceRouteManagerBase, IDisposable
16+
{
17+
#region Field
18+
19+
private readonly string _filePath;
20+
private readonly ISerializer<string> _serializer;
21+
private readonly IMqttServiceFactory _mqttServiceFactory;
22+
private readonly ILogger<SharedFileMqttServiceRouteManager> _logger;
23+
private MqttServiceRoute[] _routes;
24+
private readonly FileSystemWatcher _fileSystemWatcher;
25+
26+
#endregion Field
27+
28+
#region Constructor
29+
30+
public SharedFileMqttServiceRouteManager(string filePath, ISerializer<string> serializer,
31+
IMqttServiceFactory mqttServiceFactory, ILogger<SharedFileMqttServiceRouteManager> logger) : base(serializer)
32+
{
33+
_filePath = filePath;
34+
_serializer = serializer;
35+
_mqttServiceFactory = mqttServiceFactory;
36+
_logger = logger;
37+
38+
var directoryName = Path.GetDirectoryName(filePath);
39+
if (!Directory.Exists(directoryName))
40+
Directory.CreateDirectory(directoryName);
41+
if (!File.Exists(filePath)) File.Create(filePath).Close();
42+
_fileSystemWatcher = new FileSystemWatcher(directoryName, Path.GetFileName(filePath));
43+
44+
_fileSystemWatcher.Changed += _fileSystemWatcher_Changed;
45+
_fileSystemWatcher.Created += _fileSystemWatcher_Changed;
46+
_fileSystemWatcher.Deleted += _fileSystemWatcher_Changed;
47+
_fileSystemWatcher.Renamed += _fileSystemWatcher_Changed;
48+
_fileSystemWatcher.IncludeSubdirectories = false;
49+
_fileSystemWatcher.EnableRaisingEvents = true;
50+
}
51+
52+
#endregion Constructor
53+
54+
#region Implementation of IDisposable
55+
56+
/// <summary>
57+
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
58+
/// </summary>
59+
public void Dispose()
60+
{
61+
_fileSystemWatcher?.Dispose();
62+
}
63+
64+
#endregion Implementation of IDisposable
65+
66+
#region Overrides of ServiceRouteManagerBase
67+
68+
/// <summary>
69+
/// 获取所有可用的服务路由信息。
70+
/// </summary>
71+
/// <returns>服务路由集合。</returns>
72+
public override async Task<IEnumerable<MqttServiceRoute>> GetRoutesAsync()
73+
{
74+
if (_routes == null)
75+
await EntryRoutes(_filePath);
76+
return _routes;
77+
}
78+
79+
80+
/// <summary>
81+
/// 清空所有的服务路由。
82+
/// </summary>
83+
/// <returns>一个任务。</returns>
84+
public override Task ClearAsync()
85+
{
86+
if (File.Exists(_filePath))
87+
File.Delete(_filePath);
88+
return Task.FromResult(0);
89+
}
90+
91+
/// <summary>
92+
/// 设置服务路由。
93+
/// </summary>
94+
/// <param name="routes">服务路由集合。</param>
95+
/// <returns>一个任务。</returns>
96+
protected override async Task SetRoutesAsync(IEnumerable<MqttServiceDescriptor> routes)
97+
{
98+
using (var fileStream = new FileStream(_filePath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None))
99+
{
100+
fileStream.SetLength(0);
101+
using (var writer = new StreamWriter(fileStream, Encoding.UTF8))
102+
{
103+
await writer.WriteAsync(_serializer.Serialize(routes));
104+
}
105+
}
106+
}
107+
108+
public override async Task RemveAddressAsync(IEnumerable<AddressModel> Address)
109+
{
110+
var routes = await GetRoutesAsync();
111+
foreach (var route in routes)
112+
{
113+
route.MqttEndpoint = route.MqttEndpoint.Except(Address);
114+
}
115+
await base.SetRoutesAsync(routes);
116+
}
117+
118+
#endregion Overrides of ServiceRouteManagerBase
119+
120+
#region Private Method
121+
122+
private async Task<IEnumerable<MqttServiceRoute>> GetRoutes(string file)
123+
{
124+
MqttServiceRoute[] routes;
125+
if (File.Exists(file))
126+
{
127+
if (_logger.IsEnabled(LogLevel.Debug))
128+
_logger.LogDebug($"准备从文件:{file}中获取服务路由。");
129+
string content;
130+
while (true)
131+
{
132+
try
133+
{
134+
using (
135+
var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
136+
{
137+
var reader = new StreamReader(fileStream, Encoding.UTF8);
138+
content = await reader.ReadToEndAsync();
139+
}
140+
break;
141+
}
142+
catch (IOException)
143+
{
144+
}
145+
}
146+
try
147+
{
148+
var serializer = _serializer;
149+
routes =
150+
(await
151+
_mqttServiceFactory.CreateMqttServiceRoutesAsync(
152+
serializer.Deserialize<string, MqttServiceDescriptor[]>(content))).ToArray();
153+
if (_logger.IsEnabled(LogLevel.Information))
154+
_logger.LogInformation(
155+
$"成功获取到以下路由信息:{string.Join(",", routes.Select(i => i.MqttDescriptor.Topic))}。");
156+
}
157+
catch (Exception exception)
158+
{
159+
if (_logger.IsEnabled(LogLevel.Error))
160+
_logger.LogError(exception, "获取路由信息时发生了错误。");
161+
routes = new MqttServiceRoute[0];
162+
}
163+
}
164+
else
165+
{
166+
if (_logger.IsEnabled(LogLevel.Warning))
167+
_logger.LogWarning($"无法获取路由信息,因为文件:{file}不存在。");
168+
routes = new MqttServiceRoute[0];
169+
}
170+
return routes;
171+
}
172+
173+
private async Task EntryRoutes(string file)
174+
{
175+
var oldRoutes = _routes?.ToArray();
176+
var newRoutes = (await GetRoutes(file)).ToArray();
177+
_routes = newRoutes;
178+
if (oldRoutes == null)
179+
{
180+
//触发服务路由创建事件。
181+
OnCreated(newRoutes.Select(route => new MqttServiceRouteEventArgs(route)).ToArray());
182+
}
183+
else
184+
{
185+
//旧的服务Id集合。
186+
var oldServiceIds = oldRoutes.Select(i => i.MqttDescriptor.Topic).ToArray();
187+
//新的服务Id集合。
188+
var newServiceIds = newRoutes.Select(i => i.MqttDescriptor.Topic).ToArray();
189+
190+
//被删除的服务Id集合
191+
var removeServiceIds = oldServiceIds.Except(newServiceIds).ToArray();
192+
//新增的服务Id集合。
193+
var addServiceIds = newServiceIds.Except(oldServiceIds).ToArray();
194+
//可能被修改的服务Id集合。
195+
var mayModifyServiceIds = newServiceIds.Except(removeServiceIds).ToArray();
196+
197+
//触发服务路由创建事件。
198+
OnCreated(
199+
newRoutes.Where(i => addServiceIds.Contains(i.MqttDescriptor.Topic))
200+
.Select(route => new MqttServiceRouteEventArgs(route))
201+
.ToArray());
202+
203+
//触发服务路由删除事件。
204+
OnRemoved(
205+
oldRoutes.Where(i => removeServiceIds.Contains(i.MqttDescriptor.Topic))
206+
.Select(route => new MqttServiceRouteEventArgs(route))
207+
.ToArray());
208+
209+
//触发服务路由变更事件。
210+
var currentMayModifyRoutes =
211+
newRoutes.Where(i => mayModifyServiceIds.Contains(i.MqttDescriptor.Topic)).ToArray();
212+
var oldMayModifyRoutes =
213+
oldRoutes.Where(i => mayModifyServiceIds.Contains(i.MqttDescriptor.Topic)).ToArray();
214+
215+
foreach (var oldMayModifyRoute in oldMayModifyRoutes)
216+
{
217+
if (!currentMayModifyRoutes.Contains(oldMayModifyRoute))
218+
OnChanged(
219+
new MqttServiceRouteChangedEventArgs(
220+
currentMayModifyRoutes.First(
221+
i => i.MqttDescriptor.Topic == oldMayModifyRoute.MqttDescriptor.Topic),
222+
oldMayModifyRoute));
223+
}
224+
}
225+
}
226+
227+
private async void _fileSystemWatcher_Changed(object sender, FileSystemEventArgs e)
228+
{
229+
if (_logger.IsEnabled(LogLevel.Information))
230+
_logger.LogInformation($"文件{_filePath}发生了变更,将重新获取路由信息。");
231+
232+
if (e.ChangeType == WatcherChangeTypes.Changed)
233+
{
234+
string content;
235+
try
236+
{
237+
content = File.ReadAllText(_filePath, Encoding.UTF8);
238+
}
239+
catch (IOException) //还没有操作完,忽略本次修改
240+
{
241+
return;
242+
}
243+
if (!string.IsNullOrWhiteSpace(content))
244+
{
245+
await EntryRoutes(_filePath);
246+
}
247+
else
248+
{
249+
return;
250+
}
251+
}
252+
253+
await EntryRoutes(_filePath);
254+
}
255+
256+
public override async Task RemoveByTopicAsync(string topic, IEnumerable<AddressModel> endpoint)
257+
{
258+
var routes = await GetRoutesAsync();
259+
try
260+
{
261+
var route = routes.Where(p => p.MqttDescriptor.Topic == topic).SingleOrDefault();
262+
if (route != null)
263+
{
264+
route.MqttEndpoint = route.MqttEndpoint.Except(endpoint);
265+
await base.SetRoutesAsync(new MqttServiceRoute[] { route });
266+
}
267+
}
268+
catch (Exception ex)
269+
{
270+
throw ex;
271+
}
272+
}
273+
274+
275+
276+
#endregion Private Method
277+
}
278+
}

0 commit comments

Comments
 (0)