application.cpp 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. #include "app/application.h"
  2. #include <atomic>
  3. #include <chrono>
  4. #include <csignal>
  5. #include <cstring>
  6. #include <memory>
  7. #include <string>
  8. #include <thread>
  9. #include <unistd.h>
  10. #include "common/logger.h"
  11. #include "third/cos.h"
  12. #include "util/file.h"
  13. #include "im/handlers/account_handler.h"
  14. #include "im/handlers/favorite_handler.h"
  15. #include "im/handlers/friend_handler.h"
  16. #include "im/handlers/group_handler.h"
  17. #include "im/handlers/message_handler.h"
  18. #include "im/handlers/sticker_handler.h"
  19. #include "im/handlers/system_handler.h"
  20. #include "db/sticker_store.h"
  21. namespace im {
  22. namespace {
  23. std::atomic<bool> g_running{true};
  24. void on_signal(int)
  25. {
  26. g_running = false;
  27. }
  28. std::string find_init_sql(const std::string& cfg_path)
  29. {
  30. const std::string parent = ylib::file::parent_dir(cfg_path);
  31. const std::string candidates[] = {
  32. parent + "/../sql/init.sql",
  33. parent + "/sql/init.sql",
  34. "sql/init.sql",
  35. "../sql/init.sql",
  36. };
  37. for (const auto& path : candidates) {
  38. if (ylib::file::exist(path)) {
  39. return path;
  40. }
  41. }
  42. return parent + "/../sql/init.sql";
  43. }
  44. } // namespace
  45. int Application::run(int argc, char** argv)
  46. {
  47. if (!init(argc, argv)) {
  48. return 1;
  49. }
  50. wait_shutdown();
  51. shutdown();
  52. return 0;
  53. }
  54. bool Application::init(int argc, char** argv)
  55. {
  56. bool init_db = false;
  57. std::string cfg_path;
  58. for (int i = 1; i < argc; ++i) {
  59. const std::string arg = argv[i] != nullptr ? argv[i] : "";
  60. if (arg.empty()) {
  61. continue;
  62. }
  63. if (arg == "--init-db") {
  64. init_db = true;
  65. } else if (arg[0] == '-') {
  66. Logger::error("unknown option: " + arg);
  67. Logger::error("usage: server [--init-db] [config.ini]");
  68. return false;
  69. } else {
  70. cfg_path = arg;
  71. }
  72. }
  73. if (cfg_path.empty()) {
  74. cfg_path = AppConfig::resolve_path(0, nullptr);
  75. }
  76. if (!config_.load(cfg_path)) {
  77. return false;
  78. }
  79. if (init_db) {
  80. if (!config_.mysql().enable) {
  81. Logger::error("--init-db requires [mysql] enable=1");
  82. return false;
  83. }
  84. const std::string sql_path = find_init_sql(cfg_path);
  85. if (!ylib::file::exist(sql_path)) {
  86. Logger::error("init.sql not found, tried near " + cfg_path);
  87. return false;
  88. }
  89. Logger::info("init database from " + sql_path);
  90. if (!mysql_.init_schema(config_.mysql(), sql_path)) {
  91. return false;
  92. }
  93. }
  94. if (config_.cos().enable) {
  95. if (third::cos::enabled(config_.cos())) {
  96. Logger::info("cos storage bucket=" + config_.cos().bucket +
  97. " region=" + config_.cos().region +
  98. " prefix=" + config_.cos().prefix);
  99. } else {
  100. Logger::error("cos enable=1 but secret_id/secret_key/bucket/region incomplete");
  101. return false;
  102. }
  103. } else {
  104. Logger::error("COS storage is required");
  105. return false;
  106. }
  107. if (!config_.cdn_domain().empty()) {
  108. Logger::info("cdn domain=" + config_.cdn_domain());
  109. }
  110. if (config_.mysql().enable) {
  111. if (!mysql_.start(config_.mysql())) {
  112. if (config_.mysql().required) {
  113. Logger::error("mysql is required, abort");
  114. return false;
  115. }
  116. Logger::warn("mysql unavailable, continue without db");
  117. }
  118. }
  119. dispatcher_.add(Type::System, [] { return std::make_unique<SystemHandler>(); });
  120. dispatcher_.add(Type::Account, [] { return std::make_unique<AccountHandler>(); });
  121. dispatcher_.add(Type::Friend, [] { return std::make_unique<FriendHandler>(); });
  122. dispatcher_.add(Type::Message, [] { return std::make_unique<MessageHandler>(); });
  123. dispatcher_.add(Type::Sticker, [] { return std::make_unique<StickerHandler>(); });
  124. dispatcher_.add(Type::Favorite, [] { return std::make_unique<FavoriteHandler>(); });
  125. dispatcher_.add(Type::Group, [] { return std::make_unique<GroupHandler>(); });
  126. if (mysql_.started()) {
  127. StickerStore stickers(mysql_);
  128. stickers.seed();
  129. }
  130. std::string node_id = config_.server().node_id;
  131. if (node_id.empty()) {
  132. char host[256] = {0};
  133. if (gethostname(host, sizeof(host) - 1) != 0 || host[0] == '\0') {
  134. std::strcpy(host, "im");
  135. }
  136. node_id = std::string(host) + ":" + std::to_string(config_.server().port);
  137. }
  138. if (config_.redis().enable) {
  139. if (!redis_.start(config_.redis(), node_id)) {
  140. if (config_.redis().required) {
  141. Logger::error("redis is required, abort");
  142. return false;
  143. }
  144. Logger::warn("redis unavailable, continue in single-node mode");
  145. }
  146. } else {
  147. Logger::info("redis disabled, single-node node_id=" + node_id);
  148. }
  149. server_ = std::make_unique<ImServer>(config_, sessions_, dispatcher_, mysql_, &redis_);
  150. if (!server_->start()) {
  151. return false;
  152. }
  153. Logger::info("im server ready, packet=magic:type:cmd:seq:len + body");
  154. return true;
  155. }
  156. void Application::wait_shutdown()
  157. {
  158. std::signal(SIGINT, on_signal);
  159. std::signal(SIGTERM, on_signal);
  160. Logger::info("press Ctrl+C to stop");
  161. while (g_running) {
  162. std::this_thread::sleep_for(std::chrono::milliseconds(200));
  163. }
  164. }
  165. void Application::shutdown()
  166. {
  167. Logger::info("shutting down");
  168. if (server_) {
  169. server_->stop();
  170. server_.reset();
  171. }
  172. redis_.stop();
  173. mysql_.stop();
  174. }
  175. } // namespace im