Отправка сообщений между приложениями с помощью Azure Signal R в режиме по умолчаниюC#

Место общения программистов C#
Ответить
Anonymous
 Отправка сообщений между приложениями с помощью Azure Signal R в режиме по умолчанию

Сообщение Anonymous »

У меня есть клиентское приложение (Blazor, двойной сервер/проект Wasm, .NET 9.0), которое подключено к Azure Signal R и имеет определенный концентратор. В рамках этого приложения, используя страницу тестирования, я могу отправлять и получать сообщения через SignalR.
Это приложение использует Azure Signal R, работающее в режиме по умолчанию.
Я хочу, чтобы другое приложение отправляло сообщения клиенту blazor с помощью Azure Signal R. Теоретически это приложение должно быть чем угодно, но на практике оно будет из устойчивой функции Azure (функции 4.0, .Net 9, Изолированный режим), то есть длительный процесс, который по завершении отправит сообщение.
Для этого кода/вопроса я использую простое тестовое сообщение только для проверки подключения.
Моя проблема в том, что когда я отправляю сообщения через внешнее приложение, я не получаю никаких ошибок, а клиент не получает сообщений.
Настройка клиента
Program.cs

Код: Выделить всё

var signalRConnectionString = configuration.GetValue(ConfigurationConstants.ConfigurationKeys.SignalR.SignalRConnectionString);

services.AddSignalR(options =>
{
options.MaximumReceiveMessageSize = maximumReceiveMessageSize;
options.EnableDetailedErrors      = true;
})
.AddAzureSignalR(options =>
{
options.ConnectionString  = signalRConnectionString;
});

...

app.MapHub($"/{SignalRConstants.Hubs.Messages.HubName}");

MessagesHub

Код: Выделить всё

public interface IMessagesHub
{
Task ConnectedAsync();

Task DisconnectedAsync(Exception? exception = null);

Task ReceiveTestMessage(string name, string message);
}

public class MessagesHub : Hub
{
public override async Task OnConnectedAsync()
{
await Clients.Caller.ConnectedAsync();

await base.OnConnectedAsync();
}

public override async Task OnDisconnectedAsync(Exception? exception)
{
await Clients.Caller.DisconnectedAsync(exception);

await base.OnDisconnectedAsync(exception);
}

public async Task SendTestMessage(string name, string message)
{
Console.WriteLine($"### - [{Context.ConnectionId}] Send Test Message [{name}]: {message}");
await Clients.All.ReceiveTestMessage(name, message);
}
}

MessagesHubService

Код: Выделить всё

public interface IMessagesHubService : IAsyncDisposable
{
bool IsConnected { get; }
string ConnectionId { get; }

Task BeginMonitoringAsync(CancellationToken cancellationToken = default);
Task StopMonitoringAsync(CancellationToken cancellationToken = default);
Task SendTestMessage(string name, string message);
}

