Как настроить nginx в качестве обратного прокси-сервера для сервера Blazor с концентратором SignalRC#

Место общения программистов C#
Ответить Пред. темаСлед. тема
Anonymous
 Как настроить nginx в качестве обратного прокси-сервера для сервера Blazor с концентратором SignalR

Сообщение Anonymous »

Я использую серверное приложение Blazor уже несколько лет. Это довольно небольшой и простой сервис, которым пользуемся я и мои друзья. Он имеет обратный прокси-сервер nginx перед Kestrel и использует certbot для автоматической выдачи и обновления сертификатов Let's Encrypt.
Я запускаю службы с помощью docker-compose, и все контейнеры включены. та же сеть, которая соединена мостом с хостом.
Конфигурация для nginx выглядит так:

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

server {
listen 80;
server_name example.com *.example.com;
server_tokens off;

location /.well-known/acme-challenge/ {
root /var/www/certbot;
}

location / {
return 301 https://$host$request_uri;
}
}

server {
listen 443 ssl;
server_name example.com *.example.com;
server_tokens off;

ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
include             /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam         /etc/letsencrypt/ssl-dhparams.pem;

location / {
proxy_pass         http://docker-service-name:5000;
proxy_redirect     off;
proxy_http_version 1.1;
proxy_cache_bypass $http_upgrade;
proxy_set_header   Upgrade $http_upgrade;
proxy_set_header   Connection keep-alive;
proxy_set_header   Host $host;
proxy_set_header   X-Real-IP $remote_addr;
proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header   X-Forwarded-Proto $scheme;
proxy_set_header   X-Forwarded-Host $server_name;
}
}
Контейнер ASP.NET прослушивает порт 5000 следующим образом:

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

ENV ASPNETCORE_URLS=http://+:5000
Все те годы, что я управляю веб-сайтом, он работал хорошо.
Теперь я решил добавить собственный Концентратор SignalR, который может уведомлять клиентов об обновлениях, которые делает другой клиент, чтобы обеспечить более динамичную и плавную работу.
SignalR загружается следующим образом в Program.cs:

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

builder.Services.AddSignalR();

builder.Services.AddResponseCompression(opts =>
{
opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
["application/octet-stream"]);
});

app.UseResponseCompression();

app.MapHub(NotificationHub.HubUrl);
У меня есть один концентратор под названием NotificationHub с указанным URL-адресом концентратора /notifications.

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

public class NotificationHub :  Hub
{
public const string HubUrl = "/notifications";

public const string InviteCreatedMethod = "InviteCreated";
public const string InviteAcceptedMethod = "InviteAccepted";

private static readonly ILogger Log = Serilog.Log.ForContext();

public override Task OnConnectedAsync()
{
Log.Debug("{ConnectionId} connected", Context.ConnectionId);
return base.OnConnectedAsync();
}

public override async Task OnDisconnectedAsync(Exception? e)
{
Log.Debug(e, "{ConnectionId} disconnected", Context.ConnectionId);
await base.OnDisconnectedAsync(e);
}
}

public interface INotificationClient
{
Task InviteCreated(InviteCreatedNotification notification);
Task InviteAccepted(InviteAcceptedNotification notification);
}
На странице .razor хаб подписан следующим образом:

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

@implements IAsyncDisposable

@code {
private HubConnection _hubConnection;

public async ValueTask DisposeAsync()
{
Log.Debug("Page {Page} disposing", nameof(Home));
await _hubConnection.DisposeAsync();
}

protected override async Task OnInitializedAsync()
{
Log.Debug("Page {Page} initialized", nameof(Home));

_hubConnection = new HubConnectionBuilder()
.WithUrl(NavigationManager.ToAbsoluteUri(NotificationHub.HubUrl))
.Build();

_hubConnection.On(NotificationHub.InviteCreatedMethod, HandleInviteCreated);
_hubConnection.On(NotificationHub.InviteAcceptedMethod, HandleInviteAccepted);
await _hubConnection.StartAsync();
}

private void HandleInviteCreated(InviteCreatedNotification notification)
{
Log.Debug("{Notification} received", nameof(InviteCreatedNotification));
}

private void HandleInviteAccepted(InviteAcceptedNotification notification)
{
Log.Debug("{Notification} received", nameof(InviteAcceptedNotification));
}
}
При размещении за nginx браузер будет загружаться в течение длительного времени, пока не истечет время ожидания 504. Журналы nginx показывают, что он так и не получил ответа от ASP.NET. . Журналы ASP.NET выглядят так, будто запросы обрабатываются нормально.
При локальной работе я вижу, что получаю запросы на URL-адрес /notifications:

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

