因為一個奇怪的需求,使用 Python 和 Tornado 框架實現了一個 Web 站點的反向代理。實現的功能是這樣:
- 假設這個反向代理網站的地址是 http://www.example.com
- 訪問 http://www.example.com/.site.backend_site0/,訪問的是 backend_site0,這個網站可以是部署在內網的某個站點(外網當然也是可以)。
- 訪問 http://www.example.com/.site.backend_site1/,訪問另外一個站點 backend_site1
怎么通過一個公共的站點反向代理訪問后端的多個站點,當時的討論帖在這里,我采用的是:
- 在url中添加前綴
.site.
,第一次訪問的時候使用 http://www.example.com/.site.backend_site/ - 服務端識別出請求的后端站點
backend_site
后,會設置 Cookie,.site = backend_site
,后續的訪問會根據Cookie來識別出來。 - 當訪問另外一個網站 http://www.example.com/.site.backend_site1/,這時候,服務端會清除舊的 Cookie,並設置新的 Cookie,
.site = backend_site1
,這樣就切換到新的站點backend_site1
- 啟用頁面內容替換,保證頁面內的內網IP地址轉換成反向代理的地址。例如后端站點 backend_site0 的地址是 http://10.1.2.3/,頁面內有鏈接 http://10.1.2.3/img/a.png,將其替換成 /.site.backend_site0/img/a.png
項目的代碼開源在 Github 上,有需要的可以自取。
https://github.com/restran/web_proxy
環境需求
Python 2.7
Tornado 4.0
所代理的后端網站注意事項
-
url 的前綴不應出現 /.site.
由於反向代理采用url前綴來區分后端網站,如 /.site.example/,表示后端站點example。
例如以下為禁用的url:
/.site./
/.site.example/
/.site.example/user/login/ -
cookies 中不應出現以 .site 命名的 cookie 值,這個 cookie 是用來標識當前訪問的后端站點
CentOS 7.0 部署
Tornado 的部署可以參照這里的教程。通過啟動多個 Tornado 實例,來避免調用到同步函數塊,導致阻塞住,無法響應其他用戶的請求。使用 supervisor 來啟動 Tornado Server,並使用 Nginx 作為 Web 服務器,反向代理后端的這些 Tornado 實例。
修改配置文件 settings.py
# 轉發到后端需要代理的網站的地址列表
forward_list = {
"baidu": BackendSite('baidu', 'http://www.baidu.com', 'www.baidu.com', []),
"douban": BackendSite('douban', 'http://www.douban.com', 'www.douban.com', [
# 使用正則表達式替換頁面內容,參數分別是
# 需要替換的URI的正則表達式,源字符串的正則表達式,替換后的字符串
SubsFilterRules('.', r'http://www\.douban\.com', '/.site.douban'),
SubsFilterRules('.', r'http://img3\.douban\.com', '/.site.img3.douban'),
SubsFilterRules('.', r'http://img5\.douban\.com', '/.site.img5.douban'),
]),
"img3.douban": BackendSite('douban', 'http://img3.douban.com', 'img3.douban.com', []),
"img5.douban": BackendSite('douban', 'http://img5.douban.com', 'img5.douban.com', []),
}
使用 supervisor 啟動 Tornado Server
設置配置文件
vim /etc/supervisord.conf
輸入如下信息
[program:tornado_server_9001]
command=python /home/python/web_proxy/proxy.py --port=9001
directory=/home/python/web_proxy
autorestart=true
redirect_stderr=true
stdout_logfile = /var/log/supervisord/web_proxy.log
[program:tornado_server_9002]
command=python /home/python/web_proxy/proxy.py --port=9002
directory=/home/python/web_proxy
autorestart=true
redirect_stderr=true
stdout_logfile = /var/log/supervisord/web_proxy.log
[program:tornado_server_9003]
command=python /home/python/web_proxy/proxy.py --port=9003
directory=/home/python/web_proxy
autorestart=true
redirect_stderr=true
stdout_logfile = /var/log/supervisord/web_proxy.log
重新加載配置文件
supervisorctl reload
重新啟動所有程序
sudo supervisorctl restart all
配置 nginx
添加 nginx 配置文件
vim /etc/nginx/conf.d/web_proxy.conf
輸入如下配置信息
upstream tornadoes {
server 127.0.0.1:9001;
server 127.0.0.1:9002;
server 127.0.0.1:9003;
}
server {
listen 9000;
server_name your_server_name; # 例如輸入服務器IP
gzip on;
# 設置允許壓縮的頁面最小字節數,頁面字節數從header頭得content-length中進行獲取。默認值是0,不管頁面多大都壓縮。建議設置成大於1k的字節數,小於1k可能會越壓越大。
gzip_min_length 1000;
gzip_buffers 4 16k;
gzip_http_version 1.1;
# 1~9,默認為1,數值越大,壓縮率越高,CPU占用越多,時間越久
gzip_comp_level 3;
gzip_vary on;
# 禁用對 IE 6 使用 gzip 壓縮
gzip_disable "MSIE [1-6]\.";
gzip_types text/plain text/html text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript application/json;
## Individual nginx logs
access_log /var/log/nginx/web_proxy_access.log;
error_log /var/log/nginx/web_proxy_error.log;
location / {
proxy_pass_header Server;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_pass http://tornadoes;
}
}
重啟 nginx
service nginx restart
todo
URL 訪問控制,訪問列表