亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費注冊 查看新帖 |

Chinaunix

  平臺 論壇 博客 文庫
最近訪問板塊 發(fā)新帖
查看: 2079 | 回復: 0
打印 上一主題 下一主題

[Web] Nginx location 配置踩坑過程分享 [復制鏈接]

論壇徽章:
0
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報告]
發(fā)表于 2015-02-03 14:13 |只看該作者 |倒序瀏覽
本帖最后由 Coding扣釘 于 2015-02-03 14:14 編輯

這是五個小時與一個字符的戰(zhàn)斗

是的,作為一個程序員,你往往發(fā)現(xiàn),有的時候你花費了數(shù)小時,數(shù)天,甚至數(shù)星期來查找問題,但最終可能只花費了數(shù)秒,改動了數(shù)行,甚至幾個字符就解決了問題。這次給大家分享一個困擾了我很久,我花了五個小時才查找出問題原因,最終只添加了一個字符解決了的問題。

問題描述

我們的業(yè)務系統(tǒng)比較復雜,但最終提供給用戶的訪問接口比較單一,都是使用 Nginx 來做一個代理轉(zhuǎn)發(fā),而這個代理轉(zhuǎn)發(fā),往往需要匹配很多種不同類型的 URL 轉(zhuǎn)給不同的服務。這就使得我們的 Nginx 配置文件變得很復雜,粗略估計了下,我們有近20個 upstream,有近60個 location 匹配。這些配置按照模塊分布在不同的文件中,雖然復雜,但是仍然在我們的努力下運行的良好。直到有一天,有位同事給我反映說偶爾有些 URL 會出現(xiàn) 404 的問題。一開始沒太在意,因為他也說不準是哪一種 URL 才遇到這個問題。

問題查找

后來,慢慢的查找,找到了一些規(guī)律,一開始只知道是 tomcat 那邊返回 404了,想到 Nginx 都代理給了 tomcat,一開始就懷疑是程序的問題,不會想到是 Nginx。

我開始查找代碼的問題,我在本地的開發(fā)環(huán)境,嘗試了很久,我使用 8080 端口訪問,不論如何都是正確的結(jié)果,可是生產(chǎn)環(huán)境就是不行。然后我就聽信了某坑友同事的理論,重啟解決 95% 的問題,重裝解決 100%的問題,我嘗試重啟了 tomcat 和 Nginx,依然不行,然后是重裝,你猜結(jié)果如何????? ——想啥呢?當然也是不行!

后來就開始懷疑是生產(chǎn)環(huán)境和開發(fā)環(huán)境的差異,去服務器上訪問 8080 端口,仍然是可以的?墒且唤(jīng)過 Nginx 代理,就不行。這個時候才開始懷疑是 Nginx 出了什么問題。

