如何 运行 StartAsync connection of signalr blazor client in docker image?

how to run StartAsync connection of signalr blazor client in docker image?

我创建了默认的 Blazor 服务器端应用程序。然后添加Microsoft.AspNetCore.SignalR.ClientChatHubclass。然后编辑 startup.cs 文件(添加 services.AddSignalR()endpoints.MapHub<ChatHub>("/chatHub"))和 index.razor 页面。然后通过 IIS express 运行。没关系。

然后添加了 docker 支持和 运行 Docker 主机。它不工作。因为只有不工作的集线器连接 StartAsync 方法。如何运行呢?帮我? 非常感谢你们。

错误是:

An unhandled exception occurred while processing the request. SocketException: Cannot assign requested address System.Net.Http.ConnectHelper.ConnectAsync(string host, int port, CancellationToken cancellationToken)

HttpRequestException: Cannot assign requested address System.Net.Http.ConnectHelper.ConnectAsync(string host, int port, CancellationToken cancellationToken)

index.razor代码:

@code {
    private HubConnection _hubConnection;

    protected override async Task OnInitializedAsync()
    {
        _hubConnection = new HubConnectionBuilder()
            .WithUrl(NavigationManager.ToAbsoluteUri("/chatHub"))
            .Build();

        _hubConnection.On<string, string>("ReceiveMessage", (user, message) =>
        {
            var encodedMsg = $"{user}: {message}";
            StateHasChanged();
        });

        await _hubConnection.StartAsync(); // **DON'T WORK IN DOCKER HOST.**
    }
}

Docker 文件:

FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base
WORKDIR /app
EXPOSE 80

FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build
WORKDIR /src
COPY ["BlazorApp1/BlazorApp1.csproj", "BlazorApp1/"]
RUN dotnet restore "BlazorApp1/BlazorApp1.csproj"
COPY . .
WORKDIR "/src/BlazorApp1"
RUN dotnet build "BlazorApp1.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "BlazorApp1.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "BlazorApp1.dll"]

我最好的猜测是您的中心客户端正在尝试连接到 "the-public-url-out-of-docker/chatHub":

    _hubConnection = new HubConnectionBuilder()
        .WithUrl(NavigationManager.ToAbsoluteUri("/chatHub"))
        .Build();

NavigationManager.ToAbsoluteUri(...) 会将 /chatHub 转换为 public url 并暴露给最终用户。例如,如果您使用反向代理,它可能是域名。

请注意 url 分为三个不同的级别:

  • 暴露的域名public
  • 主机IP和端口
  • 容器ip&端口
  +----------------------------------+
  | HOST  (5000)                     |
  |   +                              |
  |   |Port Mapping---------------+  |
  |   >-->-->|Container (80)      |  |
  |          |                    |  |
  |          +--------------------+  |
  +-----^----------------------------+
        |  reverse proxy
+-------+----------------------------+
| nginx                              |
|  https://www.myexample.com/chatHub
|                                    |
+-------^----------------------------+
        |
        |
        |
        |
+-------+-----------+
|                   |
|   Browser         | (Brazor sees only the public url via NavgiationManager  )
|                   |
+-------------------+

但是,当docker中的运行时,主机的网络始终无法从容器的网络访问。

如果是这样,有几种方法应该有效:

  1. 避免使用像 .WithUrl(NavigationManager.ToAbsoluteUri("/chatHub")) 这样的 public url。将其硬编码到容器 ip 和端口。例如,如果你的容器监听 80,它应该是 http://localhost/chatHub.
  2. 为 docker 配置网络,或为 运行 docker 添加 --network。有关详细信息,请参阅 this thread

我有同样的问题,其中 运行 通过 IIS 应用程序按预期工作,但是当 运行 通过 docker SignalR 无法连接到集线器。我通过定义我自己的导航管理器解决了这个问题,它继承自 NavigationManager 并覆盖 EnsureInitialized() 方法来设置基本 URI。

public class CustomNavigationManager : NavigationManager
{
    protected override void EnsureInitialized()
    {
        var baseUri = Environment.GetEnvironmentVariable("blazor-app-url");
        Initialize(baseUri, baseUri);
    }
}

它还需要在我的 docker-compose.override.yml 文件中添加一个容器名称和一个环境变量,以便在 CustomNavigationManager.

中使用
version: '3.4'

networks:
  mynetwork:
    external: true      

services:
  my-blazor-app:
    networks: 
      - mynetwork
    container_name: blazor-app    
    environment:
      blazor_app_url: http://blazor-app:8080/
    ports:
      - "49101:8080"

CustomNavigationManager可以在启动时注册为Singleton,注入相关组件(代替NavigationManager),继续像之前的NavigationManager一样使用,例如:

public class WeatherForecastService : IWeatherForecastService
{
    private readonly HttpClient _httpClient;
    private readonly CustomNavigationManager _navigationManager;

    public WeatherForecastService(HttpClient httpClient, CustomNavigationManager navigationManager)
    {
        _httpClient = httpClient;
        _navigationManager = navigationManager;
    }

    public async Task<IEnumerable<WeatherForecast>?> GetWeatherForecast(DateTime startDate)
    {
        var uri = _navigationManager.ToAbsoluteUri($"/weatherforecast/get?startDate={startDate:s}");
        return await _httpClient.GetFromJsonAsync<WeatherForecast[]>(uri);
    }
}

为了使应用程序在 运行 通过 IIS express 时继续工作,还需要在 launchSettings.json 中定义环境变量。

"IIS Express": {
  "commandName": "IISExpress",
  "launchBrowser": true,
  "environmentVariables": {
    "blazor_app_url": "http://localhost:5282/"
  }
}