public class MessagesHubService(IConfigurationService configService,
NavigationManager navigationManager,
ISignalRHubConnectionFactory hubConnectionFactory,
IMessagesCallbackService callbackService,
ILoggerFactory loggerFactory)
: IMessagesHubService
{
private static HubConnection? _connection;

private readonly ILogger _logger = loggerFactory.CreateLogger();

public bool IsConnected => (_connection?.State ?? HubConnectionState.Disconnected) == HubConnectionState.Connected;
public string ConnectionId => _connection?.ConnectionId ?? "Not connected";

private async Task  InitializeSignalRAsync()
{
if (_connection != null)
{
return _connection;
}

var hubRoute = SignalRConstants.Hubs.Messages.HubRoute;

if (await configService.IsFeatureFlagEnabledAsync(ConfigurationConstants.FeatureFlags.UseAzureSignalR))
{
hubRoute = $"/{SignalRConstants.Hubs.Messages.HubName}";
}

var hubConnectionUrl = navigationManager.ToAbsoluteUri(hubRoute);

_connection = await hubConnectionFactory.CreateHubConnectionAsync(hubConnectionUrl);

InitializeEvents(_connection);

return _connection;
}

private void InitializeEvents(HubConnection connection)
{
connection.On(SignalRConstants.Hubs.Messages.Methods.Connected, OnConnectedAsync);
connection.On(SignalRConstants.Hubs.Messages.Methods.Disconnected, OnDisconnectedAsync);

connection.On(SignalRConstants.Hubs.Messages.Methods.TestMessage, TestMessageReceivedAsync);
}

private async Task OnConnectedAsync()
{
await callbackService.ConnectedAsync();
}

private async Task OnDisconnectedAsync(Exception? exception = null)
{
await callbackService.DisconnectedAsync(exception);
}

private async Task TestMessageReceivedAsync(string name, string message)
{
await callbackService.ReceiveTestMessageAsync(name, message);
}

public async Task BeginMonitoringAsync(CancellationToken cancellationToken = default)
{
var connection = await InitializeSignalRAsync();

if (connection == null)
{
return false;
}

if (connection.State == HubConnectionState.Connected)
{
return true;
}

if (connection.State == HubConnectionState.Disconnected)
{
try
{
await connection.StartAsync(cancellationToken);
return true;
}
catch (Exception e)
{
_logger.WriteException(e, "Error while starting Signal R Service");
}
}

return false;
}

public async Task StopMonitoringAsync(CancellationToken cancellationToken = default)
{
var connection = await InitializeSignalRAsync();

if (connection == null)
{
return;
}

if (connection.State != HubConnectionState.Disconnected)
{
await connection.StopAsync(cancellationToken);
}
}

public async Task SendTestMessage(string name, string message)
{
var connection = await InitializeSignalRAsync();

if (connection == null)
{
return;
}

if (connection.State != HubConnectionState.Connected)
{
await BeginMonitoringAsync();
}

await connection.InvokeAsync(SignalRConstants.Hubs.Messages.Methods.Testing.SendTestMessage, name, message);
}

public async ValueTask DisposeAsync()
{
if (_connection != null)
{
await _connection.DisposeAsync();
_connection = null;
}
}
}

SignalRHubConnectionFactory

Код: Выделить всё

public class SignalRHubConnectionFactory(IConfigurationService configService,
ILoggerFactory loggerFactory) : ISignalRHubConnectionFactory
{
private readonly ILogger _logger = loggerFactory.CreateLogger();

public async Task  CreateHubConnectionAsync(Uri hubRoute,
CancellationToken cancellationToken = default)
{
try
{
var ignoreCertificate = await configService.IsFeatureFlagEnabledAsync(ConfigurationConstants.FeatureFlags.IgnoreCertificateErrors);

var connection = new HubConnectionBuilder()
.WithUrl(hubRoute, options =>
{
//options.Transports = Microsoft.AspNetCore.Http.Connections.HttpTransportType.WebSockets;
options.HttpMessageHandlerFactory = (innerHandler) =>
{
if (ignoreCertificate && innerHandler is HttpClientHandler clientHandler)
{
// bypass SSL certificate
clientHandler.ServerCertificateCustomValidationCallback +=
(sender, certificate, chain, sslPolicyErrors) => { return true; };
}

return innerHandler;
//return new SignalRHubMessageHandler { InnerHandler = innerHandler };
};
})
.WithAutomaticReconnect()
.ConfigureLogging(config =>
{
config.SetMinimumLevel(LogLevel.Debug);
})
.Build();

return connection;
}
catch (Exception e)
{
_logger.WriteException(e, "Error during connection to SignalR Service");
throw;
}
}
}

MessagesCallbackService

Код: Выделить всё

public interface IMessagesCallbackService
{
event Func? OnConnectedAsync;
event Func? OnDisconnectedAsync;
event Func? OnTestMessageReceivedAsync;

Task ReceiveTestMessageAsync(string name, string message);

Task ConnectedAsync();
Task DisconnectedAsync(Exception? exception = null);
}

