国产成人精品18p,天天干成人网,无码专区狠狠躁天天躁,美女脱精光隐私扒开免费观看

nginx作grpc的反向代理踩坑總結

發(fā)布時(shí)間:2021-08-15 18:37 來(lái)源: 閱讀:0 作者:不加香菜 欄目: 服務(wù)器 歡迎投稿:712375056

背景

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

(圖片來(lái)自維基百科)

正向代理和反向代理實(shí)際上是站在用戶(hù)的角度來(lái)定義的,正向也就是代理用戶(hù)所要請求的服務(wù),而反向則是代理用戶(hù)向服務(wù)發(fā)起請求。兩者一個(gè)很重要的區別:

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

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

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é)議的反向代理,配置類(lèi)似:

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;
        }
    }
}

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

場(chǎng)景

最開(kāi)始我們的場(chǎng)景很簡(jiǎn)單,通過(guò)gRPC協(xié)議實(shí)現一個(gè)簡(jiǎn)單的C/S架構:

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

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

解決辦法

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

通過(guò)路徑區分

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

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/;
        }
    }
}

注意最后的斜杠,如果沒(méi)有這個(gè)斜杠請求的路徑會(huì )是/server1/service/method,而服務(wù)端只能響應/service/method的請求,這樣就會(huì )報404的錯誤。

通過(guò)請求參數區分

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

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

注意到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;
        }
    }
}

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

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

抓包后發(fā)現,grpc_set_header并沒(méi)有覆蓋:path的結果,而是新增了一項請求頭,相當于請求header里存在兩個(gè):path,可能就是因為這個(gè)原因導致服務(wù)端報了502的錯誤。

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

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是根據請求屬性計算好的后端服務(wù)的ip
    var serverIp string
    con, err := grpc.Dial(nginxProxy, grpc.WithInsecure(),
        grpc.WithPerRPCCredentials(extraMetadata{Ip: serverIp}))
}

然后在nginx配置里根據這個(gè)metadata轉發(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這一語(yǔ)法引用了我們傳遞的x-ip這個(gè)metadata信息。這一方法驗證有效,client可以通過(guò)nginx proxy成功訪(fǎng)問(wèn)到server的gRPC服務(wù)。

總結

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

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

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自本網(wǎng)站內容采集于網(wǎng)絡(luò )互聯(lián)網(wǎng)轉載等其它媒體和分享為主,內容觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如侵犯了原作者的版權,請告知一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容,聯(lián)系我們QQ:712375056,同時(shí)歡迎投稿傳遞力量。

亚洲第一在线综合网站| 久久精品国产乱子伦| 婷停五月深爱五月| 欧美成人精品三级网站下载| 狠狠热精品免费视频| 中文字幕精品一区二区精品|