2台机子模仿负载均衡的实验.
本机IP:115.213.73.254
云主机IP:xxx.xxx.xxx.xxx(保密起见,下同) 公网IP
本机运行连接云主机:
ssh ubuntu@某大佬 -p 10070
---------------------------------------云主机上运行netstat -pan结果如下------------------------------------------------
tcp 0 464 192.168.1.109:22 115.213.73.254:16328 ESTABLISHED -
tcp 0 0 192.168.1.109:22 115.213.73.254:16890 ESTABLISHED -
tcp 0 0 192.168.1.109:22 115.213.73.254:16253 ESTABLISHED -
可以看到:
192.168.1.109是云主机的局域网IP
115.213.73.254:16328是台式机所属路由器的端口
----------------------------------自己台式机上运行netstat -pan结果如下-----------------------------------------------------
(python3.6) appleyuchi@ubuntu:~$ netstat -pan|grep 10070
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
tcp 0 0 192.168.0.101:47450 xxx.xxx.xxx.xxx:10070 ESTABLISHED 8775/SecureCRT
tcp 0 0 192.168.0.101:47534 xxx.xxx.xxx.xxx:10070 ESTABLISHED 8775/SecureCRT
192.168.0.101:47450是自己台式机的局域网IP和端口
啦啦啦:10070是云主机的IP和防火墙端口
-------------------------------------------------概念总结--------------------------------------------------------------------------------------------
端口监听就是端口占用
上面涉及了四种端口
本机局域网端口->路由器端口->云主机防火墙端口->云主机内部局域网端口
因为本机是没有公网端口的,所以本地netstat查到的端口与云主机netstat查到的端口是不对应的
主要流程:
云主机内部局域网端口->云主机内部防火墙端口->云主机管理平台防火墙端口->客户路由器端口->客户本地端口->访问内容。
-------------------------------------------能不能用命令查询云主机管理平台防火墙?------------------------------------------------------------------
管理员大大说,refused还是drop都是云主机管理平台设定的,所以不能根据信息来判断端口到底咋了。
问了其他几位网友也是:不能用命令查。
命令查询防火墙端口是否没开:
云主机内部:
1.用web app监听一个port的情况下,
2.自己的台式机telnet 云主机IP 端口,返回connection refused
满足上述两个条件,就表示云主机管理平台防火墙端口没开。
-----------------------------------------------------------实验架构--------------------------------------------------------------------------
我们用两台机子,调度分发的时候,一台是发给自己,一台是发给云主机。
------------------------------------------------------web app部署、nginx配置、测试-------------------------------------------------------------------
1.《Flask Web开发:基于Python的Web应用开发实战》第四章的代码,具体操作如下:
①git clone https://github.com/miguelgrinberg/flasky
②gitcheck out 4c
③把上述步骤得到的文件夹分别拷贝到本机和云主机任意位置。
④分别修改本机和云主机和hello.py,具体修改在文末附录。
⑤分别在本机和云主机执行
python3 hello.py
2.
①文末附录的两个nginx.conf分别放到本机和云主机的/etc/nginx/路径下,
②nginx -s reload
3.测试:
chromium浏览器(不要使用Chrome浏览器,因为它会自动补充斜杠"/"导致访问失败,很讨厌,当然可以自己额外去hello.py中修改)
打开http://127.0.0.1:9020/flask_learn2
多打开几次会发现效果如下:
负载均衡成功。
-----------------------------------------------实验分析与调试---------------------------------------------------------------------
如果运行失败,检查以下环节:
不要在nginx.conf中使用ngrok穿透出来的网址,容易失败。
理清楚端口之间的关系,不要把端口设置重复了。
查清楚上面的“概念总结”中每一层端口是否被封
整体端口占用示意图:
/本机127.0.0.1:9002->本机127.0.0.1:9000
本机:9020
\云主机xx.xx.xx.xx:10072->云主机127.0.0.1:10071
上述9020,9002,10072端口都被nginx占用,其余都被Flask web app占用
另外,检查端口是否被占用,可以使用:
lsof -i:port
-------------------------------------------------附录-代码-------------------------------------------------------------------------
云主机的hello.py
from flask import Flask, render_template, session, redirect, url_for, flash
from flask_bootstrap import Bootstrap
from flask_moment import Moment
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired
app = Flask(__name__)
app.config['SECRET_KEY'] = 'hard to guess string'
bootstrap = Bootstrap(app)
moment = Moment(app)
class NameForm(FlaskForm):
name = StringField('What is your name?', validators=[DataRequired()])
submit = SubmitField('Submit')
@app.errorhandler(404)
def page_not_found(e):
return render_template('404.html'), 404
@app.errorhandler(500)
def internal_server_error(e):
return render_template('500.html'), 500
@app.route('/flask_learn2', methods=['GET', 'POST'])
def index():
form = NameForm()
if form.validate_on_submit():#用戶第2次以上訪問程序,這個地方的有效性(validate)驗證就是看你輸入了東西沒,沒輸入東西的話就會提醒你輸入
print(dir(session))
old_name = session.get('name')#直接從回話中讀取name參數的值
# old_name = session['name']
#session用来跨请求保存数据!
# old_name没有跨请求的能力
print("old_name=",old_name)
if old_name is not None and old_name != form.name.data:#form.name.data是html页面传过来的新数据
flash('Looks like you have changed your name!')
session['name'] = form.name.data
return redirect(url_for('index'))
if session.get("name") is None:
print("进入if")
return render_template('index.html', form=form, name=session.get('name'))#用戶第一次訪問程序
else:
print("进入else")
return render_template('index.html', form=form, name=session.get('name')+"当前是(云主机)")
#form.name.data最新提交的數據,但是這個請求一旦結束,數據就丟失了
#session.get('name')老數據
#session['name']老數據
#從上面的代碼來看,session['name']完全等效於session.get('name')
#----------------------------关闭被占用的端口-----------------------------------------
def killport(port):
command='''kill -9 $(netstat -nlp | grep :'''+str(port)+''' | awk '{print $7}' | awk -F"/" '{ print $1 }')'''
os.system(command)
if __name__ == '__main__':
port=10071
app.run(host="127.0.0.1", port=port)
本机(就是自己的电脑)中的hello.py如下:
from flask import Flask, render_template, session, redirect, url_for, flash
from flask_bootstrap import Bootstrap
from flask_moment import Moment
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired
app = Flask(__name__)
app.config['SECRET_KEY'] = 'hard to guess string'
bootstrap = Bootstrap(app)
moment = Moment(app)
class NameForm(FlaskForm):
name = StringField('What is your name?', validators=[DataRequired()])
submit = SubmitField('Submit')
@app.errorhandler(404)
def page_not_found(e):
return render_template('404.html'), 404
@app.errorhandler(500)
def internal_server_error(e):
return render_template('500.html'), 500
@app.route('/flask_learn2', methods=['GET', 'POST'])
def index():
form = NameForm()
if form.validate_on_submit():#用戶第2次以上訪問程序,這個地方的有效性(validate)驗證就是看你輸入了東西沒,沒輸入東西的話就會提醒你輸入
print(dir(session))
old_name = session.get('name')#直接從回話中讀取name參數的值
# old_name = session['name']
#session用来跨请求保存数据!
# old_name没有跨请求的能力
print("old_name=",old_name)
if old_name is not None and old_name != form.name.data:#form.name.data是html页面传过来的新数据
flash('Looks like you have changed your name!')
session['name'] = form.name.data
return redirect(url_for('index'))
if session.get("name") is None:
print("进入if")
return render_template('index.html', form=form, name=session.get('name'))#用戶第一次訪問程序
else:
print("进入else")
return render_template('index.html', form=form, name=session.get('name')+"当前是(本地)")
#form.name.data最新提交的數據,但是這個請求一旦結束,數據就丟失了
#session.get('name')老數據
#session['name']老數據
#從上面的代碼來看,session['name']完全等效於session.get('name')
#----------------------------关闭被占用的端口-----------------------------------------
def killport(port):
command='''kill -9 $(netstat -nlp | grep :'''+str(port)+''' | awk '{print $7}' | awk -F"/" '{ print $1 }')'''
os.system(command)
if __name__ == '__main__':
port=9000
app.run(host="127.0.0.1", port=port)
-------------------------------------------------附录-nginx-----------------------------------------------------------------------
云主机完整nginx.conf
user www-data;
worker_processes auto;
pid /run/nginx.pid;
events {
worker_connections 768;
# multi_accept on;
}
http {
##
# Basic Settings
##
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
# server_tokens off;
# server_names_hash_bucket_size 64;
# server_name_in_redirect off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
##
# SSL Settings
##
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
##
# Logging Settings
##
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
##
# Gzip Settings
##
gzip on;
gzip_disable "msie6";
# gzip_vary on;
# gzip_proxied any;
# gzip_comp_level 6;
# gzip_buffers 16 8k;
# gzip_http_version 1.1;
# gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
##
# Virtual Host Configs
##
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
server {
listen 10072;
#server_name mycard.moe;
#charset koi8-r;
#access_log /var/log/nginx/log/host.access.log main;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_pass http://127.0.0.1:10071/;
fastcgi_intercept_errors on;
proxy_redirect off;
}
}
}
本地完整nginx.conf(下面公网IP:xx.xx.xx.xx需要改成自己的)
user www-data;
worker_processes auto;
pid /run/nginx.pid;
events {
worker_connections 768;
# multi_accept on;
}
http {
##
# Basic Settings
##
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
# server_tokens off;
# server_names_hash_bucket_size 64;
# server_name_in_redirect off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
##
# SSL Settings
##
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
##
# Logging Settings
##
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
##
# Gzip Settings
##
gzip on;
gzip_disable "msie6";
# gzip_vary on;
# gzip_proxied any;
# gzip_comp_level 6;
# gzip_buffers 16 8k;
# gzip_http_version 1.1;
# gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
##
# Virtual Host Configs
##
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
upstream myapp{
server 127.0.0.1:9002 weight=5;
server xx.xx.xx.xx:10072 weight=5;
#server srv3.example.com;
}
server{
listen 9020;
#server_name
location / {
proxy_pass http://myapp;
}
}
server {
listen 9002;
#server_name xxx.cn
#charset koi8-r;
#access_log /var/log/nginx/log/host.access.log main;
location /{
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_pass http://127.0.0.1:9000/;
proxy_redirect off;
}
}
}
-------------------------------------------------------相关资料阅读和小记------------------------------------------------------------
[1]提到了各种负载均衡的类型(看完了)
[2]提到了flask+uwsgi
根据[3],uwsgi和gunicorn是两个相似的东西
[4]提到了nginx负载均衡, 并且有localhost测试的极好案例。
[5]提到了动静分离(动态网络资源和静态网络资源)
[6]nginx+Django+uwsgi
[7]提到了nginx对四层和七层的负载均衡的实现.
[8]提到了多个nginx的配置文件...不知道怎么一起操作.
[9]中server和upstream一起使用
[10]中一个conf文件里面有多个upstream,不知道含义是什么
[11]中提到,upstream是用来跨越单机(意思应该是转发给别的单机)
三台服务器,需要3个nginx.conf,但是每台机子上的nginx.conf内容都是不同的.
Reference:
[1]http://www.ttlsa.com/nginx/using-nginx-as-http-loadbalancer/
[2]https://my.oschina.net/RabbitXiao/blog/1583662
[3]https://segmentfault.com/q/1010000008927097/a-1020000008927326
[4]https://www.jianshu.com/p/4c250c1cd6cd
[5]https://blog.51cto.com/13178102/2063271
[6]https://segmentfault.com/a/1190000016108576
[7]https://www.jb51.net/article/153710.htm
[8]https://www.cnblogs.com/crazymagic/p/11029415.html
[9]https://www.cnblogs.com/winniejohn/p/9855351.html
[10]https://www.cnblogs.com/diantong/p/11208508.html
[11]https://blog.csdn.net/gu_wen_jie/article/details/82149003