【问题标题】:Can a http request be sent with the nginx location directive?可以使用 nginx 位置指令发送 http 请求吗?
【发布时间】:2018-12-30 04:27:46
【问题描述】:

也许这是微不足道的,但我没有找到任何有意义的东西或者我不知道去哪里找......

(如何)是否可以在请求某个路径后立即发送 curl /任何命令?

类似的东西,但实际上会起作用:

location / {
curl --data 'v=1&t=pageview&tid=UA-XXXXXXXX-X&cid=123&dp=hit'  https://google-analytics.com/collect
}

【问题讨论】:

  • 我不知道(如何)这是否可以使用“纯”nginx 来完成,但可以为您提供有关如何使用OpenResty(或ngx_http_lua_module)执行此操作的秘诀。是您的选择。
  • 如果它完成了工作,为什么不

标签: http nginx curl measurement-protocol


【解决方案1】:

(正如 cmets 中指出的那样),ngx_http_lua_module 可以做到!

location / {
          access_by_lua_block  {
            os.execute("/usr/bin/curl --data 'v=1&t=pageview&tid=UA-XXXXXXXX-X&cid=123&dp=hit'  https://google-analytics.com/collect >/dev/null 2>/dev/null") 
        }
}

请注意,执行会暂停页面加载,直到 curl 完成。要在后台运行 curl 并立即继续页面加载,请在末尾添加一个空格和一个 &,使其看起来像

>/dev/null 2>/dev/null &")

【讨论】:

  • 是的,它正在工作!必须安装 openresty 并且现在不再支持 http/2 - 希望他们很快会发布基于 nginx >1.13.9 的版本......有什么方法可以将现有的标头作为参数传递给它?
  • @LucianDavidescu 老实说,我不敢相信这个解决方案甚至被认为是可以接受的,即使是用于“测试”目的,更不用说任何类型的生产环境了。在后台生成一个新进程,同时立即返回客户端,这使得使用单个 TCP 连接的单个客户端在几秒钟内完全关闭您的整个机器变得微不足道,是的,您的整个机器,通过一个微不足道的进程表的耗尽和过载。这个答案中提出的解决方案与 forkbomb! 几乎没有什么不同
  • @LucianDavidescu,它可以是任何东西。如果 DNS 已关闭,或者 Google 决定限制您,或者 IPv6 已配置但无法正常工作,该怎么办?每个 curl 实例将持续 3 分钟以上,每个请求会持续更多分钟。您将无法使用 shell 登录系统,因为进程表已用尽。最好的选择是,如果您使用的是共享主机,并且/或者 fork 很慢(而且确实如此),并且每秒只能进行 20 到 100 个 fork,这会占用您 2/3 的 CPU 功率,从而减慢速度您网站的其余部分。我认为您还没有完全意识到分叉有多昂贵。
  • @LucianDavidescu 似乎您可以通过运行local headers, err = ngx.resp.get_headers(); 获得一组请求标头(由浏览器发送),并通过使用local headers, err = ngx.req.get_headers() 获得一组响应标头(由 nginx 发送) - 但是您可能应该使用log_by_lua_block 而不是access_by_lua_block
  • 找到了这个 - github.com/vorodevops/nginx-analytics-measurement-protocol/tree/… 它使用了 proxy_pass,到目前为止工作得很好。
【解决方案2】:

您尝试做的事情——在服务器上的每个 URL 请求上为 Google Analytics 执行一个新的 curl 实例——是解决问题的错误方法:

  1. Nginx 本身可以轻松地在任何给定时间为 10k+ 并发连接提供服务作为下限,即,作为最低限度,如果你做对了,请参阅https://en.wikipedia.org/wiki/C10k_problem

  2. 另一方面,fork(创建新进程的底层 system call)的性能非常慢,如果您想为每个请求运行 curl每秒 1k 个分叉的顺序作为上限,例如,如果你做对了,那将是最快的,请参阅 Faster forking of large processes on Linux?


