本报告从两个方面讲述Cloud Foundry中的组件Service Gateway:Service Gateway的功能和Service Gateway的通信机制。
Service Gateway 在CloudFoundry中的作用主要是:接收Cloud Foundry中的控制器Cloud Controller发来的请求,并根据请求类型,对Service Node上的service instance作相应的管理。
在CloudFoundry中,Cloud Controller 会向ServiceGateway发送20种管理请求。以下是这些请求具体的描述:
功能:为用户创建一个新的服务实例。
函数调用:@provisioner.provision_service(req)
参数解析:
req:CloudController发送的请求到达后,经Service Gateway译码后的信息,在ruby中的数据类型为hash;在provision_service(req)函数中,主要用到其中的属性的label,plan,version;
label:需要创建服务实例的类型,比如:mysql-5.1,mysql-5.0等;
plan:需要创建服务实例的计划,主要是限制创建服务实例的大小等;
version:需要创建服务实例的版本,比如在MySQL的版本号5.1。
1.2. Unprovision a service
功能:为用户清除一个已经创建完毕的服务实例。
函数调用:@provisioner.unprovision_service(params['service_id'])
参数解析:
params['service_id']:Cloud Controller发送的请求中,属性为'service_id'的值;由于清除一个已经创建完毕的服务实例,只需要知道这个服务实例的ID,所以传给unprovision_service()函数的参数只需要一个服务实例的ID。
1.3. Bind a service
功能:为用户的一个应用程序绑定一个服务实例
函数调用:@provisioner.bind_instance(req.service_id,req.binding_options)
参数解析:
req.service_id:CloudController需要给应用程序绑定的服务实例ID;
req.binding_options:CloudController需要给应用程序绑定服务实例时的绑定选项,在目前Cloud Foundry的运行中,该参数值为空,另外在源程序中该参数值的作用只限于存储与查看,并不影响程序的运行,可以认为是绑定的备注信息。
备注:由于在CloudFoundry中,绑定一个服务实例,只是为特定服务实例创建credential信息,随后将这些信息返回给Cloud Controller,由Cloud Controller将这些信息重新打包入应用程序,所以在该过程中,Service Gateway涉及的只有相应的service_id,而不需要具体的应用程序ID。
1.4. Unbind a service
功能:为用户一个绑定服务实例的应用程序解除绑定。
函数调用: @provisioner.unbind_instance(req.service_id, req.handle_id,req.binding_options)
参数解析:
req.service_id:需要解除绑定的服务实例的ID;
req.handle_id:需要解除绑定的服务实例的认证信息所在记录的ID;
req.binding_options:服务实例在绑定时的绑定选项,该参数在函数引用参数中出现,但是在函数具体实现中却没有使用到。
1.5. Createa snapshot
功能:为一个服务实例创建一个快照文件
函数调用:@provisioner.create_snapshot(service_id)
参数解析:
service_id:需要创建快照的服务实例ID。
1.6. Get snapshot details
功能:获取一个服务实例快照的具体信息
函数调用:@provisioner.get_snapshot(service_id, snapshot_id)
参数解析:
service_id:需要获取具体信息的快照对应的服务实例ID;
snapshot_id:需要获取具体信息的快照ID。
1.7. Update snapshot name
功能:为一个已经创建完毕的快照更新名称
函数调用:@provisioner.update_snapshot_name(service_id, snapshot_id, req.name)
参数解析:
service_id:需要更新名称的快照对应的服务实例ID;
snapshot_id:需要更新名称的快照ID;
req.name:最终用来更新快照的名称。
1.8. Get all snapshots related to an instance
功能:列举一个服务实例的所有快照
函数调用:snapshots = service_snapshots(service_id)
参数解析:
service_id:需要列举所有快照的服务实例ID。
1.9. Roll back to a snapshot
功能:将以个服务实例回滚到某个指定的快照
函数调用:@provisioner.rollback_snapshot(service_id, snapshot_id)
参数解析:
service_id:需要回滚的服务实例的ID;
snapshot_id:需要将该服务实例回滚到指定版本的快照ID。
1.10. Delete a snapshot
功能:删除一个服务实例的某个快照
函数调用:@provisioner.delete_snapshot(service_id, snapshot_id)
参数解析:
service_id:需要删除快照的服务实例ID;
snapshot_id:需要删除的快照ID。
1.11. Create a serialized URL
功能:为一个服务实例创建一个串行化的URL
函数调用:@provisioner.create_serialized_url(service_id, snapshot_id)
参数解析:
service_id:需要创建串行化URL的服务实例ID;
snapshot_id:需要创建串行化URL的快照ID。
1.12. Get a serialized URL
功能:获取指定服务实例的指定快照的串行化URL
函数调用:@provisioner.get_serialized_url(service_id, snapshot_id)
参数解析:
service_id:需要获取串行化URL的服务实例ID;
snapshot_id:需要获取串行化URL的服务实例中的快照ID。
1.13. Import serialized data fromURL
功能:为指定服务实例导入串行化URL
函数调用:@provisioner.import_from_url(service_id, req.url)
参数解析:
service_id:需要导入串行化URL的服务实例ID;
req.url:需要为服务实例导入的具体串行化URL。
1.14. Get job details
功能:获取指定服务实例的指定任务信息
函数调用: @provisioner.job_details(service_id, job_id)
参数解析:
service_id:需要获取任务信息的服务实例ID;
job_id:服务实例的具体任务的ID。
1.15. Restore a service
功能:备份指定服务实例
函数调用:@provisioner.restore_instance(req['instance_id'],req['backup_path'])
参数解析:
req['instance_id']:需要备份的服务实例的ID;
req['backup_path']:备份服务实例的备份路径。
1.16. Recovery an instance
功能:恢复一个服务实例,如果一个Service Node已经崩溃的话
函数调用:@provisioner.recover(instance_id, backup_path, resp.handles)
参数调用:
instance_id:需要恢复的服务实例ID;
backup_path:该服务实例的备份路径。
备注:在Cloud Foundry中,为服务实例创建一个快照与存储一个服务实例是不一样的效果。快照的意义是记录服务实例的某个时间情况,可以将服务实例回滚后某个时间点,而回滚的前提是该服务实例还存在;当服务实例不存在时(比如Service Node崩溃),那么只能从存储的备份服务实例中找到相应的服务实例,并做到恢复。
1.17. Check orphans
功能:查找orphan
函数调用:check_orphan(resp.handles,)
参数解析:
resp.handles:所有创建完毕的服务实例。
备注:orphan的意思是孤儿,在Cloud Foundry中代表那些在Service Node中已经创建完毕,而且已经在Service Gateway中有记录信息,但是在Cloud Controller中却没有记录信息的服务实例。这些服务实例的存在只会浪费ServiceNode的资源,而在实际Cloud Foundry运行的过程中,它们是不会被用户使用到(因为在Cloud Controller中不存在它们的记录)。而作为“orphan”的这些服务实例的存在是不合理的,需要被清除,而清除的第一步就是查看是否存在这样的“orphan”。
1.18. Purge orphans
功能:挖除orphan
函数调用:@provisioner.purge_orphan(orphan_ins_hash,orphan_binding_hash)
参数调用:
orphan_ins_hash:确认为orphan的所有服务实例的ID,数据类型为hash类型;
orphan_binding_hash:确认为orphan的,在Service Gateway看来已经被绑定,但是Cloud Controller却不知道的服务实例ID,数据类型为hash类型。
1.19. Migrate a service
功能:为特定Service Node上的服务实例进行迁移
函数调用:@provisioner.migrate_instance(params["node_id"], params["instance_id"],params["action"])
参数解析:
params["node_id"]:需要迁移服务实例所在的Service Node ID;
params["instance_id"]:需要被迁移的服务实例ID;
params["action"]:迁移的具体操作类型。
1.20. Get services on a node
功能:获取某个Service Node上所有的服务实例
函数调用:@provisioner.get_instance_id_list(params["node_id"])
参数解析:
params["node_id"]:需要获取的所有服务实例所在的Service Node ID。
2.ServiceGateway的通信机制
由CloudFoundry的平台框架可知,对于Service Gateway来说,需要通信的平台组件只有两个,分别是:Cloud Controller和Service Node。其中Service Gateway与Cloud Controller的通信是通过相互发送HTTP请求的形式来完成的。另外,Service Gateway与Service Node的通信是通过Cloud Foundry的消息中间件NATS来完成的。以下是对这两种通信方式的分析:
2.1. HTTP通信
在Service Gateway中,它总是通过HTTP的方式建立与Cloud Controller的通信。
这样的通信可以分为两类:
(1).Service Gateway从CloudController处获取HTTP请求,并作出相应的处理。
在第一部分讲述Service Gateway的功能时,涉及的20种功能,都是Cloud Controller的发送来管理请求,而他们都是通过HTTP的形式完成的。首先Service Gateway先对这些HTTP请求进行路由,然后将这些请求分发到相应的处理函数出进行处理,最后返回相应的结果。
(2).Service Gateway向CloudController发送HTTP请求,发送的HTTP请求的函数主要有send_heartbeat,send_deactivation_notice,fetch_handles, 以下将更深入的分析这些方法的功能与实现。
功能:Service Gateway向CloudController发送心跳信息,表示该Service Gateway存活,可以接收管理请求。
主要方法实现:
http =EM::HttpRequest.new(@offering_url).post(req)
方法分析:Service Gateway通过HTTP方式向Cloud Controller发送一个post请求,其中Cloud Controller的URL为@offering_url,具体的请求信息被打包在req对象中。以下为req内容:
req=create_http_request(
:head => @cc_req_hdrs,
:body => @svc_json
)
备注:其中发送的heartbeat中。内容包括:该Service Gateway的服务类型,URL(IP和port),描述等。
2.1.2.send_deactivation_notice
功能:Service Gateway向Cloud Controller发送停止信息,表示该Service Gateway已停止工作。
主要方法实现:
http = EM::HttpRequest.new(@offering_url).post(req)
方法分析:Service Gateway通过HTTP方式向Cloud Controller发送一个post请求,其中Cloud Controller的URL为@offering_url,具体的请求信息被打包在req对象中。以下为req内容: req=create_http_request(
:head => @cc_req_hdrs,
:body => @svc_json
)
2.1.3.fetch_handles
功能:Service Gateway向Cloud Foundry发送获取已有服务实例信息的请求(包括创建信息,绑定信息等)。
主要方法实现:
Http =EM::HttpRequest.new(@offering_url).get(req)
方法分析:Service Gateway通过HTTP方式向Cloud Controller发送一个post请求,其中Cloud Controller的URL为@offering_url,具体的请求信息被打包在req对象中。以下为req内容:req=create_http_request :head =>@cc_req_hdrs
2.1.4.update_service_handle
功能:Service Gateway向Cloud Controller发送更新已有服务实例信息的请求(包括创建信息,绑定信息等)。
主要方法实现:
http =EM::HttpRequest.new(@offering_url).get(req)
方法分析:Service Gateway通过HTTP方式向Cloud Controller发送一个get请求,其中Cloud Controller的URL为@offering_url,具体的请求信息被打包在req对象中。以下为req内容: req=create_http_request(
:head => @cc_req_hdrs,
:body => handle_json
)
2.2. NATS通信
在Service Gateway中,NATS通信主要存在于Service Gateway和Service Node的通信中。
NATS是一个基于事件驱动的轻量级,支持订阅和发布的消息中间件系统。作为Cloud Foundry的神经中枢,NATS负责组件之间的通信和交互工作。
NATS在ServiceGateway中的使用主要是创建连接,发送订阅和发布消息。以下是具体实现。
2.2.1.创建NATS连接
Service Gateway具体使用到NATS来订阅和发布消息的程序代码位于provision.rb文件中,而其中Provisioner这个类则是继承于Base这个类。
Service Gateway具体与NATS建立连接的过程则在于Base这个类中。具体代码实现为:
@node_nats= NATS.connect(:uri => options[:mbus])
其中options[:mbus]为NATS server端所在节点的URL。而代码中的NATS是一个程序包,是通过gem包的形式安装在Service Gateway节点处,在程序中是通过require的方式进行引用,引用完毕后就可以使用该gem包中的方法。@node_nats为ServiceGateway与NATS server端创建连接时产生的一个对象,而后续的一些订阅和发布的实现都是通过这个对象来完成的。
2.2.2.发送订阅和发布消息
Service Gateway通过NATS来发送订阅和发布消息,由于在创建与NATS连接的时候,已经产生了一个与NATS server端进行通信的对象@node_nats,所以在每次需要订阅和发布消息的时候,都是引用这个对象进行相应的操作。
关于消息的操作类型主要有以下几种:publish, subscribe, unsubscribe。
Publish主要是某一个组件发布一个消息,这个消息可以被其他组件订阅。
Subscribe主要是某一个组件订阅一个消息,一旦有这样的消息被发布以后,NATS就会将这样的信息分发给订阅者。
Unsubscribe主要是某一个组件不再订阅某一个消息。
在第一章中涉及的ServiceGateway功能中,只要这些功能中会需要Service Gateway和Service Node建立通信,则具体的代码实现中,都需要@node_nats这个对象来实现通信。
以下是在ServiceGateway中需要使用NATS建立通信的地方:
方法check_orphan中:
@node_nats.publish("#{service_name}.check_orphan","SendMe Handles")
主要实现:Service Gateway向NATS发布一个消息,消息的内容为指定类型的Service Node向该Service Gateway发送服务实例的具体信息。
方法purge_orphan中:
@node_nats.publish("#{service_name}.purge_orphan.#{node_id}", req.encode)
主要实现:Service Gateway向NATS发布一个消息,消息的内容为指定类型的Service Node需要挖除指定ID的服务实例。
方法unprovision_service中:
timer =EM.add_timer(@node_timeout) {
@node_nats.unsubscribe(subscription)
blk.call(timeout_fail)
}
主要实现:查看Service Gateway向NATS发送关于Service Node的请求,Service Node是否超时。
@node_nats.request("#{service_name}.unprovision.#{node_id}", request.encode)
主要实现:Service Gateway向NATS发送一个请求信息,这个请求信息主要让NATS负责找到相应的Service Node来删除某一个具体的服务实例。
方法provision_service, bind_instance, unbind_uinstance中的分别有@node_nats.request("#{service_name}.provision.#{best_node}",prov_req.encode),
@node_nats.request( "#{service_name}.bind.#{node_id}",request.encode)和
@node_nats.request( "#{service_name}.unbind.#{node_id}",request.encode)。
这三处的主要实现与unprovision_service的实现原理相同,具体功能略有区别。
方法restore_instance中:
@node_nats.request( "#{service_name}.restore.#{node_id}",request.encode)
主要实现:Service Gateway向NATS发送一个请求信息,这个请求信息让NATS负责找到相应的Service Node来为某一个服务实例创建一个备份。
方法migrate_instance中:
@node_nats.request(channel, message)
主要实现:Service Gateway向NATS发送一个请求信息,该请求让NATS负责找到相应的Service Node来实现服务的迁移。