From c98e3c9d74915856b1a9bd5b5c217dcaadc5b26e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=B1=E5=91=B1=E5=91=B1?= Date: Thu, 20 Jul 2023 17:03:40 +0800 Subject: [PATCH] update fork --- app/controllers/forks_controller.rb | 8 ++-- app/services/leaky_bucket_rate_limiter.rb | 42 ++++++++++++++++++ app/services/sliding_window_rate_limiter.rb | 39 +++++++++++++++++ app/services/token_bucket_rate_limiter.rb | 48 +++++++++++++++++++++ 4 files changed, 134 insertions(+), 3 deletions(-) create mode 100644 app/services/leaky_bucket_rate_limiter.rb create mode 100644 app/services/sliding_window_rate_limiter.rb create mode 100644 app/services/token_bucket_rate_limiter.rb diff --git a/app/controllers/forks_controller.rb b/app/controllers/forks_controller.rb index b5ad0ff21..e20f27efd 100644 --- a/app/controllers/forks_controller.rb +++ b/app/controllers/forks_controller.rb @@ -26,14 +26,16 @@ class ForksController < ApplicationController private def authenticate_project! if params[:organization].present? - return render_forbidden('参数错误,当组织字段存在时,不允许重命名fork仓库') if params[:new_identifier].present? || params[:new_name].present? + return render_forbidden('参数错误,当organization存在时不允许fork重命名') if params[:new_identifier].present? || params[:new_name].present? @organization = Organization.find_by(login:params[:organization]) return render_forbidden('仓库不存在') unless @organization.present? return render_forbidden('你没有权限操作') unless @organization.is_admin?(current_user.id) end - if params[:organization].blank? && Project.exists?(user_id: current_user.id, identifier: (params[:new_identifier] || @project.identifier)) - render_result(-2, "fork失败,您已拥有了这个项目") + if Project.exists?(user_id: current_user.id, forked_from_project_id: @project.id) + render_result(-1, "fork失败,您已fork过了这个项目") + elsif params[:organization].blank? && Project.exists?(user_id: current_user.id, identifier: (params[:new_identifier] || @project.identifier)) + render_result(-2, "fork失败, fork本人项目需要为fork仓库重命名") elsif @organization && Project.exists?(user_id: [@organization.id], identifier: (params[:new_identifier] || @project.identifier)) render_result(-1, "fork失败,组织已拥有了这个项目") elsif gitea_check_exit(current_user) diff --git a/app/services/leaky_bucket_rate_limiter.rb b/app/services/leaky_bucket_rate_limiter.rb new file mode 100644 index 000000000..2508e99e0 --- /dev/null +++ b/app/services/leaky_bucket_rate_limiter.rb @@ -0,0 +1,42 @@ +class LeakyBucketRateLimiter + def initialize(user_id, rate, capacity) + @rate = rate # 令牌发放速率(每秒发放的令牌数) + @capacity = capacity # 桶的容量(最大令牌数) + @redis = $redis_cache + @key = "#{user_id}:LeakyBucket" + @current_time = Time.now.to_f + end + + def allow_request + current_time = Time.now.to_f + last_drip_time = @redis.getset(@key + ':last_drip_time', current_time).to_f + tokens = @redis.getset(@key + ':tokens', @capacity).to_i + + # 计算已经漏掉的令牌数量 + leaked_tokens = (current_time - last_drip_time) * @rate + puts leaked_tokens + # 如果桶中的令牌多于漏掉的令牌数量,则漏水 + tokens = [@capacity, tokens + leaked_tokens].min + puts tokens + if tokens >= 1 + @redis.set(@key + ':last_drip_time', current_time) + @redis.decr(@key + ':tokens') + return true + end + false + end +end + + +=begin + 失败 + limiter = LeakyBucketRateLimiter.new(110,10, 40) # 设置令牌发放速率为10,桶的容量为40 + 30.times do + if limiter.allow_request + puts "Allow" + else + puts "Reject" + end + end + +=end \ No newline at end of file diff --git a/app/services/sliding_window_rate_limiter.rb b/app/services/sliding_window_rate_limiter.rb new file mode 100644 index 000000000..a999345ef --- /dev/null +++ b/app/services/sliding_window_rate_limiter.rb @@ -0,0 +1,39 @@ + +class SlidingWindowRateLimiter + def initialize(user_id, rate, window_size) + @rate = rate # 请求速率限制(每秒的请求数) + @window_size = window_size # 时间窗口大小(秒) + @redis = $redis_cache + @key = "#{user_id}:SlidingWindow" + @current_timestamp = Time.now.to_f + end + + def allow_request + current_timestamp = Time.now.to_f + score = current_timestamp.to_i + start_time = current_timestamp - @window_size + + @redis.zremrangebyscore(@key, '-inf', start_time) + count = @redis.zcount(@key, '-inf', '+inf').to_i + + return false if count >= @rate + + @redis.zadd(@key, score, current_timestamp) + true + end +end + + +=begin + #测试通过 + + limiter = SlidingWindowRateLimiter.new(user_id,10, 1) # 设置请求速率限制为10次/秒,时间窗口大小为1秒 + 40.times do + if limiter.allow_request + puts "Allow" + else + puts "Reject" + end + end + +=end \ No newline at end of file diff --git a/app/services/token_bucket_rate_limiter.rb b/app/services/token_bucket_rate_limiter.rb new file mode 100644 index 000000000..c0d726b37 --- /dev/null +++ b/app/services/token_bucket_rate_limiter.rb @@ -0,0 +1,48 @@ +class TokenBucketRateLimiter + def initialize(user_id,rate, capacity) + @rate = rate # 令牌发放速率(每秒发放的令牌数) + @capacity = capacity # 令牌桶容量 + @redis = $redis_cache + @key = "#{user_id}:TokenBucket" + end + + def refill + now = Time.now.to_f * 1000 + tokens = $redis_cache.hget(@key, 'tokens') + timestamp = $redis_cache.hget(@key, 'timestamp') + + if tokens.nil? + tokens = @capacity + timestamp = now + else + tokens = [@capacity, tokens.to_i + (now - timestamp.to_f) * @rate / 1000].min + timestamp = now + end + + $redis_cache.hset(@key, 'tokens', tokens) + $redis_cache.hset(@key, 'timestamp', timestamp) + end + + def allow_request + refill + tokens = @redis.hget(@key, 'tokens') + if !tokens.nil? && tokens.to_i >= 1 + @redis.hset(@key, 'tokens', tokens.to_i - 1) + return true + end + false + end +end + +=begin + #测试通过 + limiter = TokenBucketRateLimiter.new(user_id,10, 40) # 设置令牌发放速率为10,令牌桶容量为40 + 30.times do + if limiter.allow_request + puts "Allow" + else + puts "Reject" + end + end + +=end \ No newline at end of file