具有更好架构的最佳替代解决方案是什么?

  • 我的建议是通过批处理来执行此操作。通过实时执行 Google Analytics,您并没有真正获得任何收益,统计数据延迟 5 分钟应该绰绰有余。您可以使用您选择的编程语言编写一个简单的脚本来查看相关的http://nginx.org/r/access_log,收集所需时间段的数据,并发出单个批处理请求(和/或单个进程中的多个单独请求)以谷歌分析,在最后 5 分钟内提供每位访客的必要信息。您可以将其作为守护进程运行,也可以作为来自cron 作业的脚本运行,请参阅crontab(5)crontab(1)

  • 或者,如果您仍然想要 Google Analytics 的实时处理(我不建议这样做,因为这些服务中的大多数本身都是在最终一致性的基础上实现的,也就是说,GA 本身不会'不一定保证最后 XX 秒/分钟/小时/等的准确实时统计信息),那么您可能需要实现某种守护进程来实时处理统计信息:

    • 我的建议仍然是在此类守护进程中使用access_log,例如,通过您最喜欢的编程语言中的tail -f /var/www/logs/access_log 等效项,您可以将access_log 文件作为流打开,及时处理数据。

    • 或者,您可以实现此守护程序以使其本身具有 HTTP 请求接口,并将每个传入请求复制到您的实际后端以及这个额外的服务器。 您可以借助非默认构建 auth_requestadd_after_body 通过 nginx 多路复用它,以便为每个请求创建一个“免费”子请求。此子请求将发送到您的服务器,例如,以Go 编写。服务器将至少有两个goroutines:一个将传入的请求处理到一个队列中(通过缓冲的字符串通道实现),立即向客户端发出回复,以确保不会延迟 nginx 上游;另一个将通过chan string 接收来自第一个的请求,同时处理它们并将适当的请求发送到 Google Analytics。

最终,无论您采用哪种方式,您可能仍希望实施某种程度的批处理和/或限制,因为我曾想象过,如果您继续发送它,Google Analytics(分析)本身可能会受到限制来自同一 IP 地址的请求非常多,而没有任何类型的批处理实现。根据 What is the rate limit for direct use of the Google Analytics Measurement Protocol API?https://developers.google.com/analytics/devguides/collection/protocol/v1/limits-quotas,似乎大多数库都对每秒向 Google 发送的请求数量实施了内部限制。

【讨论】:

  • 确实,这似乎是可扩展的长期解决方案。然而,一方面,对于大多数用例来说,同时连接和速率限制都相当高(除非还有性能问题),另一方面,我认为“快速而肮脏”的方法可能会派上用场,至少有用用于测试目的。
  • 顺便说一句,我写了一些代码来解析 PHP 中的 nginx 访问日志,请参见此处的第 58 行 github.com/divinity76/http_log_parser/blob/master/…(但该代码来自 2015 年且未维护,如果自 2015 年以来有任何更改,请注明)
【解决方案3】:

如果您只需要向 Google Analytics 提交点击,那么它可以更轻松地完成:Nginx 可以动态修改页面 HTML,在结束 </body> 标记之前嵌入 GA 代码:

sub_filter_once on;

sub_filter '</body>' "<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');

ga('create', 'UA-XXXXXXXX-X', 'auto');
ga('send', 'pageview');
</script></body>";

location / {
}

这个 Nginx 模块被称为sub

【讨论】:

  • 碰巧包含短语&lt;/body&gt;的非html文件会发生什么?例如 XML 文件?
  • @hanshenrik 它是可配置的。默认配置是在文件中替换为text/html MIME类型,其他的可以允许。
  • 这并不是说我无法访问该网站以将 javascript 放在首位,如果那是我想做的...
【解决方案4】:

这就是我最终的做法 - proxy_pass 而不是 curl - 基于此:https://github.com/vorodevops/nginx-analytics-measurement-protocol/tree/master/lua。该代码假定安装了 openresty 或仅安装了 lua。不确定 cmets 格式是否兼容(未测试),因此最好在使用之前将其删除。

# pick your location 

location /example {

    # invite lua to the party

    access_by_lua_block  {

        # set request parameters

        local request = {
            v = 1,
            t = "pageview",

            # don' forget to put your own property here

            tid = "UA-XXXXXXX-Y",

            # this is a "unique" user id based on a hash of ip and user agent, not too reliable but possibly best that one can reasonably do without cookies

            cid = ngx.md5(ngx.var.remote_addr .. ngx.var.http_user_agent),
            uip = ngx.var.remote_addr,
            dp = ngx.var.request_uri,
            dr = ngx.var.http_referer,
            ua = ngx.var.http_user_agent,

            # here you truncate the language string to make it compatible with the javascript format - you'll want either the first two characters like here (e.g. en) or the first five (e.g en_US) with ...1, 5

            ul = string.sub(ngx.var.http_accept_language, 1, 2)
            }

        # use the location.capture thingy to send everything to a proxy

        local res = ngx.location.capture(  "/gamp",  {
        method = ngx.HTTP_POST,
        body = ngx.encode_args(request)
        })
    }
}


# make a separate location block to proxy the request away

location = /gamp {
    internal;
    expires epoch;
    access_log off;
    proxy_pass_request_headers off;
    proxy_pass_request_body on;
    proxy_pass https://google-analytics.com/collect;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-03-21
    • 2012-01-16
    • 2023-03-12
    • 2016-10-24
    • 2012-12-17
    • 2017-10-05
    • 2011-08-21
    • 2015-12-06
    相关资源
    最近更新 更多