使用 .NET SDK 进行 Pub/Sub 故障排查
Pub/Sub 故障排查
Pub/Sub 的常见问题是应用程序中的 Pub/Sub 端点未被调用。
这个问题可以分为几个层次,每个层次有不同的解决方案:
- 应用程序没有接收到来自 Dapr 的任何流量
- 应用程序没有向 Dapr 注册 Pub/Sub 端点
- Pub/Sub 端点已在 Dapr 中注册,但请求没有到达预期的端点
步骤 1:提高日志级别
这一点很重要。后续步骤将依赖于您查看日志输出的能力。ASP.NET Core 默认日志设置几乎不记录任何内容,因此您需要更改它。
调整日志详细程度以包括 ASP.NET Core 的 Information
日志,如此处所述。将 Microsoft
键设置为 Information
。
步骤 2:验证您可以接收到来自 Dapr 的流量
-
像往常一样启动应用程序(
dapr run ...
)。确保在命令行中包含--app-port
参数。Dapr 需要知道您的应用程序正在监听流量。默认情况下,ASP.NET Core 应用程序将在本地开发中监听 5000 端口的 HTTP。 -
等待 Dapr 启动完成
-
检查日志
您应该看到类似这样的日志条目:
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
Request starting HTTP/1.1 GET http://localhost:5000/.....
在初始化过程中,Dapr 会向您的应用程序发送一些请求以进行配置。如果找不到这些请求,则意味着出现了问题。请通过问题或 Discord 请求帮助(包括日志)。如果您看到对应用程序的请求,请继续执行下一步。
步骤 3:验证端点注册
-
像往常一样启动应用程序(
dapr run ...
)。 -
使用命令行中的
curl
(或其他 HTTP 测试工具)访问/dapr/subscribe
端点。
假设您的应用程序监听端口为 5000,这里是一个示例命令:
curl http://localhost:5000/dapr/subscribe -v
对于配置正确的应用程序,输出应如下所示:
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 5000 (#0)
> GET /dapr/subscribe HTTP/1.1
> Host: localhost:5000
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Fri, 15 Jan 2021 22:31:40 GMT
< Content-Type: application/json
< Server: Kestrel
< Transfer-Encoding: chunked
<
* Connection #0 to host localhost left intact
[{"topic":"deposit","route":"deposit","pubsubName":"pubsub"},{"topic":"withdraw","route":"withdraw","pubsubName":"pubsub"}]* Closing connection 0
特别注意 HTTP 状态码和 JSON 输出。
< HTTP/1.1 200 OK
200 状态码表示成功。
JSON 数据块是 /dapr/subscribe
的输出,由 Dapr 运行时处理。在这种情况下,它使用的是此仓库中的 ControllerSample
- 这是正确输出的示例。
[
{"topic":"deposit","route":"deposit","pubsubName":"pubsub"},
{"topic":"withdraw","route":"withdraw","pubsubName":"pubsub"}
]
通过此命令的输出,您可以诊断问题或继续下一步。
选项 0:响应为 200 并包含一些 Pub/Sub 条目
如果您在此测试的 JSON 输出中有条目,则问题出在其他地方,请继续执行下一步。
选项 1:响应不是 200,或不包含 JSON
如果响应不是 200 或不包含 JSON,则 MapSubscribeHandler()
端点未被访问。
确保在 Startup.cs
中有如下代码并重复测试。
app.UseRouting();
app.UseCloudEvents();
app.UseEndpoints(endpoints =>
{
endpoints.MapSubscribeHandler(); // 这是 Dapr 订阅处理程序
endpoints.MapControllers();
});
如果添加订阅处理程序没有解决问题,请在此仓库中打开一个问题,并包括您的 Startup.cs
文件的内容。
选项 2:响应包含 JSON 但为空(如 []
)
如果 JSON 输出是一个空数组(如 []
),则订阅处理程序已注册,但没有注册主题端点。
如果您使用控制器进行 Pub/Sub,您应该有一个类似的方法:
[Topic("pubsub", "deposit")]
[HttpPost("deposit")]
public async Task<ActionResult> Deposit(...)
// 使用 Pub/Sub 路由
[Topic("pubsub", "transactions", "event.type == \"withdraw.v2\"", 1)]
[HttpPost("withdraw")]
public async Task<ActionResult> Withdraw(...)
在此示例中,Topic
和 HttpPost
属性是必需的,但其他细节可能不同。
如果您使用路由进行 Pub/Sub,您应该有一个类似的端点:
endpoints.MapPost("deposit", ...).WithTopic("pubsub", "deposit");
在此示例中,调用 WithTopic(...)
是必需的,但其他细节可能不同。
在更正此代码并重新测试后,如果 JSON 输出仍然是空数组(如 []
),请在此仓库中打开一个问题,并包括 Startup.cs
和您的 Pub/Sub 端点的内容。
步骤 4:验证端点可达性
在此步骤中,我们将验证注册的 Pub/Sub 条目是否可达。上一步应该让您得到如下的 JSON 输出:
[
{
"pubsubName": "pubsub",
"topic": "deposit",
"route": "deposit"
},
{
"pubsubName": "pubsub",
"topic": "deposit",
"routes": {
"rules": [
{
"match": "event.type == \"withdraw.v2\"",
"path": "withdraw"
}
]
}
}
]
保留此输出,因为我们将使用 route
信息来测试应用程序。
-
像往常一样启动应用程序(
dapr run ...
)。 -
使用命令行中的
curl
(或其他 HTTP 测试工具)访问注册的 Pub/Sub 端点之一。
假设您的应用程序监听端口为 5000,并且您的一个 Pub/Sub 路由是 withdraw
,这里是一个示例命令:
curl http://localhost:5000/withdraw -H 'Content-Type: application/json' -d '{}' -v
以下是对示例运行上述命令的输出:
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 5000 (#0)
> POST /withdraw HTTP/1.1
> Host: localhost:5000
> User-Agent: curl/7.64.1
> Accept: */*
> Content-Type: application/json
> Content-Length: 2
>
* upload completely sent off: 2 out of 2 bytes
< HTTP/1.1 400 Bad Request
< Date: Fri, 15 Jan 2021 22:53:27 GMT
< Content-Type: application/problem+json; charset=utf-8
< Server: Kestrel
< Transfer-Encoding: chunked
<
* Connection #0 to host localhost left intact
{"type":"https://tools.ietf.org/html/rfc7231#section-6.5.1","title":"One or more validation errors occurred.","status":400,"traceId":"|5e9d7eee-4ea66b1e144ce9bb.","errors":{"Id":["The Id field is required."]}}* Closing connection 0
根据 HTTP 400 和 JSON 负载,此响应表明端点已被访问,但请求由于验证错误而被拒绝。
您还应该查看正在运行的应用程序的控制台输出。这是去掉 Dapr 日志头后的示例输出,以便更清晰。
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
Request starting HTTP/1.1 POST http://localhost:5000/withdraw application/json 2
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
Executing endpoint 'ControllerSample.Controllers.SampleController.Withdraw (ControllerSample)'
info: Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker[3]
Route matched with {action = "Withdraw", controller = "Sample"}. Executing controller action with signature System.Threading.Tasks.Task`1[Microsoft.AspNetCore.Mvc.ActionResult`1[ControllerSample.Account]] Withdraw(ControllerSample.Transaction, Dapr.Client.DaprClient) on controller ControllerSample.Controllers.SampleController (ControllerSample).
info: Microsoft.AspNetCore.Mvc.Infrastructure.ObjectResultExecutor[1]
Executing ObjectResult, writing value of type 'Microsoft.AspNetCore.Mvc.ValidationProblemDetails'.
info: Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker[2]
Executed action ControllerSample.Controllers.SampleController.Withdraw (ControllerSample) in 52.1211ms
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
Executed endpoint 'ControllerSample.Controllers.SampleController.Withdraw (ControllerSample)'
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
Request finished in 157.056ms 400 application/problem+json; charset=utf-8
主要关注的日志条目是来自路由的:
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
Executing endpoint 'ControllerSample.Controllers.SampleController.Withdraw (ControllerSample)'
此条目显示:
- 路由已执行
- 路由选择了
ControllerSample.Controllers.SampleController.Withdraw (ControllerSample)
端点
现在您有了排查此步骤问题所需的信息。
选项 0:路由选择了正确的端点
如果路由日志条目中的信息是正确的,则意味着在隔离情况下,您的应用程序行为正确。
示例:
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
Executing endpoint 'ControllerSample.Controllers.SampleController.Withdraw (ControllerSample)'
您可能想尝试使用 Dapr CLI 直接发送 Pub/Sub 消息并比较日志输出。
示例命令:
dapr publish --pubsub pubsub --topic withdraw --data '{}'
如果在这样做之后您仍然不理解问题,请在此仓库中打开一个问题,并包括您的 Startup.cs
的内容。
选项 1:路由未执行
如果您在日志中没有看到 Microsoft.AspNetCore.Routing.EndpointMiddleware
的条目,则意味着请求被其他东西处理了。通常情况下,问题是一个行为不当的中间件。请求的其他日志可能会给您一个线索。
如果您需要帮助理解问题,请在此仓库中打开一个问题,并包括您的 Startup.cs
的内容。
选项 2:路由选择了错误的端点
如果您在日志中看到 Microsoft.AspNetCore.Routing.EndpointMiddleware
的条目,但它包含错误的端点,则意味着您有一个路由冲突。被选择的端点将出现在日志中,这应该能给您一个关于冲突原因的想法。
如果您需要帮助理解问题,请在此仓库中打开一个问题,并包括您的 Startup.cs
的内容。
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.