redis.cpp 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. #include "redis.h"
  2. #include "dll_interface.h"
  3. extern "C" {
  4. #ifdef _WIN32
  5. DLL_EXPORT
  6. #endif
  7. int fastweb_module_regist(void* sol2, void* lua)
  8. {
  9. sol::state* state = static_cast<sol::state*>(sol2);
  10. module::redis_regist(state);
  11. return 0;
  12. }
  13. }
  14. module::redis_pool::redis_pool()
  15. {
  16. }
  17. module::redis_pool::~redis_pool()
  18. {
  19. }
  20. void module::redis_pool::start(const std::string& address, ushort port, const std::string& password, int max_size)
  21. {
  22. close();
  23. std::unique_lock<std::mutex> uni(m_mutex);
  24. m_closed = false;
  25. m_address = address;
  26. m_port = port;
  27. m_password = password;
  28. m_max_size = max_size;
  29. }
  30. void module::redis_pool::close()
  31. {
  32. std::unique_lock<std::mutex> uni(m_mutex);
  33. redisContext* ctx = nullptr;
  34. while (m_queue.pop(ctx))
  35. redisFree(ctx);
  36. m_pop_size = 0;
  37. m_closed = true;
  38. }
  39. std::shared_ptr<module::redis> module::redis_pool::get()
  40. {
  41. if (m_closed)
  42. {
  43. throw ylib::exception("connection pool is closed");
  44. }
  45. redisContext* ctx = nullptr;
  46. if (m_queue.pop(ctx))
  47. {
  48. m_pop_size++;
  49. return std::make_shared<module::redis>(ctx, this);
  50. }
  51. if (m_pop_size >= m_max_size)
  52. throw ylib::exception("connection pool releases more connections than the maximum number");
  53. ctx = reget(nullptr);
  54. m_pop_size++;
  55. return std::make_shared<module::redis>(ctx, this);
  56. }
  57. int module::redis_pool::pop_size()
  58. {
  59. return m_pop_size;
  60. }
  61. void module::redis_pool::regist_global(const char* name, sol::state* lua)
  62. {
  63. lua->registry()[name] = this;
  64. (*lua)[name] = this;
  65. }
  66. redisContext* module::redis_pool::reget(redisContext* ctx)
  67. {
  68. if (ctx != nullptr)
  69. {
  70. redisFree(ctx);
  71. ctx = nullptr;
  72. }
  73. redisContext* context = redisConnect(m_address.c_str(), m_port);
  74. if (context == NULL || context->err) {
  75. std::string exception;
  76. if (context) {
  77. exception = "connection error: " + std::string(context->errstr);
  78. redisFree(context);
  79. }
  80. else
  81. exception = "Connection error: can't allocate redis context";
  82. throw ylib::exception(exception);
  83. }
  84. if (m_password != "")
  85. {
  86. try
  87. {
  88. reply(nullptr, context, (redisReply*)redisCommand(context, "AUTH %s", m_password.c_str()));
  89. }
  90. catch (const ylib::exception& e)
  91. {
  92. redisFree(context);
  93. throw e;
  94. }
  95. }
  96. return context;
  97. }
  98. sol::object module::redis_pool::reply(sol::this_state* ts,redisContext* ctx, redisReply* reply)
  99. {
  100. if (reply == NULL) {
  101. if (ctx->err)
  102. throw ylib::exception("connection error: " + std::string(ctx->errstr));
  103. else
  104. throw ylib::exception("Unknown error: reply is NULL but context has no error\n");
  105. }
  106. sol::object result;
  107. try
  108. {
  109. switch (reply->type) {
  110. case REDIS_REPLY_ERROR:
  111. throw ylib::exception("redis error: " + std::string(reply->str));
  112. break;
  113. case REDIS_REPLY_STATUS:
  114. case REDIS_REPLY_STRING:
  115. if (ts != nullptr)
  116. result = sol::make_object(*ts, reply->str);
  117. break;
  118. case REDIS_REPLY_INTEGER:
  119. if (ts != nullptr)
  120. result = sol::make_object(*ts, reply->integer);
  121. break;
  122. case REDIS_REPLY_NIL:
  123. if (ts != nullptr)
  124. result = sol::make_object(*ts, sol::nil);
  125. break;
  126. case REDIS_REPLY_ARRAY:
  127. if (ts != nullptr)
  128. {
  129. sol::state_view lua(*ts);
  130. sol::table table = lua.create_table();
  131. for (size_t i = 0; i < reply->elements; i++) {
  132. table[i + 1] = this->reply(ts, ctx, reply->element[i]);
  133. }
  134. result = table;
  135. }
  136. break;
  137. default:
  138. throw ylib::exception("Unknown reply type: " + std::to_string(reply->type));
  139. }
  140. }
  141. catch (const std::exception& e)
  142. {
  143. freeReplyObject(reply);
  144. throw ylib::exception(e.what());
  145. }
  146. freeReplyObject(reply);
  147. return result;
  148. }
  149. void module::redis_pool::recover(redisContext* ctx)
  150. {
  151. if (ctx == nullptr)
  152. return;
  153. if (m_closed)
  154. {
  155. redisFree(ctx);
  156. return;
  157. }
  158. m_queue.push(ctx);
  159. m_pop_size--;
  160. }
  161. module::redis::redis(redisContext* context, redis_pool* pool):m_context(context),m_pool(pool)
  162. {
  163. }
  164. module::redis::~redis()
  165. {
  166. m_pool->recover(m_context);
  167. }
  168. sol::object module::redis::command(const std::string& cmd, sol::this_state ts)
  169. {
  170. //return m_pool->reply(&ts,m_context,(redisReply*)redisCommand(m_context, cmd.c_str()));
  171. auto reply = (redisReply*)redisCommand(m_context, cmd.c_str());
  172. if (reply == NULL) {
  173. if (m_context->err)
  174. {
  175. m_context = m_pool->reget(m_context);
  176. reply = (redisReply*)redisCommand(m_context, cmd.c_str());
  177. }
  178. else
  179. throw ylib::exception("Unknown error: reply is NULL but context has no error\n");
  180. }
  181. return m_pool->reply(&ts,m_context,reply);
  182. }
  183. void module::redis_regist(sol::state* lua)
  184. {
  185. lua->new_usertype<module::redis_pool>("fw_redis_pool",
  186. "close", &module::redis_pool::close,
  187. "start", &module::redis_pool::start,
  188. "get", &module::redis_pool::get,
  189. "pop_size", &module::redis_pool::pop_size,
  190. "self", &module::redis_pool::self
  191. );
  192. lua->new_usertype<module::redis>("fw_redis_conn",
  193. "command", &module::redis::command
  194. );
  195. }