AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / server / 问题 / 432636
Accepted
Quintin Par
Quintin Par
Asked: 2012-09-28 08:52:04 +0800 CST2012-09-28 08:52:04 +0800 CST 2012-09-28 08:52:04 +0800 CST

在 nginx 中检测 Slashdot 效果

  • 772

有没有办法让 Nginx 在来自推荐人的点击量超过阈值时通知我?

例如,如果我的网站在 Slashdot 上有特色,并且突然间我在一小时内有 2K 的点击量,我想在一小时的点击量超过 1K 时收到通知。

是否可以在 Nginx 中执行此操作?可能没有lua?(因为我的产品不是lua编译的)

nginx
  • 4 4 个回答
  • 826 Views

4 个回答

  • Voted
  1. Ladadadada
    2012-09-28T08:58:43+08:002012-09-28T08:58:43+08:00

    我认为使用 logtail 和 grep 会更好。即使可以使用 lua 内联,您也不希望每个请求都有这样的开销,尤其是当您被 Slashdotted 时,您更不希望这样做。

    这是一个 5 秒的版本。将其粘贴到脚本中并在其周围放置一些更具可读性的文本,您就成功了。

    5 * * * * logtail -f /var/log/nginx/access_log -o /tmp/nginx-logtail.offset | grep -c "http://[^ ]slashdot.org"
    

    当然,这完全忽略了 reddit.com 和 facebook.com 以及所有其他可以为您带来大量流量的网站。更不用说 100 个不同的站点,每个站点都会向您发送 20 个访问者。您可能应该只有一个普通的旧流量阈值,它会导致向您发送电子邮件,而不管引荐来源。

    • 13
  2. rmalayter
    2012-09-28T09:41:27+08:002012-09-28T09:41:27+08:00

    nginx limit_req_zone指令可以将其区域基于任何变量,包括 $http_referrer。

    http {
        limit_req_zone  $http_referrer  zone=one:10m   rate=1r/s;
    
        ...
    
        server {
    
            ...
    
            location /search/ {
                limit_req   zone=one  burst=5;
            }
    

    不过,您还需要做一些事情来限制 Web 服务器上所需的状态量,因为引荐来源标头可能非常长且多种多样,您可能会看到无穷无尽的变化。您可以使用 nginx split_clients功能为所有基于 referrer 标头哈希的请求设置一个变量。下面的示例仅使用 10 美元,但您可以同样轻松地使用 1000 美元。因此,如果您被 slashdotted,其引荐来源恰好哈希到与 slashdot URL 相同的存储桶中的人也会被阻止,但您可以通过在 split_clients 中使用 1000 个存储桶来将其限制为 0.1% 的访问者。

    它看起来像这样(完全未经测试,但方向正确):

    http {
    
    split_clients $http_referrer $refhash {
                   10%               x01;
                   10%               x02;
                   10%               x03;
                   10%               x04;
                   10%               x05;
                   10%               x06;
                   10%               x07;
                   10%               x08;
                   10%               x09;
                   *                 x10;
                   }
    
    limit_req_zone  $refhash  zone=one:10m   rate=1r/s;
    
    ...
    
    server {
    
        ...
    
        location /search/ {
            limit_req   zone=one  burst=5;
        }
    
    • 4
  3. Best Answer
    cnst
    2017-08-31T17:37:11+08:002017-08-31T17:37:11+08:00

    最有效的解决方案可能是编写一个守护程序来跟踪该tail -f字段。access.log$http_referer

    然而,一个快速而肮脏的解决方案是添加一个额外的access_log文件,只记录$http_referer带有自定义的变量log_format,并每 X 分钟自动轮换一次日志。

    • 这可以在标准 logrotate 脚本的帮助下完成,它可能需要正常重启 nginx 以便重新打开文件(例如,标准程序,看一下SO 上的 /a/15183322一段时间-基于脚本)…

    • 或者,通过在 中使用变量,可能通过在或指令的帮助下access_log获取分钟规范(取决于您想要放置 的位置)。$time_iso8601mapifaccess_log

    因此,对于上面的内容,您可能有 6 个日志文件,每个日志文件涵盖 10 分钟的时间段http_referer.Txx{0,1,2,3,4,5}x.log,例如,通过获取分钟的第一位数字来区分每个文件。

    现在,您所要做的就是拥有一个可以每 10 分钟运行一次的简单 shell 脚本,cat将上述所有文件放在一起,通过管道传输到sort,通过管道传输到uniq -c,到sort -rn,到head -16,然后您就有了 16 种最常见Referer变体的列表— 自由决定数字和字段的任何组合是否超出您的标准,并执行通知。

    随后,在一次成功通知后,您可以删除所有这 6 个文件,并且在后续运行中,除非所有六个文件都存在(和/或您认为合适的某个其他数量),否则不会发出任何通知。

    • 3
  4. cnst
    2017-08-26T16:38:19+08:002017-08-26T16:38:19+08:00

    是的,在 NGINX 中当然可以!

    您可以做的是实施以下DFA:

    1. 基于 实施速率限制,$http_referer可能通过 a 使用一些正则表达式map来规范化值。当超过限制时,将引发内部错误页面,您可以根据相关问题error_page通过处理程序捕获该页面,将其作为内部重定向转到新的内部位置(客户端不可见)。

    2. 在上述超出限制的位置,您执行警报请求,让外部逻辑执行通知;该请求随后被缓存,确保您在每个给定时间窗口只会收到 1 个唯一请求。

    3. 捕获先前请求的 HTTP 状态代码(通过返回状态代码 ≥ 300 并使用proxy_intercept_errors on,或者,使用默认未构建auth_request或add_after_body发出“免费”子请求),并完成原始请求,就好像不涉及先前的步骤。请注意,我们需要启用递归error_page处理才能使其工作。

    这是我的 PoC 和 MVP,也在https://github.com/cnst/StackOverflow.cnst.nginx.conf/blob/master/sf.432636.detecting-slashdot-effect-in-nginx.conf:

    limit_req_zone $http_referer zone=slash:10m rate=1r/m;  # XXX: how many req/minute?
    server {
        listen 2636;
        location / {
            limit_req zone=slash nodelay;
            #limit_req_status 429;  #nginx 1.3.15
            #error_page 429 = @dot;
            error_page 503 = @dot;
            proxy_pass http://localhost:2635;
            # an outright `return 200` has a higher precedence over the limit
        }
        recursive_error_pages on;
        location @dot {
            proxy_pass http://127.0.0.1:2637/?ref=$http_referer;
            # if you don't have `resolver`, no URI modification is allowed:
            #proxy_pass http://localhost:2637;
            proxy_intercept_errors on;
            error_page 429 = @slash;
        }
        location @slash {
            # XXX: placeholder for your content:
            return 200 "$uri: we're too fast!\n";
        }
    }
    server {
        listen 2635;
        # XXX: placeholder for your content:
        return 200 "$uri: going steady\n";
    }
    proxy_cache_path /tmp/nginx/slashdotted inactive=1h
            max_size=64m keys_zone=slashdotted:10m;
    server {
        # we need to flip the 200 status into the one >=300, so that
        # we can then catch it through proxy_intercept_errors above
        listen 2637;
        error_page 429 @/.;
        return 429;
        location @/. {
            proxy_cache slashdotted;
            proxy_cache_valid 200 60s;  # XXX: how often to get notifications?
            proxy_pass http://localhost:2638;
        }
    }
    server {
        # IRL this would be an actual script, or
        # a proxy_pass redirect to an HTTP to SMS or SMTP gateway
        listen 2638;
        return 200 authorities_alerted\n;
    }
    

    请注意,这按预期工作:

    % sh -c 'rm /tmp/slashdotted.nginx/*; mkdir /tmp/slashdotted.nginx; nginx -s reload; for i in 1 2 3; do curl -H "Referer: test" localhost:2636; sleep 2; done; tail /var/log/nginx/access.log'
    /: going steady
    /: we're too fast!
    /: we're too fast!
    
    127.0.0.1 - - [26/Aug/2017:02:05:49 +0200] "GET / HTTP/1.1" 200 16 "test" "curl/7.26.0"
    127.0.0.1 - - [26/Aug/2017:02:05:49 +0200] "GET / HTTP/1.0" 200 16 "test" "curl/7.26.0"
    
    127.0.0.1 - - [26/Aug/2017:02:05:51 +0200] "GET / HTTP/1.1" 200 19 "test" "curl/7.26.0"
    127.0.0.1 - - [26/Aug/2017:02:05:51 +0200] "GET /?ref=test HTTP/1.0" 200 20 "test" "curl/7.26.0"
    127.0.0.1 - - [26/Aug/2017:02:05:51 +0200] "GET /?ref=test HTTP/1.0" 429 20 "test" "curl/7.26.0"
    
    127.0.0.1 - - [26/Aug/2017:02:05:53 +0200] "GET / HTTP/1.1" 200 19 "test" "curl/7.26.0"
    127.0.0.1 - - [26/Aug/2017:02:05:53 +0200] "GET /?ref=test HTTP/1.0" 429 20 "test" "curl/7.26.0"
    %
    

    您可以看到第一个请求导致一个前端和一个后端命中,正如预期的那样(我不得不向具有 的位置添加一个虚拟后端limit_req,因为 areturn 200会优先于限制,所以不需要真正的后端其余的处理)。

    第二个请求超出了限制,所以,我们发送警报(getting 200),并缓存它,返回429(这是必要的,因为前面提到的限制不能捕获低于 300 的请求),随后被前端捕获,现在是免费的,可以自由地做任何想做的事。

    第三个请求仍然超出限制,但我们已经发送了警报,因此不会发送新的警报。

    完毕! 不要忘记在 GitHub 上分叉它!

    • 2

相关问题

  • Gzip 与反向代理缓存

  • nginx 作为代理的行为

  • Nginx 学习资源 [关闭]

  • 提供 70,000 个静态文件 (jpg) 的最佳方式?

  • 在 Apache、LightTPD 和 Nginx Web 服务器上提供 PHP 5.x 应用程序的现状?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    新安装后 postgres 的默认超级用户用户名/密码是什么?

    • 5 个回答
  • Marko Smith

    SFTP 使用什么端口?

    • 6 个回答
  • Marko Smith

    命令行列出 Windows Active Directory 组中的用户?

    • 9 个回答
  • Marko Smith

    什么是 Pem 文件,它与其他 OpenSSL 生成的密钥文件格式有何不同?

    • 3 个回答
  • Marko Smith

    如何确定bash变量是否为空?

    • 15 个回答
  • Martin Hope
    Tom Feiner 如何按大小对 du -h 输出进行排序 2009-02-26 05:42:42 +0800 CST
  • Martin Hope
    Noah Goodrich 什么是 Pem 文件,它与其他 OpenSSL 生成的密钥文件格式有何不同? 2009-05-19 18:24:42 +0800 CST
  • Martin Hope
    Brent 如何确定bash变量是否为空? 2009-05-13 09:54:48 +0800 CST
  • Martin Hope
    cletus 您如何找到在 Windows 中打开文件的进程? 2009-05-01 16:47:16 +0800 CST

热门标签

linux nginx windows networking ubuntu domain-name-system amazon-web-services active-directory apache-2.4 ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve