uWSGI FastRouter
对于高级设置,uWSGI包含了”fastrouter”插件,它是一个代理/负载均衡器/路由器,使用uwsgi协议。默认内建它。你可以将其放在你的web服务器和真正的uWSGI实例之间,以获取对于HTTP请求到你的应用服务器的路由的更多控制。
开始
首先,你必须运行fastrouter,将它绑定到一个指定的地址上。也支持多个地址。
- uwsgi --fastrouter 127.0.0.1:3017 --fastrouter /tmp/uwsgi.sock --fastrouter @foobar
注解
这是世界上最没用的Fastrouter设置。
恭喜你!你刚刚运行了世界上最没用的Fastrouter设置。简单将fastrouter绑定到一堆地址上并不会指示它如何路由请求。要赋予它智能,你必须告诉它如何路由请求。
方法1:–fastrouter-use-base
这个选项将会告诉fastrouter连接到一个UNIX socket,这个socket的名字与指定目录中的请求主机的名字相同。
- uwsgi --fastrouter 127.0.0.1:3017 --fastrouter-use-base /tmp/sockets/
如果你接收到一个对 example.com
的请求,那么fastrouter将会转发请求到 /tmp/sockets/example.com
。
方法2:–fastrouter-use-pattern
与前面的设置相同,但是你将能够使用模式,其中, %s
映射到请求的键/主机名。
- uwsgi --fastrouter 127.0.0.1:3017 --fastrouter-use-pattern /tmp/sockets/%s/uwsgi.sock
对 example.com
的请求将会被映射到/tmp/sockets/example.com/uwsgi.sock
.
方法3:–fastrouter-use-cache
你可以在 uWSGI cache 中存储键值映射。选择一种方式去填充缓存,例如,一个像这样的Python脚本……
- import uwsgi
- # Requests for example.com on port 8000 will go to 127.0.0.1:4040
- uwsgi.cache_set("example.com:8000", "127.0.0.1:4040")
- # Requests for unbit.it will go to 127.0.0.1:4040 with the modifier1 set to 5 (perl/PSGI)
- uwsgi.cache_set("unbit.it", "127.0.0.1:4040,5")
然后运行你启用了Fastrouter的服务器,告诉它首先运行脚本。
- uwsgi --fastrouter 127.0.0.1:3017 --fastrouter-use-cache --cache 100 --file foobar.py
方法4:–fastrouter-subscription-server
这可能是用于大量自动扩展托管的最好的方式之一。它使用 subscription server 来允许实例宣告自己,并订阅到fastrouter。
- uwsgi --fastrouter 127.0.0.1:3017 --fastrouter-subscription-server 192.168.0.100:7000
这将会在地址192.168.0.100,端口7000上生成一个订阅服务器
现在,你可以生成订阅到fastrouter的实例了:
- uwsgi --socket :3031 -M --subscribe-to 192.168.0.100:7000:example.com
- uwsgi --socket :3032 -M --subscribe-to 192.168.0.100:7000:unbit.it,5 --subscribe-to 192.168.0.100:7000:uwsgi.it
正如你可能注意到的,你可以订阅多个fastrouter,使用多个键。具有相同的键的订阅到相同fastrouter的多个实例将会自动负载均衡,并被监控。很方便,不是吗?就像缓存键/值存储,可以用一个逗号来设置 modifier1
。(上面是 ,5
) 订阅系统的另一个特性是避免选择端口。你可以将实例绑定到随机端口,而订阅系统将会发送真实的值到订阅服务器上。
- uwsgi --socket 192.168.0.100:0 -M --subscribe-to 192.168.0.100:7000:example.com
映射文件
如果你需要指定大量的键,那么你可以使用一个映射文件来取代。
- # mappings.txt
- unbit.it
- unbit.it:8000,5
- uwsgi.it
- projects.unbit.it
- uwsgi --socket :3031 -M --subscribe-to 192.168.0.100:7000:@mappings.txt
方法5:–fastrouter-use-code-string
如果Darth Vader穿着一个带有你的脸的T恤 (还要其他一些边界情况),那么你可以使用代码驱动的映射来自定义fastrouter。选择一个支持uWSGI的语言 (就像Python或者Lua),然后定义你的映射函数。
- def get(key):
- return '127.0.0.1:3031'
- uwsgi --fastrouter 127.0.0.1:3017 --fastrouter-use-code-string 0:mapper.py:get
这将会指示fastrouter使用插件(modifier1) 0加载脚本 mapper.py
,并且调用’get’全局函数,传给它key值。在前面的例子中,你会总是路由请求到127.0.0.1:3031。让我们创建一个更高级的系统,只是为了好玩!
- domains = {}
- domains['example.com'] = {'nodes': ('127.0.0.1:3031', '192.168.0.100:3032'), 'node': 0}
- domains['unbit.it'] = {'nodes': ('127.0.0.1:3035,5', '192.168.0.100:3035,5'), 'node': 0}
- DEFAULT_NODE = '192.168.0.1:1717'
- def get(key):
- if key not in domains:
- return DEFAULT_NODE
- # get the node to forward requests to
- nodes = domains[key]['nodes']
- current_node = domains[key]['node']
- value = nodes[current_node]
- # round robin :P
- next_node = current_node + 1
- if next_node >= len(nodes):
- next_node = 0
- domains[key]['node'] = next_node
- return value
- uwsgi --fastrouter 127.0.0.1:3017 --fastrouter-use-code-string 0:megamapper.py:get
只需短短几行,我们就实现了一个带有回退节点的循环负载均衡。Pow! 你可以添加某些形式的节点监控,在脚本中启动线程,或者其他疯狂的东东。 (确保将其添加到文档中!)
注意
记住,不要在你的函数中写阻塞代码。fastrouter是完全非阻塞的,不要毁了它!
Cheap模式和共享socket
一个常见的设置是让一个web服务器/代理连接到一个fastrouter,并且让一系列的uWSGI实例订阅到它上面。通常请情况下,你会将web服务器节点当成一个uWSGI实例节点使用。这个节点将会订阅到本地fastrouter。好吧……不要在上面浪费时间周期!共享socket是一种在各种uWSGI部件之间共享socket的方式。让我们使用它在fastrouter和uWSGI实例之间共享socket。
- [uwsgi]
- ;create a shared socket (the webserver will connect to it)
- shared-socket = 127.0.0.1:3031
- ; bind the fastrouter to the shared socket
- fastrouter = =0
- ; bind an instance to the same socket
- socket = =0
- ; having a master is always a good thing...
- master = true
- ; our subscription server
- fastrouter-subscription-server = 192.168.0.100:4040
- ; our app
- wsgi-file = /var/www/myheavyapp.wsgi
- ; a bunch of processes
- processes = 4
- ; and put the fastrouter in cheap mode
- fastrouter-cheap = true
使用这个设置,你的请求将会直接到达你的应用(无代理开销),或者到fastrouter (传递请求给远程节点)。当fastrouter处于cheap模式的时候,它将不会响应请求,直到节点可用。这意味着,当没有订阅的节点的时候,只有你本地的应用会响应。当所有的节点都挂掉的时候,fastrouter将会回到cheap模式。看到套路了吗?另一个到棒棒哒的自动缩放的设置。
Post-buffering模式 (uWSGI >= 2.0.9)
fastrouter (默认情况下) 是一个流代理。这意味着,一旦解析了uwsgi包 (即,请求头部),它就会被转发到后端(们)。
现在,如果你的web代理也是一个流代理 (例如apache,或者uWSGI http路由器),那么你的应用在带有请求体的请求下会被阻塞很长一段时间。说得更明白一点:
- 客户端启动请求,发送http头部
- web代理接收它,然后发送给fastrouter
- fastrouter接收它,然后发送给后端
- 客户端开始发送请求体块 (例如文件上传)
- web代理接收它们,然后转发给fastrouter
- fastrouter接收它们,然后转发给后端,以此类推
现在,想象有10个并发的客户端在做这件事,你将会得到10个处于忙碌状态不确定时间的应用服务器worker(或者线程)。(注意,这个问题会被这样一个事实放大:一般来说,线程/进程的数目是非常受限的,甚至是处于异步模式也是如此,因此,你有一个有限的并发请求,但是它通常如此之高,以至于这个问题没那么相关)
像nginx这样的web代理是会“缓冲”的,因此它们会等待,直到整个请求(及其请求体)读完,然后再将其发送到后端。
你可以用 —fastrouter-post-buffering <n>
选项指示fastrouter像nginx一样,其中,<n>是指,请求体到多大之后,将会被存储到磁盘(作为临时文件),而不是内存:
- [uwsgi]
- fastrouter = 127.0.0.1:3031
- fastrouter-to = /var/run/app.socket
- fastrouter-post-buffering = 8192
将会把fastrouter置于缓冲模式,每当请求体大于8192字节,就会把它存储到一个临时文件中,而当小于(或等于)时,则会存在内存里
记住,post-buffering,并非一本万利的解决方法 (否则,会默认使用它),启用它会破坏websockets,块输入,上传过程等等等等。只在需要的时候启用它。
注意
- fastrouter使用以下变量 (按优先顺序) 来选择使用的键:
UWSGI_FASTROUTER_KEY
- 最通用的,因为它不依赖于以任何方式的请求HTTP_HOST
SERVER_NAME
- 你可以使用–fastrouter-events增加fastrouter可以管理的异步事件数目 (默认情况下,它是依赖于系统的)
你可以用–fastrouter-timeout修改默认超时时间。默认情况下,当通过unix socket使用的时候,fastrouter会设置fd socket passing。如果你不想要它,那么添加–no-fd-passing