Nginx 怎么會出問題呢,業(yè)務系統(tǒng)中 URL 模式 /helloworld/* ,這樣的 URL 我們都是統(tǒng)一處理的。怎么會出現(xiàn)一些行,一些不行呢。問題表現(xiàn)為 A URL (/helloworld/nn/hello/world)沒問題,而 B URL(/helloworld/ii/hello/world) 有問題。

所以到目前為止,基本可以肯定是 Nginx 的 location 上出了一些問題。

問題解決

因篇幅有限,為了直面本次問題的核心,我不再貼出完整的 Nginx 配置,我簡化此次問題的模型。請看如下 Nginx 配置,這是我們之前的會導致問題的錯誤配置模型。
  1. worker_processes  1;
  2. error_log  logs/error.log;
  3. events {
  4.     worker_connections  1024;
  5. }

  6. http {
  7.     include       mime.types;
  8.     default_type  application/octet-stream;
  9.     log_format  main  '$remote_addr - $request_time - $remote_user [$time_local] "$request" '
  10.                       '$status $body_bytes_sent "$http_referer" '
  11.                       '"$http_user_agent" "$http_x_forwarded_for"';

  12.     access_log  logs/access.log main;

  13.     sendfile        on;
  14.     keepalive_timeout  65;

  15.     gzip  on;

  16.     server {
  17.         listen       80;
  18.         server_name  localhost;
  19.         location / {
  20.             root   html;
  21.             index  index.html index.htm;
  22.         }
  23.         location = /helloworld {
  24.                 return 602;
  25.         }
  26.         location /helloworld {
  27.                 return 603;
  28.         }
  29.         
  30.         ## 生產(chǎn)環(huán)境中如下兩個 location 在另外一個文件中,通過 include 包含進來
  31.         location /ii {
  32.                 return 604;
  33.         }
  34.         location ~ /ii/[^\/]+/[^\/]+ {
  35.                 return 605;
  36.         }
  37.         ##
  38.         
  39.         location ~ ^/helloworld/(scripts|styles|images).* {
  40.                 return 606;
  41.         }
  42.     }
  43. }
復制代碼
注意,這里有幾點需要說明一下,生產(chǎn)環(huán)境的 Nginx 服務器配置文件比這里要復雜很多,而且是按模塊分布在不同的文件中的。這里簡化模型后,使用 Http 響應狀態(tài)碼 60x 來區(qū)分到底被哪個 location 匹配到了。

我針對當時的情況,做了大量嘗試,最終的簡化版本如下:

  • 嘗試1:http://
                  localhost/helloworld ==> 602 符合預期
  • 嘗試2:http://
                  localhost/helloworld/hello ==> 603 符合預期
  • 嘗試3:http://
                  localhost/ii ==> 604 符合預期
  • 嘗試4:http://
                  localhost/ii/oo ==> 604 符合預期
  • 嘗試5:http://
                  localhost/ii/pp/kk ==> 605 符合預期
  • 嘗試6:http://
                  localhost/ii/pp/kk/ll ==> 605 符合預期
  • 嘗試7:http://
                  localhost/helloworld/scripts/aaa.js ==> 606 符合預期
  • 嘗試8:http://
                  localhost/helloworld/ii/hello/world ==> 605 不符合預期,預期為【603】


上面這些嘗試支持讀者自行試驗,Nginx 配置文件是完整可用的,我本地 Nginx 的版本是1.6.2

問題就在這里:我這里是事后,把這些匹配 location 標記成了不同的響應碼,才方便查找問題。當發(fā)現(xiàn)這個不符合預期后,我還是難以理解,為何我一個以 /helloworld 開頭的 URL 會被匹配到 605 這個以 /ii 開頭的 location 里面來。在當時的生產(chǎn)環(huán)境中,以 /ii 的配置統(tǒng)一放在另外一個文件中,這里是很難直觀的察覺出來這個 /ii 跟訪問的 URL 里面的 /ii 的關系。

我不得不重新編譯了 Nginx ,加上了調(diào)試參數(shù),修改配置項,看調(diào)試日志了。

這里不再講如何給 Nginx 加調(diào)試的編譯參數(shù),可自行查看相關文檔。修改配置項很簡單,只需要在
  1. error_log  logs/error.log;
復制代碼
后面加上 debug 就可以了。

打出詳細調(diào)試日志后,訪問
  1. http://
  2.          localhost/helloworld/ii/hello/world
復制代碼
我得到了這樣的一段日志(省略掉了前后無用的日志,只保留有意義的一段):
  1. 2015/02/02 15:38:48 [debug] 5801#0: *60 http request line: "GET /helloworld/ii/hello/world HTTP/1.1"
  2. 2015/02/02 15:38:48 [debug] 5801#0: *60 http uri: "/helloworld/ii/hello/world"
  3. 2015/02/02 15:38:48 [debug] 5801#0: *60 http args: ""
  4. 2015/02/02 15:38:48 [debug] 5801#0: *60 http exten: ""
  5. 2015/02/02 15:38:48 [debug] 5801#0: *60 http process request header line
  6. 2015/02/02 15:38:48 [debug] 5801#0: *60 http header: "User-Agent: curl/7.37.1"
  7. 2015/02/02 15:38:48 [debug] 5801#0: *60 http header: "Host: localhost"
  8. 2015/02/02 15:38:48 [debug] 5801#0: *60 http header: "Accept: */*"
  9. 2015/02/02 15:38:48 [debug] 5801#0: *60 http header done
  10. 2015/02/02 15:38:48 [debug] 5801#0: *60 event timer del: 4: 1422862788055
  11. 2015/02/02 15:38:48 [debug] 5801#0: *60 rewrite phase: 0
  12. 2015/02/02 15:38:48 [debug] 5801#0: *60 test location: "/"
  13. 2015/02/02 15:38:48 [debug] 5801#0: *60 test location: "ii"
  14. 2015/02/02 15:38:48 [debug] 5801#0: *60 test location: "helloworld"
  15. 2015/02/02 15:38:48 [debug] 5801#0: *60 test location: ~ "/ii/[^\/]+/[^\/]+"
  16. 2015/02/02 15:38:48 [debug] 5801#0: *60 using configuration "/ii/[^\/]+/[^\/]+"
  17. 2015/02/02 15:38:48 [debug] 5801#0: *60 http cl:-1 max:1048576
  18. 2015/02/02 15:38:48 [debug] 5801#0: *60 rewrite phase: 2
  19. 2015/02/02 15:38:48 [debug] 5801#0: *60 http finalize request: 605, "/helloworld/ii/hello/world?" a:1, c:1
  20. 2015/02/02 15:38:48 [debug] 5801#0: *60 http special response: 605, "/helloworld/ii/hello/world?"
  21. 2015/02/02 15:38:48 [debug] 5801#0: *60 http set discard body
  22. 2015/02/02 15:38:48 [debug] 5801#0: *60 posix_memalign: 00007FC3BB816000:4096 @16
  23. 2015/02/02 15:38:48 [debug] 5801#0: *60 HTTP/1.1 605
  24. Server: nginx/1.6.2
  25. Date: Mon, 02 Feb 2015 07:38:48 GMT
  26. Content-Length: 0
  27. Connection: keep-alive
