对使用 .NET SDK 的 发布/订阅 进行故障排除。

试用 .NET 虚拟 Actor

发布/订阅的故障排除

发布/订阅 最常见的问题是应用程序中的 发布/订阅 终结点没有被调用。

这个问题有几层有不同的解决方案:

  • 应用程序没有接收到任何来自 Dapr 的流量
  • 应用程序没有向 Dapr 注册 发布/订阅 终结点
  • 发布/订阅 终结点在 Dapr 注册,但请求没有到达所需的终结点

第 1 步:打开日志

这一点很重要。 之后的步骤将取决于您能否看到日志输出。 ASP.NET Core日志几乎没有默认日志设置,所以您需要更改它。

按照 这里的描述,调整日志记录的详细程度,以包含 ASP.NET Core 的 Information 日志记录。 将 Microsoft 键的值设为 Information

第 2 步:验证您可以接收来自 Dapr 的流量

  1. 像平常一样启动应用程序(dapr run ...)。 请确保您在命令行中包含 --app-port 参数。 Dapr需要知道您的应用程序正在监听流量。 默认情况下,ASP.NET Core应用程序将在本地开发中通过5000端口监听HTTP。

  2. 等待 Dapr 完成启动

  3. 检查日志

你应该看到一个日志条目,如:

  1. info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
  2. Request starting HTTP/1.1 GET http://localhost:5000/.....

在初始化过程中,Dapr会对您的应用程序发出一些配置请求。 如果你找不到这些,那么这意味着出了问题。 请通过 issue 或 Discord 请求帮助 (包括日志)。 如果您看到向应用程序发出的请求,请继续第 3 步。

第 3 步:验证终结点注册

  1. 像平常一样启动应用程序(dapr run …)。

  2. 在命令行中使用curl(或其他HTTP测试工具)来访问/dapr/subscribe端点。

下面是一个例子,假设你的应用程序的监听端口是5000。

  1. curl http://localhost:5000/dapr/subscribe -v

