CloudFoundry源码分析:Router

Router的实现主要由两部分组成: 一个是lib/router/router.rb,主要作用和nats服务器交互,管理router表。一个是lib/router/router_uls_server.rb,主要是处理各种外部请求。

Router

Router(lib/router/router.rb)主要通过注册事件和nats服务器交互,处理其他模块发布的信息,并统计一些运行信息。

router在setup_listeners里主要注册了两个事件:分别是“router.register”和“router.unregister”。DEA模块会向nats server发布"router.register"消息和“router.unregister"消息,这主要根据DEA的状态以及instance的态等(在分析DEA时会详细介绍)。

register

      NATS.subscribe('router.register') { |msg|
        msg_hash = Yajl::Parser.parse(msg, :symbolize_keys => true)
        return unless uris = msg_hash[:uris]
        uris.each { |uri| register_droplet(uri, msg_hash[:host], msg_hash[:port],
                                           msg_hash[:tags], msg_hash[:app]) }
      }

当router接收到'router.register'信息时,信息结构类似于:多个uri对应一个droplet instance(host,port,tags,app)。router在router表里面注册每个uri与droplet instance的对应关系,实现方法在 def register_droplet(url, host, port, tags, app_id)里面。

在路由表对于任意一个url,可以映射到app的多个droplet实例。

unregister

NATS.subscribe('router.unregister') { |msg|
        msg_hash = Yajl::Parser.parse(msg, :symbolize_keys => true)
        return unless uris = msg_hash[:uris]
        uris.each { |uri| unregister_droplet(uri, msg_hash[:host], msg_hash[:port]) }
      }
router在setup_sweepers方法里面添加了几个定时器,进行一些”清扫“工作。

calc_rps
计算最近一段时间间隔的请求数(requests per second),保存在VCAP::Component.varz[:requests_per_sec]中,并计算访问最多的apps

check_registered_urls

检测超过MAX_AGE_STALE未响应的droplet实例,取消注册

RouterULSServer

这是一个基于Sinatra实现的http server。

Server接受一个GET请求,请求路由"/"。

请求的信息存放在body中,如下:

{
	"host":"mytest.cloudfoundry.com",
	"stats":[
		{	"response_latency":0,
			"request_tags":"BAh7BjoOY29tcG9uZW50SSIUQ2xvdWRDb250cm9sbGVyBjoGRVQ=",
			"response_codes":{"responses_2xx":1},
			"response_samples":1
		}
		]
}

当有请求到来之后,http server首先更新state信息,然后解析出请求“mytest.cloudfoundry.com”,根据“mytest.cloudfoundry.com”,寻找对应的droplet

droplets = Router.lookup_droplet(url)
# Pick a droplet based on original backend addr or pick a droplet randomly
      if sticky
        _, host, port = Router.decrypt_session_cookie(sticky)
        droplet = check_original_droplet(droplets, host, port)
      end
      droplet ||= droplets[rand*droplets.size]

当选中一个droplet实例之后,更新实例的tags和vaz,然后返回实例的信息。

uls_response = {
        ULS_STICKY_SESSION => new_sticky,
        ULS_BACKEND_ADDR   => "#{droplet[:host]}:#{droplet[:port]}",
        ULS_REQUEST_TAGS   => uls_req_tags,
        ULS_ROUTER_IP      => Router.inet,
        ULS_APP_ID         => droplet[:app] || 0,
      }

Router中的统计数据

  VCAP::Component.varz[:requests]  Router接收到的所有请求数目

  VCAP::Component.varz[:bad_requests] Router接收到的所有bad请求数目
  VCAP::Component.varz[:latency] 
  VCAP::Component.varz[:responses_2xx]
  VCAP::Component.varz[:responses_3xx]
  VCAP::Component.varz[:responses_4xx]
  VCAP::Component.varz[:responses_5xx]
  VCAP::Component.varz[:responses_xxx] 
  VCAP::Component.varz[:urls] Router注册的所有的url数目,也是整个CF实例的url数目
  VCAP::Component.varz[:droplets]  Router注册的所有的droplet的实例的数目
  VCAP::Component.varz[:tags] = {} Router搜集的所有的tags

  VCAP::Component.varz[:requests_per_sec] Router最近一个统计周期的RPS


Router的核心结构:@droplets

droplets记录了所有的平台的所有的实例信息:

一个url对应一个droplet的数组。多个实例服务于同一个url。

url.downcase=>[droplet,droplet,droplet,...]

一个droplet的信息包括如下

droplet = {
        :host => host,
        :port => port,
        :clients => Hash.new(0),
        :url => url,
        :timestamp => Time.now,
        :requests => 0,
        :tags => tags
      }
host:运行的主机

port:监听的端口

clients:一个统计周期内请求的客户端ip和请求数目的映射

url :监听的url

timestamp: 上一个心跳时间

requests:一个统计周期内的请求次数

tags:该droplet的tag









你可能感兴趣的:(CloudFoundry源码分析:Router)