根据“内容类型”的 nginx 请求路由将 grpc 与 REST API 请求分离

根据“内容类型”的 nginx 请求路由将 grpc 与 REST API 请求分离

我有一个使用两个级联 nginx 实例的现有设置:

  1. 外层 nginx 只是进行 TLS 终止并转发到内层 nginx
  2. 内部 nginx 将请求路由到不同的应用程序(REST API)

到目前为止,我们只使用 HTML 页面和 REST API。外部 nginx 的路由非常简单,它主要提供静态 HTML,其他流量转发到内部 nginx。

现在我们还想支持 grpc API。grpc 的问题在于请求的路径由 grpc 接口协议定义,例如“acme.myapi/MyMethod”。nginx 不应依赖于该接口定义,因为当添加新的 grpc 接口时,外部 nginx 不需要更改,并且 REST API 和 grpc 之间的 url 路径甚至可以重叠,例如两者都可以以“/acme/”开头。此外,我们无法控制 proto 包名称的名称,因此也无法控制 url 路径。

grpc 路由nginx 使用:authorityheader 来路由到 grpc 应用程序。添加新应用程序时,配置会动态更新。

所有 grpc 调用都应重定向到内部 nginx 的特定端点(grpc://127.0.0.1:50051),REST API 调用应使用另一个端点(https://127.0.0.1:4443/) 内部 nginx 实例。

从 nginx 配置的角度来看,最简单的方法是对外部 nginx 使用两个不同的端点(不同的端口)。不幸的是,在防火墙中打开另一个端口的组织工作量非常大,并且不被接受,所以我需要对所有协议使用相同的端点。

这是我目前使用的配置,它可以解决上面描述的问题:

location ~ ^acme\. { # "acme" is the grpc packagename
    grpc_pass grpc://127.0.0.1:50051;
    grpc_set_header "Host" $host;
    grpc_read_timeout 1200s;
    grpc_send_timeout 1200s;
    client_body_timeout 1200s;
    client_max_body_size 0;
}
location / {
    proxy_pass https://127.0.0.1:4443/;
    [...] # setting lots of other options
}

这是我想要使用的配置(简化到最低限度),但根据如果是邪恶的…这不能使用:

location / {
  if ($http_content_type ~ ^application/grpc)
  {
    grpc_pass grpc://127.0.0.1:50051;
    grpc_set_header "Host" $host;
    grpc_read_timeout 1200s;
    grpc_send_timeout 1200s;
    client_body_timeout 1200s;
    client_max_body_size 0;
  }

  # normally I would use 'else' here, but this is not supported
  if ($http_content_type !~ ^application/grpc)
  {
    # also set other REST API specific config options here, left out for brevity
    proxy_pass https://127.0.0.1:4443/;
  }

我从 nginx 收到以下错误:

nginx: [emerg] "grpc_set_header" directive is not allowed here in /etc/nginx/conf.d/my.conf.locations:80

最后,我需要能够将其用作$http_content_type选择器来代替(或另外)使用 url 路径。

有没有什么好的解决方案可以满足这个要求?

答案1

我认为我找到了一个很好的解决方案,并且if只允许使用 for rewritewhich。我需要将任何条件映射到 URL 路径,因此我只能使用location语句来匹配它。在我的例子中,我将content-type ~ ^application/grpc标头映射到位置/grpc,并在location部分中删除/grpc前缀。

    if ($http_content_type ~ ^application/grpc) {
        rewrite "(.*)" "/grpc$1" break; # add "/grpc" to url
    }
    location /grpc/ {
        internal; # not reachable from outside, just for the rewritten location
        rewrite "^/grpc(.*)" "$1" break; # remove "/grpc" from url
        grpc_pass grpc://127.0.0.1:4080;
        #grpc_set_header ":authority" $host; # unfortunately this doesn't work
        grpc_set_header "Host" $host; # workaround because ":authority" can't be set
        grpc_read_timeout 3153600000s;
        grpc_send_timeout 3153600000s;
        client_body_timeout 3153600000s;
        client_max_body_size 0;
    }

REST API部分location不受影响。

第一次测试有效,但如果我遗漏了任何副作用,我需要深入研究。

相关内容