Cloud Foundry新版router源码分析

长平狐 发布于 2012/08/24 10:07
阅读 491
收藏 1

华为云11月刊推送:DIY微信问答机器人,高性能计算代码的20个技巧!>>>

调整格式太费力了,这里直接粘帖过来了,原文我写在自己的博客上了:http://lory-yang.com/2012/08/06/cloud_foundry_router/

Cloud Foundry在几个月前改进了它的router,我们可以在新版cloudfoundry解密中看到它的介绍:

上个版本中。Router作为一个nginx脚本存在。所以的请求都必须经过Ruby代码,然后加以转发。这个设计干净利落,不过Ruby也因此转发了大量的数据,容易引起性能问题,所以下个版本中做了如下的改进。

新版CloudFoundry揭秘

  在新版本的设计中,他们使用Lua脚本来代替原先的Ruby脚本。而Lua脚本会对请求加以分析,转发给Ruby程序,然后Ruby程序再将分析的结果返回。这样一来,proxied request已经不再经过Ruby代码。逻辑和数据完美分离。性能和稳定性都大幅提高了。

在前版设计中,当Router接收到请求后,会随机分配一个Droplet来处理这个请求,这种方式使得用户没有办法使用Session,因为连续的HTTP请求会被分发到不同的应用实例上处理。新版本设计中增加了对SESSION的支持,当Router发现用户的请求中带了cookie信息,它会在Cookie里暗藏一个Droplet的host,port地址。当有新的请求进来,Router通过解析Cookie得到上次的应用实例,然后尽量转发到同一台Droplet上。

那么本文就在此基础上,依据源码来做更加详细的分析。

Router工程

Router有一个独立的app目录,在vcap下面的router文件夹下。我们只需要关注两个部分:ext和lib。ext中有两个lua脚本,而lib中是ruby文件。

Nginx通过lua脚本发送lookup请求

总的来讲,Nginx通过lua脚本把lookup请求发送给由ruby代码建立的http server(这个server叫做uls server,也就是上图中的upstream locator svc),获取用户请求所指向的node的ip。那么本节主要研究Nginx如何通过lua发送这个lookup消息。

Nginx使用lua脚本是通过它的配置文件实现的。这个文件一般在:~/cloudfoundry/.deployments/devbox/deploy/nginx/nginx-0.8.54/conf/nginx.conf。我们可以看一下这个文件的内容,如果没有安装过,可以在vcap的源码的/dev_box/cookbooks/nginx/templates/defaults/ubuntu-nginx.conf.erb文件看。这个erb文件就是在chef安装cf时,填入参数,然后放到上述的conf目录下的。

那么这个文件的主要内容是:

access_by_lua '
-- add package.path andpackage.cpath
 package.path = package.path..";/root/cloudfoundry/.deployments/devbox/deploy/lua/lua-5.1.4/lib/lua/5.1/?.lua"
 package.cpath = package.cpath..";/root/cloudfoundry/.deployments/devbox/deploy/lua/lua-5.1.4/lib/lua/5.1/?.so"
 local uls = require ("uls")
 
ngx.log(ngx.DEBUG,"monitor trigger stats syncup")
 
local req = uls.generate_stats_request()
 
-- generate one subrequest to uls to update stats
 ngx.location.capture(
 "/vcapuls", { body = req }
 )
 ';

access_by_lua就是Nginx调用lua脚本来发送请求的配置,可以看 http://wiki.nginx.org/HttpLuaModule#access_by_lua 学习。那么在这个代码块中,首先把lua配置好了,包括lua文件,在package.path的路径中,就是上文提到的两个lua脚本。然后local req = uls.generate_stats_request()这行代码,其实是调用lua脚本中的一个函数,产生http request。这个函数可以在uls.lua文件中找到。

至此已经基本清楚了:Nginx配置好了lua,然后调用lua产生http request,然后Nginx将这个request发送给server。其他就不详细了,具体可以自己看代码

sticky session

新版本支持sticky session,这部分可以参考lua脚本:uls.lua,其中有函数专门解析request中的session,然后将这个信息加入到lookup request中,那么ruby在回应的时候会将其考虑在内。具体可以自己看代码。

请求的格式

这里码一下这个请求的格式,这段文字在uls.lua文件中:

Message between nginx and uls (as http body)
 nginx -> uls
 {
 "host": api.vcap.me,
 "backend_addr": 10.117.9.178:9022,
 "stats": [
 {
 "request_tags": xxx,
 "response_latency": xxx,
 "response_samples": xxx,
 "response_codes": {
 {"responses_xxx":xxx},
 {"responses_2xx":xxx}
 }
 },
 {
 "request_tags": xxx,
 "response_latency": xxx,
 "response_samples": xxx,
 "response_codes": {
 {"responses_xxx":xxx},
 {"responses_2xx":xxx}
 {"responses_5xx":xxx},
 }
 }
 ]
 }
 
nginx <- uls{
 "backend_addr": xxx,
 "request_tags": xxx,
 "router_ip": xxx
 }


第一部分是Nginx发出的请求格式,包括了host,tag,host就是用户发来的需要访问的url,而tag则是加密之后的参数。一个实际的例子:

1
{"host":"api.vcap.me","stats":[{"response_latency":0,"request_tags":"BAh7BjoOY29tcG9uZW50SSIUQ2xvdWRDb250cm9sbGVyBjoGRVQ=","response_codes":{"responses_2xx":1},"response_samples":1}]}

第二部分则是返回的response,包括了转发的ip以及tag。

Ruby端http server

现在的ruby文件主要负责建立一个http server供lua脚本查询ip,那么在router工程的lib文件中主要是/router/router_uls_server.rb文件。这个文件基于sinatra建立了一个server,而这个server在router.rb文件中被启动。而其他ruby则负责从NATS拿到其他组件的register信息,然后更新自己的router表。这部分也不是太难懂,就不展开了。


原文链接:http://blog.csdn.net/cherry_sun/article/details/7835005
加载中
返回顶部
顶部