[14:09:11 INF] Request starting HTTP/2 POST https://localhost:7108/notifications/negotiate?negotiateVersion=1 - null 0
[14:09:11 INF] Executing endpoint '/notifications/negotiate'
При работе в рабочей среде я не вижу журналов, содержащих /notifications.
Когда время ожидания nginx истекает, это то, что зарегистрирован:

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

2024/12/18 15:10:36 [error] 39#39: *1 upstream timed out (110: Operation timed out) while reading response header from upstream, client: [client public IP], server: example.com, request: "GET / HTTP/1.1", upstream: "http://172.19.0.7:5000/", host: "www.example.com"
При первоначальном получении запроса журналы ASP.NET содержат следующее:

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

[15:09:36 INF] Request starting HTTP/1.1 GET http://www.example.com/ - null null
[15:09:36 INF] Executed DbCommand (2ms) [Parameters=[@__p_0='?' (DbType = Int64)], CommandType='Text', CommandTimeout='30']
SELECT a."Id", a."AccessFailedCount", a."ConcurrencyStamp", a."Email", a."EmailConfirmed", a."LockoutEnabled", a."LockoutEnd", a."NormalizedEmail", a."NormalizedUserName", a."PasswordHash", a."PhoneNumber", a."PhoneNumberConfirmed", a."SecurityStamp", a."TwoFactorEnabled", a."UserName"
FROM "AspNetUsers" AS a
WHERE a."Id" = @__p_0
LIMIT 1
[15:09:36 INF] Executed DbCommand (1ms) [Parameters=[@__user_Id_0='?' (DbType = Int64)], CommandType='Text', CommandTimeout='30']
SELECT a."Id", a."ClaimType", a."ClaimValue", a."UserId"
FROM "AspNetUserClaims" AS a
WHERE a."UserId" = @__user_Id_0
[15:09:36 INF] Executing endpoint '/ (/)'
[15:09:36 DBG] Adding entry for 18b0d7e3-dde7-45b6-875b-efb27271616c/MudBlazor.MudPopoverProvider.  1 total observers after add.
[15:09:36 DBG] Updating entry for 18b0d7e3-dde7-45b6-875b-efb27271616c/MudBlazor.MudPopoverProvider. 1 total observers.
[15:09:36 DBG] Page Home initialized
Подключение к концентратору, подробно описанное ранее, описано на главной странице.
Что я пробовал:
  • Обновите конфигурацию nginx в соответствии с рекомендациями Microsoft по использованию nginx вместе с ASP.NET.

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

map $http_connection $connection_upgrade {
"~*Upgrade" $http_connection;
default keep-alive;
}

location / {
proxy_pass         http://docker-service-name:5000;
proxy_http_version 1.1;
proxy_set_header   Upgrade $http_upgrade;
proxy_set_header   Connection $connection_upgrade;
proxy_set_header   Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header   X-Forwarded-Proto $scheme;
}
  • Явная настройка местоположения для URL-адреса концентратора, как предложено в ответе здесь на SO. Я добавил его как второе место после существующего "/".

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

location /notifications {
proxy_pass http://docker-service-name:5000;

# Configure WebSockets
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_cache_bypass $http_upgrade;

# Configure ServerSentEvents
proxy_buffering off;

# Configure LongPolling
proxy_read_timeout 100s;

proxy_set_header Host $host;
}
  • Обновите конфигурацию nginx в соответствии с рекомендациями Microsoft по использованию nginx вместе с SignalR.

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

server {
listen 443 ssl;
server_name example.com *.example.com;
server_tokens off;

ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
include             /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam         /etc/letsencrypt/ssl-dhparams.pem;

location / {
proxy_pass         http://docker-service-name:5000;
proxy_http_version 1.1;
proxy_set_header   Upgrade $http_upgrade;
proxy_set_header   Connection $connection_upgrade;
proxy_set_header   Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header   X-Forwarded-Proto $scheme;
}

location /notifications {
proxy_pass http://docker-service-name:5000;

# Configuration for WebSockets
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_cache off;
proxy_http_version 1.1;

# Configuration for ServerSentEvents
proxy_buffering off;

# Configuration for LongPolling or if your KeepAliveInterval is longer than 60 seconds
proxy_read_timeout 100s;

proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Все эти попытки приводят к одному и тому же поведению: время ожидания истекает при попытке просмотреть любую страницу, которая пытается установить соединение с хабом.
Как можно ли настроить nginx в качестве обратного прокси-сервера перед серверным приложением Blazor, использующим SignalR?

Подробнее здесь: https://stackoverflow.com/questions/792 ... ignalr-hub
Реклама
Ответить Пред. темаСлед. тема

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

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

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

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

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

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