復制代碼
可以看到,Nginx 測試了幾次 location 匹配,最終選擇了
  1. ~ "/ii/[^\/]+/[^\/]+"
復制代碼
這個作為最終的匹配項。到這里問題就完全展現(xiàn)出來了,我們本來的意思,是要以 /ii 開頭,后面有兩個或者更多的 / 分割的 URL 模型才匹配,但是這里的正則表達式匹配寫的不夠精準,導致了匹配錯誤。正則表達式?jīng)]有限制必須從開頭匹配,所以才會匹配到 /helloworld/ii/hello/world 這樣的 URL 。

解決辦法就是在這個正則表達式前面加上 ^ 來強制 URL 必須以 /ii 開頭才能匹配.
  1. /ii/[^\/]+/[^\/]+
復制代碼
變成
  1. ^/ii/[^\/]+/[^\/]+
復制代碼
至此,這個坑被填上了,消耗的是五個小時和一個字符。

相信很多人在寫 Nginx 的location 的時候都會 location ~ /xxx 或者 location /iii 這樣簡單了事,但是我想說的是能盡量精確就盡量精確,否則出現(xiàn)問題的時候,非常難以查找。

有關 Nginx 的 location 匹配規(guī)則,可以查看: http://
nginx.org/en/docs/http/ngx_http_core_module.html

問題總結(jié)

這個問題看似簡單,卻也隱含了不少問題,值得我們深思。

  • 計算機或者軟件出的問題往往是確定的,你發(fā)現(xiàn)他捉摸不定的時候,往往是沒有觀察到問題點
  • 追蹤一個問題,如果有一個必現(xiàn)方式,一定要緊追不舍,這就是所謂線索
  • 當你實在是找不到問題所在的時候,要懷疑一下之前被自己排除掉的可能性
  • 借助各個組件的詳細調(diào)試日志來查找問題,往往能得到意想不到的效果
  • 程序員的價值不是用行數(shù),字數(shù),提交數(shù)衡量的!


本文作者: 王振威
文章出自: Coding 官方技術(shù)博客
如需轉(zhuǎn)載,請注明作者與出處,謝謝


評分

參與人數(shù) 1可用積分 +6 收起 理由
cryboy2001 + 6 贊一個!

查看全部評分

您需要登錄后才可以回帖 登錄 | 注冊

本版積分規(guī)則 發(fā)表回復

  

北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報專區(qū)
中國互聯(lián)網(wǎng)協(xié)會會員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關心和支持過ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP