1. <ul id="0c1fb"></ul>

      <noscript id="0c1fb"><video id="0c1fb"></video></noscript>
      <noscript id="0c1fb"><listing id="0c1fb"><thead id="0c1fb"></thead></listing></noscript>

      99热在线精品一区二区三区_国产伦精品一区二区三区女破破_亚洲一区二区三区无码_精品国产欧美日韩另类一区

      RELATEED CONSULTING
      相關(guān)咨詢
      選擇下列產(chǎn)品馬上在線溝通
      服務(wù)時間:8:30-17:00
      你可能遇到了下面的問題
      關(guān)閉右側(cè)工具欄

      新聞中心

      這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
      nginx作grpc的反向代理踩坑總結(jié)

      背景

      為南漳等地區(qū)用戶提供了全套網(wǎng)頁設(shè)計制作服務(wù),及南漳網(wǎng)站建設(shè)行業(yè)解決方案。主營業(yè)務(wù)為成都網(wǎng)站制作、成都網(wǎng)站建設(shè)、南漳網(wǎng)站設(shè)計,以傳統(tǒng)方式定制建設(shè)網(wǎng)站,并提供域名空間備案等一條龍服務(wù),秉承以專業(yè)、用心的態(tài)度為用戶提供真誠的服務(wù)。我們深信只要達到每一位用戶的要求,就會得到認(rèn)可,從而選擇與我們長期合作。這樣,我們也可以走得更遠(yuǎn)!

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

      (圖片來自維基百科)

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

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

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

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

      場景

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

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

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

      解決辦法

      如果是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/; } }}

      注意最后的斜杠,如果沒有這個斜杠請求的路徑會是/server1/service/method,而服務(wù)端只能響應(yīng)/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的請求頭會導(dǎo)致服務(wù)端報錯,一種可能的錯誤如下:

      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é)果,而是新增了一項請求頭,相當(dāng)于請求header里存在兩個:path,可能就是因為這個原因?qū)е路?wù)端報了502的錯誤。

      山窮水盡之際想起gRPC的metadata功能,我們可以在client端將server的信息存儲在metadata中,然后在nginx路由時根據(jù)metadata中server的信息轉(zhuǎn)發(fā)給對應(yīng)的后端服務(wù),這樣就實現(xiàn)了我們的需求。對于go語言,設(shè)置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ù)請求屬性計算好的后端服務(wù)的ip var serverIp string con, err := grpc.Dial(nginxProxy, grpc.WithInsecure(), grpc.WithPerRPCCredentials(extraMetadata{Ip: serverIp}))}

      然后在nginx配置里根據(jù)這個metadata轉(zhuǎn)發(fā)到對應(yīng)的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服務(wù)。

      總結(jié)

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

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


      文章題目:nginx作grpc的反向代理踩坑總結(jié)
      轉(zhuǎn)載來于:http://ef60e0e.cn/article/idoisi.html
      99热在线精品一区二区三区_国产伦精品一区二区三区女破破_亚洲一区二区三区无码_精品国产欧美日韩另类一区
      1. <ul id="0c1fb"></ul>

        <noscript id="0c1fb"><video id="0c1fb"></video></noscript>
        <noscript id="0c1fb"><listing id="0c1fb"><thead id="0c1fb"></thead></listing></noscript>

        普兰店市| 玉田县| 宜兴市| 万州区| 万源市| 茶陵县| 泰和县| 金平| 定安县| 锡林郭勒盟| 文昌市| 富平县| 东乡县| 佛山市| 舒兰市| 张家界市| 棋牌| 青州市| 克什克腾旗| 德庆县| 东台市| 洪江市| 全南县| 贵德县| 桑植县| 滨海县| 汾阳市| 泸水县| 长垣县| 镶黄旗| 楚雄市| 交城县| 建瓯市| 汽车| 西贡区| 应城市| 阜康市| 万荣县| 蚌埠市| 长兴县| 抚州市|