欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

nginx作grpc的反向代理踩坑總結(jié)

 更新時間:2021年07月01日 08:42:13   作者:不加香菜  
nginx是一款高性能的web服務器,常用于負載均衡和反向代理,本文主要介紹了nginx作grpc的反向代理踩坑總結(jié),感興趣的可以了解一下

背景

眾所周知,nginx是一款高性能的web服務器,常用于負載均衡和反向代理。所謂的反向代理是和正向代理相對應,正向代理即我們常規(guī)意義上理解的“代理”:例如正常情況下在國內(nèi)是無法訪問google的,如果我們需要訪問,就需要通過一層代理去轉(zhuǎn)發(fā)。這個正向代理代理的是服務端(也就是google),而反向代理則相反,代理的是客戶端(也就是用戶),用戶的請求到達nginx后,nginx會代理用戶的請求向?qū)嶋H的后端服務發(fā)起請求,并將結(jié)果返回給用戶。

(圖片來自維基百科)

正向代理和反向代理實際上是站在用戶的角度來定義的,正向也就是代理用戶所要請求的服務,而反向則是代理用戶向服務發(fā)起請求。兩者一個很重要的區(qū)別:

正向代理服務方不感知請求方,反向代理請求方不感知服務方。
思考一下上面的例子,你通過代理訪問google時,google只能感知到請求來自代理服務器,而無法直接感知到你(當然通過cookie等手段也可以追蹤到);而通過nginx反向代理時,你是不感知請求具體被轉(zhuǎn)發(fā)到哪個后端服務器上的。

nginx最常被用于反向代理的場景就是我們所熟知的http協(xié)議,通過配置nginx.conf文件可以很簡單地定義一個反向代理規(guī)則:

worker_processes  1;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    server {
        listen       80;
        server_name  localhost;

        
        location / {
            proxy_pass http://domain;
        }
    }
}

nginx從1.13.10以后就支持gRPC協(xié)議的反向代理,配置類似:

worker_processes  1;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    server {
        listen       81 http2;
        server_name  localhost;

        
        location / {
            grpc_pass http://ip;
        }
    }
}

但是當需求場景更加復雜的時候,就發(fā)現(xiàn)nginx的gRPC模塊實際上有很多坑,實現(xiàn)的能力不如http完整,當套用http的解決方案時就會出現(xiàn)問題

場景

最開始我們的場景很簡單,通過gRPC協(xié)議實現(xiàn)一個簡單的C/S架構(gòu):

但這種單純的直連有些場景下是不可行的,例如client和server在兩個網(wǎng)絡環(huán)境下,彼此不相連通,那就無法通過簡單的gRPC連接訪問服務。一種解決辦法是通過中間的代理服務器轉(zhuǎn)發(fā),用上面說的nginx反向代理gRPC方法:

nginx proxy部署在兩個環(huán)境都能訪問的集群上,這樣就實現(xiàn)了跨網(wǎng)絡環(huán)境的gRPC訪問。隨之而來的問題是如何配置這個路由規(guī)則?注意我們最開始的gRPC的目標節(jié)點都是清晰的,也就是server1和server2的ip地址,當中間加了一層nginx proxy后,client發(fā)起的gRPC請求的對象都是nginx proxy的ip地址。那client與nginx建立連接后,nginx如何知道需要將請求轉(zhuǎn)發(fā)給server1還是server2呢?(這里server1和server2不是簡單的同一個服務的冗備部署,可能需要根據(jù)請求的屬性決定由誰響應,例如用戶id等,因此不能使用負載均衡隨機挑選一個響應請求)

解決辦法

如果是http協(xié)議,那有很多實現(xiàn)方法:

通過路徑區(qū)分

請求將server的信息添加在path里,例如:/server1/service/method,然后nginx轉(zhuǎn)發(fā)請求的時候還原為原始的請求:

worker_processes  1;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    server {
        listen       80;
        server_name  localhost;

        location ~ ^/server1/ {
            proxy_pass http://domain1/;
        }
        
        location ~ ^/server2/ {
            proxy_pass http://domain2/;
        }
    }
}

注意http://domain/最后的斜杠,如果沒有這個斜杠請求的路徑會是/server1/service/method,而服務端只能響應/service/method的請求,這樣就會報404的錯誤。

通過請求參數(shù)區(qū)分

也可以將server1的信息放在請求參數(shù)里:

worker_processes  1;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    server {
        listen       80;
        server_name  localhost;

        location /service/method {
            if ($query_string ~ x_server=(.*)) {
                proxy_pass http://$1;
            }
        }
    }
}

但對于gRPC就沒這么簡單了,首先gRPC不支持URI的寫法,nginx轉(zhuǎn)發(fā)的請求會保留原來的path,無法在轉(zhuǎn)發(fā)的時候修改path,這意味著上述的第一種辦法不可行。其次gRPC是基于HTTP 2.0協(xié)議的,HTTP2沒有queryString這一概念,請求頭里有一項:path代表請求的路徑,例如/service/method,而這一路徑是不能攜帶請求參數(shù)的,也就是:path不能寫為/service/method?server=server1。這意味著上述的第二種方法也不可行。

注意到HTTP2中請求頭:path是指定請求的路徑的,那我們直接修改:path不就行了嗎:

worker_processes  1;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    server {
        listen       80 http2;
        server_name  localhost;

        location ~ ^/(.*)/service/.* {
            grpc_set_header :path /service/$2;
            grpc_pass http://$1;
        }
    }
}

