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脚本来发送请求的配置,可以看学习。那么在这个代码块中,首先把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则是加密之后的参数。一个实际的例子:

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

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

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/9f174b3c04a2d90921e1d373bc35e12c.html