request-response-discovery.cpp 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. //
  2. // Copyright (c) 2019-2022 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com)
  3. //
  4. // Distributed under the MIT Software License
  5. //
  6. // in the example the usage of request-response pattern is demonstrated
  7. // the "server" actor takes the number from request and replies to
  8. // "client" actor with square root if the value is >= 0, otherwise
  9. // it replies with error.
  10. //
  11. // The key point here is that request is timeout supervised, i.e.
  12. // if the server will not answer with the specified timeout,
  13. // the client will know that.
  14. // this is an improved version, as client dynamically locates the server, i.e.
  15. // it pulls-in all dependencies
  16. #include "rotor.hpp"
  17. #include "rotor/asio.hpp"
  18. #include <iostream>
  19. #include <cmath>
  20. #include <system_error>
  21. #include <random>
  22. namespace asio = boost::asio;
  23. namespace pt = boost::posix_time;
  24. namespace payload {
  25. struct sample_res_t {
  26. double value;
  27. };
  28. struct sample_req_t {
  29. using response_t = sample_res_t;
  30. double value;
  31. };
  32. } // namespace payload
  33. namespace message {
  34. using request_t = rotor::request_traits_t<payload::sample_req_t>::request::message_t;
  35. using response_t = rotor::request_traits_t<payload::sample_req_t>::response::message_t;
  36. } // namespace message
  37. struct server_actor : public rotor::actor_base_t {
  38. using rotor::actor_base_t::actor_base_t;
  39. void configure(rotor::plugin::plugin_base_t &plugin) noexcept override {
  40. rotor::actor_base_t::configure(plugin);
  41. plugin.with_casted<rotor::plugin::starter_plugin_t>(
  42. [](auto &p) { p.subscribe_actor(&server_actor::on_request); });
  43. plugin.with_casted<rotor::plugin::registry_plugin_t>(
  44. [&](auto &p) { p.register_name("server", get_address()); });
  45. }
  46. void on_request(message::request_t &req) noexcept {
  47. auto in = req.payload.request_payload.value;
  48. if (in >= 0) {
  49. auto value = std::sqrt(in);
  50. reply_to(req, value);
  51. } else {
  52. // IRL, it should be your custom error codes
  53. auto ec = std::make_error_code(std::errc::invalid_argument);
  54. reply_with_error(req, make_error(ec));
  55. }
  56. }
  57. };
  58. struct client_actor : public rotor::actor_base_t {
  59. using rotor::actor_base_t::actor_base_t;
  60. rotor::address_ptr_t server_addr;
  61. void set_server(const rotor::address_ptr_t addr) { server_addr = addr; }
  62. void configure(rotor::plugin::plugin_base_t &plugin) noexcept override {
  63. rotor::actor_base_t::configure(plugin);
  64. plugin.with_casted<rotor::plugin::starter_plugin_t>(
  65. [](auto &p) { p.subscribe_actor(&client_actor::on_response); });
  66. plugin.with_casted<rotor::plugin::registry_plugin_t>(
  67. [&](auto &p) { p.discover_name("server", server_addr, true).link(); });
  68. }
  69. void on_response(message::response_t &res) noexcept {
  70. if (!res.payload.ee) { // check for possible error
  71. auto &in = res.payload.req->payload.request_payload.value;
  72. auto &out = res.payload.res.value;
  73. std::cout << " in = " << in << ", out = " << out << "\n";
  74. }
  75. do_shutdown(); // optional;
  76. }
  77. void on_start() noexcept override {
  78. rotor::actor_base_t::on_start();
  79. auto timeout = rotor::pt::milliseconds{1};
  80. request<payload::sample_req_t>(server_addr, 25.0).send(timeout);
  81. }
  82. };
  83. int main() {
  84. asio::io_context io_context;
  85. auto system_context = rotor::asio::system_context_asio_t::ptr_t{new rotor::asio::system_context_asio_t(io_context)};
  86. auto strand = std::make_shared<asio::io_context::strand>(io_context);
  87. auto timeout = boost::posix_time::milliseconds{500};
  88. auto sup = system_context->create_supervisor<rotor::asio::supervisor_asio_t>()
  89. .strand(strand)
  90. .create_registry()
  91. .timeout(timeout)
  92. .finish();
  93. sup->create_actor<server_actor>().timeout(timeout).finish();
  94. sup->create_actor<client_actor>().timeout(timeout).autoshutdown_supervisor().finish();
  95. sup->do_process();
  96. return 0;
  97. }