但是實際驗證表明這種方法也不可行,直接修改:path的請求頭會導致服務端報錯,一種可能的錯誤如下:

rpc error: code = Unavailable desc = Bad Gateway: HTTP status code 502; transport: received the unexpected content-type "text/html"

抓包后發(fā)現(xiàn),grpc_set_header并沒有覆蓋:path的結(jié)果,而是新增了一項請求頭,相當于請求header里存在兩個:path,可能就是因為這個原因?qū)е路斩藞罅?02的錯誤。

山窮水盡之際想起gRPC的metadata功能,我們可以在client端將server的信息存儲在metadata中,然后在nginx路由時根據(jù)metadata中server的信息轉(zhuǎn)發(fā)給對應的后端服務,這樣就實現(xiàn)了我們的需求。對于go語言,設置metadata需要實現(xiàn)PerRPCCredentials接口,然后在發(fā)起連接的時候傳入這個實現(xiàn)類的實例:

type extraMetadata struct {
    Ip string
}

func (c extraMetadata) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
    return map[string]string{
        "x-ip": c.Ip,
    }, nil
}

func (c extraMetadata) RequireTransportSecurity() bool {
    return false
}

func main(){
    ...
    // nginxProxy是nginx proxy的ip或域名地址
    var nginxProxy string
    // serverIp是根據(jù)請求屬性計算好的后端服務的ip
    var serverIp string
    con, err := grpc.Dial(nginxProxy, grpc.WithInsecure(),
        grpc.WithPerRPCCredentials(extraMetadata{Ip: serverIp}))
}

然后在nginx配置里根據(jù)這個metadata轉(zhuǎn)發(fā)到對應的server:

worker_processes  1;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    server {
        listen       80 http2;
        server_name  localhost;

        location ~ ^/service/.* {
            grpc_pass grpc://$http_x_ip:8200;
        }
    }
}

注意這里使用了$http_x_ip這一語法引用了我們傳遞的x-ip這個metadata信息。這一方法驗證有效,client可以通過nginx proxy成功訪問到server的gRPC服務。

總結(jié)

nginx的gRPC模塊的文檔太少了,官方文檔只給出了幾個指令的用途,并沒有說明metadata這一方法,網(wǎng)上的文檔也鮮有涉及,導致花了兩三天的時間在排查。將整個過程總結(jié)在這里,希望能幫助到遇到同一問題的人。

到此這篇關于nginx作grpc的反向代理踩坑總結(jié)的文章就介紹到這了,更多相關nginx grpc反向代理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 詳解Nginx服務器之負載均衡策略(6種)

    詳解Nginx服務器之負載均衡策略(6種)

    這篇文章主要介紹了詳解Nginx服務器之負載均衡策略(6種),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-07-07
  • Nginx 禁止訪問某個目錄或文件的設置方法

    Nginx 禁止訪問某個目錄或文件的設置方法

    如果基于WEB根目錄下,要禁止用戶訪問/config目錄,或者要禁止用戶訪問/config.ini(ZF常用INI,不過建議還是放到WEB目錄以外的地方),可以通過location進行配置,返回403或者404等
    2010-09-09
  • Centos7.3 安裝部署Nginx并配置https的方法步驟

    Centos7.3 安裝部署Nginx并配置https的方法步驟

    這篇文章主要介紹了Centos7.3 安裝部署Nginx并配置https的方法步驟,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-04-04
  • nginx高可用集群的實現(xiàn)過程

    nginx高可用集群的實現(xiàn)過程

    這篇文章主要介紹了nginx高可用集群的實現(xiàn)過程,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-10-10
  • kubernetes啟用PHP+Nginx網(wǎng)頁環(huán)境教程

    kubernetes啟用PHP+Nginx網(wǎng)頁環(huán)境教程

    這篇文章主要介紹了kubernetes啟用PHP+Nginx網(wǎng)頁環(huán)境教程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-10-10
  • 解讀Nginx和Apache的特點與區(qū)別

    解讀Nginx和Apache的特點與區(qū)別

    這篇文章主要介紹了解讀Nginx和Apache的特點與區(qū)別,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • nginx中proxy_pass各種用法詳解

    nginx中proxy_pass各種用法詳解

    nginx中配置location代理轉(zhuǎn)發(fā)規(guī)則的時候不同寫法對應不同轉(zhuǎn)發(fā)規(guī)則。本文就介紹幾種常見的匹配情況,感興趣的可以了解一下
    2021-11-11
  • 淺析nginx剛剛發(fā)布的JavaScript能力nginScript

    淺析nginx剛剛發(fā)布的JavaScript能力nginScript

    Nginx [engine x]是全球最受歡迎,也是最優(yōu)秀的web服務器、反向代理服務器。nginScript是JavaScript/ECMAscript的子集,nginScript不是通過V8引擎實現(xiàn)的。本文給大家介紹nginx剛剛發(fā)布的JavaScript能力nginScript,感興趣的朋友跟著小編一起了解了解吧
    2015-09-09
  • 詳解Nginx + Tomcat 反向代理 負載均衡 集群 部署指南

    詳解Nginx + Tomcat 反向代理 負載均衡 集群 部署指南

    Nginx是一種服務器軟件,也是一種高性能的http和反向代理服務器,本篇文章主要介紹了Nginx + Tomcat 反向代理 負載均衡 集群 部署指南,有興趣的可以了解一下。
    2016-12-12
  • Ansible批量部署Nginx的示例代碼

    Ansible批量部署Nginx的示例代碼

    這篇文章主要介紹了Ansible批量部署Nginx的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-10-10

最新評論