uWSGI订阅服务器
uWSGI栈的一些组建需要一个键值映射系统。
比如,The uWSGI FastRouter需要知道某个请求要发到哪个服务器上。
在一个具有很多节点的网络中,手工维护这个配置会是地狱一般的体验。uWSGI实现了一个订阅系统,节点可以利用这个订阅系统向订阅服务器声明其存在,这样就会反过来填充订阅服务器的内部字典。
uwsgi --fastrouter :1717 --fastrouter-subscription-server 192.168.0.100:2626
这会在1717端口上运行一个uWSGI fastrouter,并创建一个空的字典,其中键为主机名,值为uwsgi地址。
可以通过联系192.168.0.100:2626这个订阅服务器地址来填充这个字典。
对于每个键,可以对应存在多个地址,这样就可以开启负载均衡(可以使用多种负载均衡算法)。
几点可以通过使用subscribe-to或者subscribe2选项来向订阅服务器声明其存在。
uwsgi --socket 192.168.0.10:3031 --wsgi myapp -M --subscribe-to 192.168.0.100:2626:uwsgi.it
FastRouter会将对uwsgi.it的每个请求映射到192.168.0.31:3031上。
现在为uwsgi.it添加第二个节点,再次运行并订阅即可:
uwsgi --socket 192.168.0.11:3031 --wsgi myapp --master --subscribe-to 192.168.0.100:2626:uwsgi.it
死亡节点会被自动从这池中移除掉。
subscribe2的语法与之类似,只是其可以允许更多的控制,比如可以指定所有请求都应该被转发到的地址。它的值的语法是一个“键-值”对的字符串,通过逗号来区分分割不同的键值对。
uwsgi -s 192.168.0.10:3031 --wsgi myapp --master --subscribe2 server=192.168.0.100:2626,key=uwsgi.it,addr=192.168.0.10:3031
可用的subscribe2键列表,可以参考下面的内容。
订阅系统当前可用于加入集群(当多播/广播不可用时),Fastrouter、HTTP/HTTPS/SPDY路由,rawrouter和sslrouter。
也就是说你可以立即创建一个事件驱动的/性能极好的HTTP负载均衡器。
uwsgi --http :80 --http-subscription-server 192.168.0.100:2626 --master
现在只需要简单地将你的节点订阅到HTTP订阅服务器即可。
可以通过http-stats-server选项来检查订阅服务器的统计信息和/或参与订阅的节点。
uwsgi --http :80 --http-subscription-server 192.168.0.100:2626 --http-stats-server 192.168.0.100:5004 --master
可以使用http-resubscribe选项将订阅请求转发到其它服务器。
uwsgi --http :80 --http-subscription-server 192.168.0.100:2626 --http-resubscribe 192.168.0.101:2627 --master
订阅系统安全
这个订阅系统适用于“受信任”网络。网络中的所有节点都可能让整个网络变得一团糟。
如果你正在为不受信任的用户构建一个架构,或者你仅仅是需要控制谁能够去订阅订阅服务器,你就可以使用openssl rsa公钥/私钥对用来“签名”订阅请求。
# 首先,为订阅者创建一个私钥。不要为这个私钥设置口令
openssl genras --output private.pem
# 为订阅服务器创建公钥:
openssl rsa -pubout -out test.uwsgi.it_8000.pem -in private.pem
公钥必须被命名为我们要订阅提供服务器的域以及.pem扩展名命名。
注意
如果你是在为一个监听于特定端口的应用订阅,你需要使用domain_port.pem的结构来命名公钥文件。通常所有DNS允许的字符都是可以使用的,其它字符被映射为下划线。
一个由RSA保护的服务器的例子,是这个样子的:
[uwsgi]
master = 1
http = :8000
http-subscription-server = 127.0.0.1:2626
subscriptions-sign-check = SHA1:/etc/uwsgi/keys
最后一行告诉uWSGI,公钥文件存储于/etc/uwsgi/keys中。
对于每次订阅请求,服务器会去检查对应的公钥的可用性,如果有,就使用它来验证包的签名。未能通过验证的包会被拒绝。
在客户端,你需要将私钥在subscribe-to选项中进行指定。下面是一个例子:
[uwsgi]
socket = 127.0.0.1:8080
subscribe-to = 127.0.0.1:2626:test.uwsgi.it:8000,5,SHA1:/home/foobar/private.pem
psgi = test.psgi
我们来分析一下subscribe-to的使用:
- 127.0.0.1:2626是我们想要去订阅的订阅服务器。
- test.uwsgi.it:8000是订阅键。
- 5是psgi应用的modifer1的值
- SHA1:/home/foobar/private.pem是<digest>:<rsa>对,用于服务器认证(其中<rsa>是私钥路径)
注意
请确定在服务器和客户端上使用相同的摘要方法(在上面的例子中是SHA1)。
为了避免重放攻击,每个订阅包都有一个自增数字(通常就是unix时间),用于禁止重复的包。即使是一个攻击者能够嗅探到一个订阅包,也是没有任何用处的,因为这个包之前已经处理过了。显然,如果有人拿到了你的私钥,他就能够构建伪造的包了。
使用SSH**
SSH格式的**受到开发者的普遍欢迎(当然,是相比于经典的PEM文件)。
-subscribe-to和-subscribe2(见下文)都支持SSH私钥,而对于服务器部分,你需要将公钥编码为pkcs8格式:
ssh-****** -f chiavessh001.pub -e -m pkcs8
-subscribe2
这是-subscribe-to的键值对版本。它支持更多的配置,并有一个(通常)更可读的语法:
uwsgi --socket 127.*:0 --subscribe2 server=127.0.0.1:7171,key=ubuntu64.local:9090,sign=SHA1:chiavessh001
支持的字段有:
- server 订阅服务器的地址
- key 订阅的键(通常是域名)
- addr 要订阅的地址(此项的值)
- socket 套接字编号(从零开始),这类似于‘addr’,只是取内部套接字的编号
- weight 负载均衡值
- modifier1和modifier2
- sign <algo>:<file>安全系统的签名
- check 接收一个文件。如果其存在就发送包,否则就跳过
- sni_key 为SNI代理管理设置key file
- sni_crt 为SNI代理管理设置crt file
- sni_ca 为SNI代理管理设置 ca file
- algo (uWSGI 2.1)设置使用的负载均衡算法(可插拔,包括wrr,lrc,wlrc和iphash)
- proto(uWSGI 2.1)使用的协议,默认是‘uwsgi’
- backup(uWSGI 2.1)设置备份等级(根据不同的算法,意义有所变化)
通知
当你订阅到一个服务器的时候,你可以要求它“确认”对请求的接受。
只需要加上--subscription-notify-socket <addr>指向一个数据报(Unix或者UDP)地址,你的一个服务实例会绑定到这个地址上,订阅服务器会发送确认到这个服务器。
挂载点(uWSGI 2.1)
通常你会将应用订阅到某个域名上。
由于有了uWSGI 2.1中引入的挂载点支持,你可以订阅每个节点到一个特定的目录上(你需要指定想要支持多少层):
首先,你需要告知订阅服务器接受(并处理)挂载点请求:
uwsgi --master --http :8080 --http-subscription-server 127.0.0.1:4040 --subscription-mountpoints 1
让后你就可以订阅到挂载点上了。
uwsgi --socket 127.0.0.1:0 --subscribe2 server=127.0.0.1:4040,key=mydomain.it/foo
uwsgi --socket 127.0.0.1:0 --subscribe2 server=127.0.0.1:4040,key=mydomain.it/bar
uwsgi --socket 127.0.0.1:0 --subscribe2 server=127.0.0.1:4040,key=mydomain.it/foo
uwsgi --socket 127.0.0.1:0 --subscribe2 server=127.0.0.1:4040,key=mydomain.it
第一个和第三个实例会回应对/foo的请求,第二个会回应对/bar的请求,最后一个会处理剩余其它的请求。
对于安全的订阅系统,你只需要使用域名**(不要为每个挂载点生成一个证书)。
如果你想要支持类似于/one/two,而不是/one形式的挂载点,只需要对--subscription-mountpoints选项指定‘2’即可。出于性能考量,你需要选择路径可支持的元素的数量,并且不能混用(就是说:如果-subscription-mountpoints为2,你可以支持/one/two或者/foo/bar,但是不能支持/foobar)。