public class MessagesCallbackService : IMessagesCallbackService
{
public event Func? OnTestMessageReceivedAsync;
public event Func? OnConnectedAsync;
public event Func? OnDisconnectedAsync;

public async Task ConnectedAsync()
{
if (OnConnectedAsync != null)
{
await OnConnectedAsync.Invoke();
}
}

public async Task DisconnectedAsync(Exception? exception = null)
{
if (OnDisconnectedAsync != null)
{
await OnDisconnectedAsync.Invoke(exception);
}
}

public async Task ReceiveTestMessageAsync(string name, string message)
{
if (OnTestMessageReceivedAsync != null)
{
await OnTestMessageReceivedAsync.Invoke(name, message);
}
}
}

Служба MessagesHubService внедряется для запуска соединения и отправки сообщений в приложении blazor. MessagesCallbackService — это то, как я подключаю события для получения сообщений и действий на них. Эти части работают в рамках приложения blazor, где я могу использовать тестовую страницу, чтобы завершить сообщение и получить его в другом компоненте, где оно обрабатывается и изменяется пользовательский интерфейс.
Внешнее приложение (приложение-функции)
Program.cs

Код: Выделить всё

var signalRConnectionString = configuration.GetValue(ConfigurationConstants.ConfigurationKeys.SignalRConnectionString);

services.AddSignalR(options =>
{
options.EnableDetailedErrors = true;
}).AddAzureSignalR(signalRConnectionString);

SignalRMessagesService

Код: Выделить всё

public class SignalRMessagesService(IConfigurationService configService,
ISignalRConnectionFactory connectionFactory,
ILoggerFactory loggerFactory) : ISignalRMessagesService
{
private readonly ILogger _logger = loggerFactory.CreateLogger();

private ServiceManager? _serviceManager;
private HubConnection?  _connection;

private async Task GetServiceManagerAsync()
{
if (_serviceManager != null)
{
return _serviceManager;
}

try
{
_serviceManager = await connectionFactory.GetAzureServiceManagerAsync();

return _serviceManager;
}
catch (Exception e)
{
_logger.WriteException(e, "Error building Service Manager");
throw;
}
}

public async Task SendTestMessageAsync(string name, string message, CancellationToken cancellationToken = default)
{
var serviceManager = await GetServiceManagerAsync();

if (serviceManager == null)
{
throw new ArgumentNullException(nameof(ServiceManager));
}

try
{
await using var serviceHubContext = await serviceManager.CreateHubContextAsync(SignalRConstants.Hubs.Messages.HubName, cancellationToken);

await serviceHubContext
.Clients
.All
.SendAsync(method:SignalRConstants.Hubs.Messages.Methods.TestMessage, arg1:name, arg2:message, cancellationToken: cancellationToken);
}
catch (Exception e)
{
_logger.WriteException(e, "Error sending Test Message");
throw;
}
}

public void Dispose()
{
_serviceManager?.Dispose();
}

public async ValueTask DisposeAsync()
{

}
}

SignalRConnectionFactory

Код: Выделить всё

public class SignalRConnectionFactory(IConfigurationService configService,
ILoggerFactory loggerFactory) : ISignalRConnectionFactory
{
private readonly ILogger _logger = loggerFactory.CreateLogger();

public Task GetAzureServiceManagerAsync()
{
var serviceManager = new ServiceManagerBuilder()
.WithOptions(option =>
{
option.ConnectionString = configService.GetConfigurationValue(ConfigurationConstants.ConfigurationKeys
.SignalRConnectionString);
option.UseJsonObjectSerializer(new JsonObjectSerializer(new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
}));
})
.WithLoggerFactory(loggerFactory)
.BuildServiceManager();

return Task.FromResult(serviceManager);
}
}

В этом внешнем приложении я могу отправить сообщение без ошибок, однако клиентское приложение не получает сообщение.
Что я упускаю или делаю неправильно, чтобы сообщения не передавались между приложениями через одно и то же соединение Azure Signal R?>

Подробнее здесь: https://stackoverflow.com/questions/798 ... fault-mode
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C#»