您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

mod_muc_rate_limit.lua 5.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. -- enable under the main muc component
  2. local queue = require "util.queue";
  3. local new_throttle = require "util.throttle".create;
  4. local timer = require "util.timer";
  5. -- we max to 500 participants per meeting so this should be enough, we are not suppose to handle all
  6. -- participants in one meeting
  7. local PRESENCE_QUEUE_MAX_SIZE = 1000;
  8. -- default to 5 participants per second
  9. local join_rate_per_conference = module:get_option_number("muc_rate_joins", 5);
  10. -- Measure/monitor the room rate limiting queue
  11. local measure = require "core.statsmanager".measure;
  12. local measure_longest_queue = measure("distribution",
  13. "/mod_" .. module.name .. "/longest_queue");
  14. local measure_rooms_with_queue = measure("rate",
  15. "/mod_" .. module.name .. "/rooms_with_queue");
  16. -- throws a stat that the queue was full, counts the total number of times we hit it
  17. local measure_full_queue = measure("rate",
  18. "/mod_" .. module.name .. "/full_queue");
  19. -- keeps track of the total times we had an error processing the queue
  20. local measure_errors_processing_queue = measure("rate",
  21. "/mod_" .. module.name .. "/errors_processing_queue");
  22. -- we keep track here what was the longest queue we have seen
  23. local stat_longest_queue = 0;
  24. -- Adds item to the queue
  25. -- @returns false if queue is full and item was not added, true otherwise
  26. local function add_item_to_queue(joining_queue, item, room, from)
  27. if not joining_queue:push(item) then
  28. module:log('error', 'Error pushing presence in queue for %s in %s', from, room.jid);
  29. measure_full_queue();
  30. return false;
  31. else
  32. -- check is this the longest queue and if so throws a stat
  33. if joining_queue:count() > stat_longest_queue then
  34. stat_longest_queue = joining_queue:count();
  35. measure_longest_queue(stat_longest_queue);
  36. end
  37. return true;
  38. end
  39. end
  40. -- process join_rate_presence_queue in the room and pops element passing them to handle_normal_presence
  41. -- returns 1 if we want to reschedule it after 1 second
  42. local function timer_process_queue_elements (room)
  43. local presence_queue = room.join_rate_presence_queue;
  44. if not presence_queue or presence_queue:count() == 0 then
  45. return;
  46. end
  47. for _ = 1, join_rate_per_conference do
  48. local ev = presence_queue:pop();
  49. if ev then
  50. -- we mark what we pass here so we can skip it on the next muc-occupant-pre-join event
  51. ev.stanza.delayed_join_skip = true;
  52. room:handle_normal_presence(ev.origin, ev.stanza);
  53. end
  54. end
  55. -- if there are elements left, schedule an execution in a second
  56. if presence_queue:count() > 0 then
  57. return 1;
  58. else
  59. room.join_rate_queue_timer = false;
  60. end
  61. end
  62. -- we check join rate before occupant joins. If rate is exceeded we queue the events and start a timer
  63. -- that will run every second processing the events passing them to the room handling function handle_normal_presence
  64. -- from where those arrived, this way we keep a maximum rate of joining
  65. module:hook("muc-occupant-pre-join", function (event)
  66. local room, stanza = event.room, event.stanza;
  67. -- skipping events we had produced and clear our flag
  68. if stanza.delayed_join_skip == true then
  69. event.stanza.delayed_join_skip = nil;
  70. return false;
  71. end
  72. local throttle = room.join_rate_throttle;
  73. if not room.join_rate_throttle then
  74. throttle = new_throttle(join_rate_per_conference, 1); -- rate per one second
  75. room.join_rate_throttle = throttle;
  76. end
  77. if not throttle:poll(1) then
  78. if not room.join_rate_presence_queue then
  79. -- if this is the first item for a room we increment the stat for rooms with queues
  80. measure_rooms_with_queue();
  81. room.join_rate_presence_queue = queue.new(PRESENCE_QUEUE_MAX_SIZE);
  82. end
  83. if not add_item_to_queue(room.join_rate_presence_queue, event, room, stanza.attr.from) then
  84. -- let's not stop processing the event
  85. return false;
  86. end
  87. if not room.join_rate_queue_timer then
  88. timer.add_task(1, function ()
  89. local status, result = pcall(timer_process_queue_elements, room);
  90. if not status then
  91. -- there was an error in the timer function
  92. module:log('error', 'Error processing queue: %s', result);
  93. measure_errors_processing_queue();
  94. -- let's re-schedule timer so we do not lose the queue
  95. return 1;
  96. end
  97. return result;
  98. end);
  99. room.join_rate_queue_timer = true;
  100. end
  101. return true; -- we stop execution, so we do not process this join at the moment
  102. end
  103. if room.join_rate_queue_timer then
  104. -- there is timer so we need to order the presences, put it in the queue
  105. -- if add fails as queue is full we return false and the event will continue processing, we risk re-order
  106. -- but not losing it
  107. return add_item_to_queue(room.join_rate_presence_queue, event, room, stanza.attr.from);
  108. end
  109. end, 9); -- as we will rate limit joins we need to be the first to execute
  110. -- we ran it after muc_max_occupants which is with priority 10, there is nothing to rate limit
  111. -- if max number of occupants is reached
  112. -- clear queue on room destroy so timer will skip next run if any
  113. module:hook('muc-room-destroyed',function(event)
  114. event.room.join_rate_presence_queue = nil;
  115. end);