对于正确配置的应用,输出应如下所示:

  1. * Trying ::1...
  2. * TCP_NODELAY set
  3. * Connected to localhost (::1) port 5000 (#0)
  4. > GET /dapr/subscribe HTTP/1.1
  5. > Host: localhost:5000
  6. > User-Agent: curl/7.64.1
  7. > Accept: */*
  8. >
  9. < HTTP/1.1 200 OK
  10. < Date: Fri, 15 Jan 2021 22:31:40 GMT
  11. < Content-Type: application/json
  12. < Server: Kestrel
  13. < Transfer-Encoding: chunked
  14. <
  15. * Connection #0 to host localhost left intact
  16. [{"topic":"deposit","route":"deposit","pubsubName":"pubsub"},{"topic":"withdraw","route":"withdraw","pubsubName":"pubsub"}]* Closing connection 0

特别注意 HTTP 状态代码和 JSON 输出。

  1. < HTTP/1.1 200 OK

200 状态代码表示成功。

最后包含的 JSON blob 是/dapr/subscribe的输出,由Dapr运行时处理。 在这种情况下,它使用的是这个repo中的ControllerSample - 所以这是一个正确输出的例子。

  1. [
  2. {"topic":"deposit","route":"deposit","pubsubName":"pubsub"},
  3. {"topic":"withdraw","route":"withdraw","pubsubName":"pubsub"}
  4. ]

有了这个命令的输出,你就可以诊断问题或进入下一步了。

选项0:响应是200,其中包含一些 发布/订阅 条目。

如果您在 JSON 输出中有此测试的条目,那么问题就会在其他地方出现,然后转到步骤 2。

选项 1: 响应不是 200, 或不包含 Json

如果响应不是200或不包含 JSON ,则 MapSubscribibeHandler() 终结点未能实现。

请确保您在 Startup.cs 中有一些代码,然后重复测试。

  1. app.UseRouting();
  2. app.UseCloudEvents();
  3. app.UseEndpoints(endpoints =>
  4. {
  5. endpoints.MapSubscribeHandler(); // This is the Dapr subscribe handler
  6. endpoints.MapControllers();
  7. });

如果添加订阅处理程序无法解决问题。 请在此仓库中打开一个问题,并包含您的 Startup.cs 文件。

选项 2:响应包含JSON,但它是空的(如 []

如果JSON输出是一个空数组 (如 []),那么下面的处理程序将被注册,但没有任何 topic 终结点。


如果你正在使用一个控制器来处理 发布/订阅 ,你应该有一个类似的方法:

  1. [Topic("pubsub", "deposit")]
  2. [HttpPost("deposit")]
  3. public async Task<ActionResult> Deposit(...)

在这个示例中,需要 TopicHttpPost 属性,但其他细节可能不同。


如果你使用的是 发布/订阅 的路由,你应该有一个终结点,比如:

  1. endpoints.MapPost("deposit", ...).WithTopic("pubsub", "deposit");

在这个例子中,需要调用 WithTopic(...) ,但其他细节可能有所不同。


在纠正这段代码并重新测试后,如果JSON输出仍然是空数组(像 [] ),那么请在这个仓库上打开一个问题,并包含 Startup.cs 的内容和你的 发布/订阅 终结点。

第 4 步:验证终结点是否可访问

在这一步中,我们将验证用 发布/订阅 注册的条目是否可以访问。 最后一步应该给你留下一些JSON输出,比如下面:

  1. [
  2. {"topic":"deposit","route":"deposit","pubsubName":"pubsub"},
  3. {"topic":"withdraw","route":"withdraw","pubsubName":"pubsub"}
  4. ]

保留此输出,因为我们将使用 route 信息来测试应用程序。

  1. 像平常一样启动应用程序(dapr run ...)。

  2. 在命令行使用 curl (或其他HTTP测试工具) 来访问一个注册了 发布/订阅 终结点的路由。

下面是一个例子,假设您的应用程序的监听端口是5000,并且您的 发布/订阅 路由之一是withdraw

  1. curl http://localhost:5000/withdraw -H 'Content-Type: application/json' -d '{}' -v

以下是对示例运行上述命令的输出:

  1. * Trying ::1...
  2. * TCP_NODELAY set
  3. * Connected to localhost (::1) port 5000 (#0)
  4. > POST /withdraw HTTP/1.1
  5. > Host: localhost:5000
  6. > User-Agent: curl/7.64.1
  7. > Accept: */*
  8. > Content-Type: application/json
  9. > Content-Length: 2
  10. >
  11. * upload completely sent off: 2 out of 2 bytes
  12. < HTTP/1.1 400 Bad Request
  13. < Date: Fri, 15 Jan 2021 22:53:27 GMT
  14. < Content-Type: application/problem+json; charset=utf-8
  15. < Server: Kestrel
  16. < Transfer-Encoding: chunked
  17. <
  18. * Connection #0 to host localhost left intact
  19. {"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日志头的输出示例。

  1. info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
  2. Request starting HTTP/1.1 POST http://localhost:5000/withdraw application/json 2
  3. info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
  4. Executing endpoint 'ControllerSample.Controllers.SampleController.Withdraw (ControllerSample)'
  5. info: Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker[3]
  6. 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).
  7. info: Microsoft.AspNetCore.Mvc.Infrastructure.ObjectResultExecutor[1]
  8. Executing ObjectResult, writing value of type 'Microsoft.AspNetCore.Mvc.ValidationProblemDetails'.
  9. info: Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker[2]
  10. Executed action ControllerSample.Controllers.SampleController.Withdraw (ControllerSample) in 52.1211ms
  11. info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
  12. Executed endpoint 'ControllerSample.Controllers.SampleController.Withdraw (ControllerSample)'
  13. info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
  14. Request finished in 157.056ms 400 application/problem+json; charset=utf-8
  15. info: Microsoft.AspNetCore.Mvc.Infrastructure.ObjectResultExecutor[1]
  16. Executing ObjectResult, writing value of type 'Microsoft.AspNetCore.Mvc.ValidationProblemDetails'.
  17. info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
  18. Request starting HTTP/1.1 POST http://localhost:5000/withdraw application/json 2
  19. info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
  20. Executing endpoint 'ControllerSample.Controllers.SampleController.Withdraw (ControllerSample)'
  21. info: Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker[3]
  22. 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).
  23. info: Microsoft.AspNetCore.Mvc.Infrastructure.ObjectResultExecutor[1]
  24. Executing ObjectResult, writing value of type 'Microsoft.AspNetCore.Mvc.ValidationProblemDetails'.
  25. info: Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker[2]
  26. Executed action ControllerSample.Controllers.SampleController.Withdraw (ControllerSample) in 52.1211ms
  27. info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
  28. Executed endpoint 'ControllerSample.Controllers.SampleController.Withdraw (ControllerSample)'
  29. info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
  30. Request finished in 157.056ms 400 application/problem+json; charset=utf-8

主要关注的日志条目是来自路由的:

  1. info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
  2. Executing endpoint 'ControllerSample.Controllers.SampleController.Withdraw (ControllerSample)'

此条目显示:

  • 路由已执行
  • 路由选择 ControllerSample.Controllers.SampleController.Withdraw (ControllerSample) 终结点

现在你已经掌握了解决这个问题所需的信息。

选项0:路由选择正确的终结点

如果路由日志条目中的信息是正确的,那么这意味着您的应用程序的行为是正确的。

You can run Kafka locally using this Docker image. To run without Docker, see the getting started guide here.

  1. info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
  2. Executing endpoint 'ControllerSample.Controllers.SampleController.Withdraw (ControllerSample)'

您可能想尝试使用 Dapr cli 执行直接发送 发布/订阅 消息并比较日志输出。

示例命令:

  1. dapr publish --pubsub pubsub --topic withdraw --data '{}'

如果这样做之后,你仍然不理解这个问题,请在此仓库中打开一个问题,并包含您的 Startup.cs 文件。

选项 1:路由没有执行

如果您在日志中没有看到 Microsoft.AspNetCore.Routing.EndpointMiddleware 的条目,那么这意味着该请求是由路由以外的其他东西处理的。 在这种情况下,问题通常是中间件错乱。 请求中的其他日志可能会给你一个线索,让你知道发生了什么。

如果您需要帮助理解这个问题,请在此仓库中打开一个问题,并包含您的 Startup.cs 文件。

选项 2:路由选择了错误的终结点

如果您在日志中看到 Microsoft.AspNetCore.Routing.EndpointMiddleware 的条目,但它包含了错误的端点,那么这意味着您有路由冲突。 所选择的终结点将出现在日志中,以便让你了解造成冲突的原因。

如果您需要帮助理解这个问题,请在此仓库中打开一个问题,并包含您的 Startup.cs 文件。