request-response.cpp 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. //
  2. // Copyright (c) 2019-2021 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. // better version: with discovered & linked
  15. #include "rotor.hpp"
  16. #include "rotor/asio.hpp"
  17. #include <iostream>
  18. #include <cmath>
  19. #include <system_error>
  20. namespace asio = boost::asio;
  21. namespace pt = boost::posix_time;
  22. namespace payload {
  23. struct sample_res_t {
  24. double value;
  25. };
  26. struct sample_req_t {
  27. using response_t = sample_res_t;
  28. double value;
  29. };
  30. } // namespace payload
  31. namespace message {
  32. using request_t = rotor::request_traits_t<payload::sample_req_t>::request::message_t;
  33. using response_t = rotor::request_traits_t<payload::sample_req_t>::response::message_t;
  34. } // namespace message
  35. struct server_actor : public rotor::actor_base_t {
  36. using rotor::actor_base_t::actor_base_t;
  37. void configure(rotor::plugin::plugin_base_t &plugin) noexcept override {
  38. rotor::actor_base_t::configure(plugin);
  39. plugin.with_casted<rotor::plugin::starter_plugin_t>(
  40. [](auto &p) { p.subscribe_actor(&server_actor::on_request); });
  41. }
  42. void on_request(message::request_t &req) noexcept {
  43. auto in = req.payload.request_payload.value;
  44. if (in >= 0) {
  45. auto value = std::sqrt(in);
  46. reply_to(req, value);
  47. } else {
  48. // IRL, it should be your custom error codes
  49. auto ec = std::make_error_code(std::errc::invalid_argument);
  50. reply_with_error(req, make_error(ec));
  51. }
  52. }
  53. };
  54. struct client_actor : public rotor::actor_base_t {
  55. using rotor::actor_base_t::actor_base_t;
  56. rotor::address_ptr_t server_addr;
  57. void set_server(const rotor::address_ptr_t addr) { server_addr = addr; }
  58. void configure(rotor::plugin::plugin_base_t &plugin) noexcept override {
  59. rotor::actor_base_t::configure(plugin);
  60. plugin.with_casted<rotor::plugin::starter_plugin_t>(
  61. [](auto &p) { p.subscribe_actor(&client_actor::on_response); });
  62. }
  63. void on_response(message::response_t &res) noexcept {
  64. if (!res.payload.ee) { // check for possible error
  65. auto &in = res.payload.req->payload.request_payload.value;
  66. auto &out = res.payload.res.value;
  67. std::cout << " in = " << in << ", out = " << out << "\n";
  68. }
  69. do_shutdown();
  70. }
  71. void on_start() noexcept override {
  72. rotor::actor_base_t::on_start();
  73. auto timeout = rotor::pt::milliseconds{1};
  74. request<payload::sample_req_t>(server_addr, 25.0).send(timeout);
  75. }
  76. };
  77. int main() {
  78. asio::io_context io_context;
  79. auto system_context = rotor::asio::system_context_asio_t::ptr_t{new rotor::asio::system_context_asio_t(io_context)};
  80. auto strand = std::make_shared<asio::io_context::strand>(io_context);
  81. auto timeout = boost::posix_time::milliseconds{500};
  82. auto sup =
  83. system_context->create_supervisor<rotor::asio::supervisor_asio_t>().strand(strand).timeout(timeout).finish();
  84. auto server = sup->create_actor<server_actor>().timeout(timeout).finish();
  85. auto client = sup->create_actor<client_actor>().timeout(timeout).autoshutdown_supervisor().finish();
  86. client->set_server(server->get_address());
  87. sup->do_process();
  88. return 0;
  89. }