使用Ocelot作为API网关在Docker Compose中出现连接拒绝问题
我正在基于Docker搭建包含多个.NET 8 API、Ocelot API网关与Angular前端的微服务架构。目前两个API服务可正常运行,Angular前端也能直接调用API获取数据。
为了在API与前端间加入网关,我选用Ocelot并配置了如下ocelot.json文件:
{"Routes": [{"DownstreamPathTemplate": "/api/v1/todos/tasks","DownstreamScheme": "http","DownstreamHostAndPorts": [{"Host": "todo.api","Port": "8080"}],"UpstreamPathTemplate": "/api/todos","UpstreamHttpMethod": ["Get"]},{"DownstreamPathTemplate": "/api/v1/food/recipes","DownstreamScheme": "http","DownstreamHostAndPorts": [{"Host": "food.api","Port": "5010"}],"UpstreamPathTemplate": "/api/recipes","UpstreamHttpMethod": ["Get"]}],"Aggregates": [],"GlobalConfiguration": {"BaseUrl": "http://localhost:5900"}}
随后我将所有服务加入Docker Compose配置:
version: '3.4' services: todo.api: image: todoapi container_name: todo.api build: context: ./TestProject.Service.Todo dockerfile: ./Api/Dockerfile ports: - "5000:8080" - "5001:8081" environment: - ASPNETCORE_ENVIRONMENT=Development todo.database: image: postgres:latest container_name: todo.database environment: - POSTGRES_DB=todos - POSTGRES_USER=postgres - POSTGRES_PASSWORD=postgres volumes: - ./.containers/todos-db:/var/lib/postgresql/data ports: - 5432:5432 gateway.web: image: gatewayweb container_name: gateway.web build: context: ./TestProject.Gateway.Web dockerfile: ./Api/Dockerfile ports: - "5900:8080" - "5901:8081" environment: - ASPNETCORE_ENVIRONMENT=Development dbbeaver: image: dbeaver/cloudbeaver ports: ["8000:8978"] hostname: gui container_name: dbeaver-container food.api: image: foodapi container_name: food.api build: context: ./TestProject.Service.Food dockerfile: ./Api/Dockerfile ports: - "5010:8080" - "5011:8081" environment: - ASPNETCORE_ENVIRONMENT=Development food.database: image: postgres:latest container_name: food.database environment: - POSTGRES_DB=food - POSTGRES_USER=postgres - POSTGRES_PASSWORD=postgres volumes: - ./.containers/food-db:/var/lib/postgresql/data ports: - 3366:5432 frontend: image: frontend build: context: ./TestProject.Frontend/TestProject.frontend dockerfile: ./dockerfile ports: - 4200:80
所有服务启动正常,但通过网关URL请求数据时出现如下错误:
requestId: 0HN3GO8DRCRJT:00000002, previousRequestId: No PreviousRequestId, message: 'Error Code: ConnectionToDownstreamServiceError Message: Error connecting to downstream service, exception: System.Net.Http.HttpRequestException: Connection refused (food.api:5010) ---> System.Net.Sockets.SocketException (111): Connection refused at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken cancellationToken) at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(Int16 token) at System.Net.Sockets.Socket.<ConnectAsync>g__WaitForConnectWithCancellation|285_0(AwaitableSocketAsyncEventArgs saea, ValueTask connectTask, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.ConnectToTcpHostAsync(String host, Int32 port, HttpRequestMessage initialRequest, Boolean async, CancellationToken cancellationToken) --- End of inner exception stack trace --- at System.Net.Http.HttpConnectionPool.ConnectToTcpHostAsync(String host, Int32 port, HttpRequestMessage initialRequest, Boolean async, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.CreateHttp11ConnectionAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.AddHttp11ConnectionAsync(QueueItem queueItem) at System.Threading.Tasks.TaskCompletionSourceWithCancellation`1.WaitWithCancellationAsync(CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken) at System.Net.Http.DiagnosticsHandler.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken) at Ocelot.Requester.TimeoutDelegatingHandler.SendAsync(HttpRequestMessage request, Boolean cancellationToken) at Ocelot.Requester.MessageInvokerHttpRequester.GetResponse(HttpContext httpContext) errors found in ResponderMiddleware. Setting error response for request path:/api/recipes, request method: GET'
我最初以为是Ocelot配置问题,但在非Docker环境下调试时网关可正常工作。尝试用localhost:端口替代服务名时,又出现"Cannot assign requested address (localhost:XXXX)"错误。目前无法定位问题原因,恳请有类似经验的开发者提供解决方案。
核心问题分析
Docker Compose容器间通信依赖服务名+容器内部端口,而非主机映射端口。你的Ocelot配置中指定的Port: "5010"是主机到容器的映射端口,但容器内API实际监听的是8080,这直接导致网关无法连接下游服务。另外,若服务不在同一Docker网络也会引发连接问题,但你当前的核心问题是端口配置错误。
具体修复步骤
修正Ocelot配置的下游端口
将ocelot.json中DownstreamHostAndPorts的端口改为容器内部监听的8080(而非主机映射端口):{"Routes": [ {"DownstreamPathTemplate": "/api/v1/todos/tasks", "DownstreamScheme": "http", "DownstreamHostAndPorts": [{"Host": "todo.api","Port": "8080"}], "UpstreamPathTemplate": "/api/todos", "UpstreamHttpMethod": ["Get"]}, {"DownstreamPathTemplate": "/api/v1/food/recipes", "DownstreamScheme": "http", "DownstreamHostAndPorts": [{"Host": "food.api","Port": "8080"}], "UpstreamPathTemplate": "/api/recipes", "UpstreamHttpMethod": ["Get"]}], "Aggregates": [], "GlobalConfiguration": {"BaseUrl": "http://localhost:5900"}}确保服务在同一Docker网络
Docker Compose默认会为项目创建默认网络,所有服务自动加入。若有手动配置网络的情况,需确保gateway.web、todo.api、food.api在同一网络中。显式配置示例:version: '3.4' networks: microservices-network: driver: bridge services: todo.api: # 原有配置 networks: - microservices-network food.api: # 原有配置 networks: - microservices-network gateway.web: # 原有配置 networks: - microservices-network验证容器间DNS解析
进入网关容器,ping下游服务名确认解析正常:docker exec -it gateway.web ping todo.api docker exec -it gateway.web ping food.api若无法ping通,检查服务名是否与
docker-compose.yml中services的名称完全一致(Docker服务名不区分大小写,但需避免拼写错误)。检查API服务监听地址
确保API服务在容器内监听0.0.0.0:8080(而非仅localhost),否则容器外部无法访问。可通过以下方式修改:- 在.NET API的
Program.cs中指定监听地址:var app = builder.Build(); app.Run("http://0.0.0.0:8080"); - 或在Dockerfile中添加环境变量:
ENV ASPNETCORE_URLS=http://0.0.0.0:8080
- 在.NET API的
验证修复
修改配置后,重新构建并启动服务:
docker-compose down docker-compose build docker-compose up -d
访问网关URL(如http://localhost:5900/api/recipes),确认可正常获取数据。
内容的提问来源于stack exchange,提问作者John Jameson

