Cloud Foundry作为业界第一个开源的PaaS解决方案,正越来越多的被业界接受和认可。随着PaaS的发展,Cloud Foundry顺应潮流,充分发挥开源项目的特点,到目前为止,已经支持了大批第三方技术和服务。

在开发框架的支持上,Cloud Foundry支持如今很多主流的开发框架,比如:Spring、Lift、Grails、Play、Rails、Sinatra、Node.js、PHP、Python等。另外,Cloud Foundry还有供用户定义自身代码框架的接口提供,大大扩展Cloud Foundry自身的开发框架。

另一方面,Cloud Foundry已集成较多第三方服务,以供用户扩展应用,比如:数据库服务MySQL、Postgresql、MongoDB、Neo4j、Redis等;存储类服务Vblob、filesystem等;其他类型服务RabbitMQ、ElasticResearch等。另外,Cloud Foundry还有提供用户自定义添加系统服务的接口,大大增强平台本身对服务的可扩展性。

1.   JasperReports简介

报表是企业或组织的基本业务需求,它可以帮助企业或组织管理部门更感观地访问数据,处理数据,查阅数据,并依此深入洞察企业活组织的运营状态。报表往往被认为是企业或组织发展的强大驱动力。

在工业界,JasperReports无疑是一款功能强大,广受欢迎的报表引擎。它可以通过报表模板的设计以及数据源数据的填充,灵活生成诸如PDF、HTML、XML等多种格式的报表。由于JasperReports是用Java开发的开源程序库,因此开发者可以方便快捷的将JasperReports引入开发的应用程序,以完成应用中生成报表模块的功能。

正是由于JasperReports报表引擎的功能强大与应用方便,对于开放的PaaS平台来讲,将JasperReports集成进PaaS平台就变得十分具有意义。它不仅满足应用对于报表的需求,还大大丰富了平台本身提供的功能。

2.   JasperReports service与app形式比较

       将JasperReports集成进CloudFoundry,可以有两种方式:第一种是将JasperReports设计成一项服务,以服务的形式提供给应用来访问;第二种是将JasperReports设计成一个应用程序,直接部署进Cloud Foundry,负责报表的生成。

虽然JasperReports的service形式和app形式在Cloud Foundry中都可行,但是这两种形式在功能和性能上却有着很大的差异。

第一,在功能上,JasperReports的service形式要比app形式强。当用户需要完成一个系统的报表工作时,如果将JasperReports设计成app形式,那么JasperReports作为一个独立的应用程序时,不能和用户的主应用程序融合在一起。可见报表的生成需要用户人为手动操作,也就是说完成一个报表的生成,需要在Cloud Foundry使用两个不同的应用。这种app的形式,大大违反了Cloud Foundry的可操作性,加大了用户使用的难度,使得应用功能的实现变得繁琐不便。

但是如果将JasperReports设计成一项服务集成进Cloud Foundry的话,JasperReports就完全可以和应用程序分离开来,并以服务的形式提供给app应用使用。当在Cloud Foundry上部署的app应用需要JasperReports功能时,只需要创建一个JasperReports服务实例,并将整个服务实例与整个app应用绑定,即可由应用程序来决定是否访问该JasperReports服务实例。这种service的形式,大大简化了JasperReports报表功能的业务逻辑,也简化了用户的操作难度,但也不可避免的加大了应用开发者的开发难度。

第二,在性能上,JasperReports的service形式,最大限度地将报表负载转移到Cloud Foundry的service节点上,实现报表负载与app应用的完美分离,大大缓解app应用的负载压力,降低Cloud Foundry中DEA的负载压力。由于Cloud Foundry的service节点与DEA节点之间低耦合,service节点负责同种类型service的负载,而DEA节点负责该节点上所有app的运行,因此当JasperReports以app形式存在时,进行数据量极大的报表生成时,DEA节点的CPU、内存等负载将大幅提高,不仅影响自身应用的性能,同时还有可能影响同DEA节点的其他app应用性能。

3.   JasperReports中service概念与Cloud Foundry中service概念对应

Cloud Foundry支持的现有服务中,大多数都属于传统的关系型数据库以及NoSQL数据库。在这两类数据库服务中,提供service服务,相当于由数据库server提供数据存储的服务,换言之,也就是由数据库server端创建database,然后将该database转交给Cloud Foundry的app应用使用。

MySQL被认为是一项Cloud Foundry中较为传统的service,以下则讲述该MySQL数据库,描述MySQL在Cloud Foundry中service instance的概念。JasperReports的集成以此为参考,根据相关概念借鉴的可行性,抽象出JasperReports在Cloud Foundry中的service 概念。

以MySQL为例,MySQL数据库向CloudFoundry中的app应用提供MySQL服务,也就是MySQL数据库的server端可以创建MySQL db并将该db的URL传递给app应用,以供app应用使用。在Cloud Foundry中,MySQL的service instance的概念对于与一个MySQL的db,而Cloud Foundry对于MySQL服务所有的管理操作,也只限于db这一层。

为实现将JasperReports作为一项服务集成进CloudFoundry,首先需要将JasperReports以一个server的形式存在于Cloud Foundry中。在调研了JasperReports的相关产品之后,决定选择JasperReports Server作为一个service节点部署于Cloud Foundry。

JasperReportsServer是一款单节点且可嵌入的报表server,它由JasperSoft公司开发,并且开源,支持AGPL协议。它提供的报表业务和分析业务,可以被嵌入至web应用或移动应用中。Web应用可以通过RESTful和SOAP的形式访问JasperReports Server的资源。

选择合适的server端之后,需要将JasperReports中service的概念清晰化,并将其与CloudFoundry中传统的service概念相对应。

由于JasperReportsServer在内部是通过清晰的文件系统来存储资源的,在最终的报表生成时,通过调用指定路径下的资源(如:报表模板jrxml文件,数据源,报表设置等)来完成。为方便起见,将JasperReports的service instance设计成JasperReports Server中文件系统的一个文件夹节点,而用来生成报表的资源(jrxml文件,数据源定义,报表设置等)都存放于该节点下。

以下为JasperReports 与MySQL service instance的对比与参照:

JasperReports

MySQL

service instance

JasperReports Server文件系统节点

MySQL database

instance 内资源

数据源定义,jrxml文件,报表设置

数据表

instance credentials

host,port,username,password,node locatoin

host,port,username,password,dbname

下图为JasperReports在Cloud Foundry中service instance的具体表现形式:

其中,JasperReports service instance是名为bd15e871-7b8d-41d8-a4a9-f6d67eb056dc的文件夹节点,该JasperReports serviceinstance内资源有数据源MonthlyReports,报表模板monthlyreports.jrxml,以及报表配置reports。

当app应用需要访问该JasperReports service instance的时候,只需按要求访问路径/root/SHL/bd15e871-7b8d-41d8-a4a9-f6d67eb056dc下的reports文件,即可生成最终报表。

4.   JasperReports Service 具体实现

JasperReports service的实现首先需要完成JasperReportsservice中的数据表现形式以及存储形式。由于在上一部分以及抽象归纳出JasperReports service的service instance定义,service instance内资源描述以及service instance的credentials信息,因此,对于JasperReports service的存储就更清晰,更容易设计。

在JasperReports service 中,我们使用JasperReports Server的文件系统来存储JasperReports service的service instance。另外,credentials信息的存储,使用Cloud Foundry对传统型服务的credentials的存储方式,在Service Node处,使用sqlite数据库存储,Service Gateway处使用内存存储,Cloud Controller处,使用postgres数据库存储。

CloudFoundry中和service相关的模块主要有CloudController、DEA和NATS。如下图,为简化的Cloud Foundry框架图。

从上图的service模块中,可以看出service模块主要由Service Gateway和Service Node构成。从模块功能来看,Service Gateway主要负责接收Cloud Foundry对于service的管理请求,处理后发送给Service Node执行操作;而Service Node则主要负责接收Service Gateway处理后的请求,并对service instance执行请求操作。

将JasperReports以service形式集成进Cloud Foundry,则需要开发设计JasperReports service的Service Gateway和Service Node。

4.1.   JasperReports Service Gateway实现

在Cloud Foundry中,Service Gateway主要负责接收从Cloud Controller发来的service管理请求,其中包括provision、unprovision、bind、unbind等操作。

在开发过程中,JasperReports Service Gateway主要是继承vcap-service-base中的Base:Gateway类,并且在启动JasperReports Service Gateway创建一个JasperReports_gateway类,并启动。

关于Cloud Foundry中JasperReports Service Gateway的实现过程较简单,大部分的工作全都由Base:Gateway来完成。所以,集成过程中,JasperReport创建了一个启动JasperReports Service Gateway的文件,由Cloud Foundry来调用。文件目录为:~/cloudfoundry/vcap/services/jasper/bin/jasper_gateway,文件内容为:

4.2.   JasperReports Service Node实现

在Cloud Foundry中,Service Node主要负责管理service,其中包括provision、unprovision、bind、unbind等操作,但是Service Node却不是最终service的提供者,service的提供者为该类service的server。

以MySQL为例,MySQL Service Node负责接收Service Gateway通过NATS发来的请求,并根据请求做相应的管理操作,真正的service instance位于MySQL server端。

类比于JasperReports,JasperReports Service Node需要实现接收Service Gateway发来的请求,并根据请求做相应的管理请求,真正的service instance位于该节点处的JasperReports Server端。

ENV["BUNDLE_GEMFILE"] ||= File.expand_path('../../Gemfile',__FILE__)
require 'bundler/setup'
require 'vcap_services_base'
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
require 'jasper_service/jasper_provisioner'
class VCAP::Services::Jasper::Gateway < VCAP::Services::Base::Gateway
def provisioner_class
VCAP::Services::Jasper::Provisioner
end
def default_config_file
File.join(File.dirname(__FILE__), '..', 'config', 'jasper_gateway.yml')
end
end
VCAP::Services::Jasper::Gateway.new.start

4.2.1.     JasperReports Service类图及类功能

整个Service Node节点从代码上来分析,可以分为两个层次结构,一部分是Cloud Foundry提供的Base模板,这部分存在于包vcap-service-base中,该包以gem包的形式被下载至service node节点gem环境下,这部分已经帮助service开发者完成了很多service的开发工作;另一部分是JasperReports Service Node部分,这部分主要针对相应的服务,进行相应的操作,需要service开发人员自行设计与编写相应代码。

以下是JasperReports的 Service Node的类图:

上图中vcap-service-base层次的类都是系统提供的Node节点模板,自定义的JasperReports Service通过继承Nodebin和Node两个类,并实现其中预留的接口,最终完成JasperReport Service集成进入Cloud Foundry。

下表主要介绍了每一个类实现的功能。

类名

功能

Base::Base

提供了整个Service通信框架,主要是提供了NATS的连接与初始化。

Base::Node

实现了Node节点功能模板,预留了大量的抽象方法,便于Service开发者实现自定义的功能。

Base::NodeBin

主要完成Node节点的初始化配置,然后将配置参数传入Base::Node。

Jasper::NodeBin

需要实现Base::NodeBin下配置文件读入,以及所有的参数的初始化。

Jasper::Node

需要实现Base::Node下的抽象方法,实现Node节点的实例创建、管理等操作。

4.2.2.     JasperReports Service Node启动流程

以JasperReports Service为例,以下是该类型Service Node的启动流程:

创建JasperReports:Nodebin实例过程中,主要继承于Base:Nodebin,所以JasperReports:Nodebin继承了Base:Nodebin所有功能。

初始化参数过程中,涉及到Node节点运行过程中需要的所有配置参数,都会从配置文件中读取,并完成初始化。初始化的配置文件变量与初始值如下:

---
plan: free
capacity: 100
local_db: sqlite3:/var/vcap/services/jasper/jasper_node.db
mbus: nats://localhost:4222
base_dir: /var/vcap/services/jasper/
index: 0
logging:
level: debug
pid: /var/vcap/sys/run/jasper_node.pid
node_id: jasper_node_1
host: 10.10.17.36
port: 8081
user: jasperadmin
password: jasperadmin
supported_versions: ["1.0"]

创建JasperReports:Node实例,主要是为了创建一个最终会对service进行操作的类,随后,所有一切关于service的管理操作,都由JasperReports:Node这个类接收请求,并执行。

以上三阶段的操作都是在文件jasper_node文件中实现,具体代码如下:

#!/usr/bin/env ruby
# -*- mode: ruby -*-
# Copyright (c) 2009-2011 VMware, Inc. ENV["BUNDLE_GEMFILE"] ||=File.expand_path("../../Gemfile", __FILE__)
require 'bundler/setup'
require 'vcap_services_base'
$LOAD_PATH.unshift(File.expand_path("../../lib",__FILE__))
require "jasper_service/jasper_node"
class VCAP::Services::Jasper::NodeBin <VCAP::Services::Base::NodeBin
def node_class
VCAP::Services::Jasper::Node
end
def default_config_file
File.join(File.dirname(__FILE__), '..', 'config', 'jasper_node.yml')
end
defadditional_config(options, config)
options[:host] =parse_property(config, "host", String)
options[:port] =parse_property(config, "port", Integer)
options[:user] =parse_property(config, "user", String)
options[:password] =parse_property(config, "password", String)
options
end
end
VCAP::Services::Jasper::NodeBin.new.start

连接至NATS,主要完成Service Node与Cloud Foundry中消息中间件通信的工作。

订阅主题,主要完成向消息中间件NATS订阅相应的主题,以便NATS接收到类似主题的时候,会将请求转发给该Service Node。

添加周期性任务,就是在Node节点正常运行中需要定期执行的工作,包括向Service Gateway发送本节点信息,以及想Component组件注册信息。

 

4.2.3.     JasperReports Service Node运行流程

JasperReports Service Node在初始化完成之后,在运行过程中,除了添加两次周期性任务之外,最主要的任务就是负责JasperReports service instance的管理,其中包括provision、unprovision、bind、unbind等。

以下主要从provision和bind两个操作来说明JasperService Node关于操作请求的运行流程。

4.2.3.1.    Provision a JasperReportsservice instance

创建JasperReports service instance使用的主题是provision主题,创建完毕的serviceinstance并未与app应用进行绑定。

一个JasperReports service instance的创建流程大致如下图:

(1).   用户使用vmc等管理工具向Cloud Controller发送创建JasperReports service instance的请求;

(2).   Cloud Controller根据请求要求对应的JasperReportsService Gateway来处理该任务;

(3).   JasperReports Service Gateway调用provision_service()方法从管理节点中选择合适的节点,并通过NATS向该节点发送provision;

(4).   Base:Node中on_provision方法接收这个主题的请求;

(5).   调用provision方法创建JasperReports service instance;

(6).   执行创建一个JasperReports service instance;

(7).   一旦创建成功JasperReports:Node返回一个credentials信息给Base:Node;

(8).   Base:Node返回一个正确的编码信息给Gateway,以示创建成功,另外将credentials信息返回给Gateway。

4.2.3.2.     JasperReports:Node 中provision方法

添加自定义的JasperReportsservice进Cloud Foundry,其中provision方法显得尤为重要。

Provision方法主要是由JasperReports:Node执行,执行的对象是JasperReportsserver。

由于JasperReportsserver是一个基于web service的server端,并且支持RESTful和SOAP请求,因此可以在provision方法中实现向JasperReports server发送RESTful的HTTP请求,并通过请求的response信息来确定provision的成功与否。

关于HTTP请求中所需的host、port、username、password、URL信息均在初始化参数时,从配置文件中读得,并存在于实例变量中。

另外,在创建一个JasperReportsservice instance的时候,需要使用一个ruby的数据库映射框架。创建的service instance会存入相应的数据库,也就是说,在JasperReports Node端会将创建service instance的信息进行备份,以备后来bind以及其他请求的需要。

下图为provision过程中JasperReports:Node与JasperReportsserver的交互示意图。

在Service Node接收到provision aservice请求的时候,惠子啊provision方法中调用方法create_resource方法来实现,该方法的具体代码实现如下:

def create_resource(provisioned_service)
name= [:name].map { |field|provisioned_service.send(field) } name=name[0]
begin
@logger.debug("start creatingresource #{name}")
response_login =
RestClient.post("http://#{@host}:#{@port}/jasperserver/rest/login",
{:j_username=> @user,
:j_password=> @password}
) if response_login.code != 200
@logger.debug("401:Unauthorized by JasperReports Server")
return false
end resource_descriptor="<resourceDescriptorname='"+name+"' wsType='folder' uriString='/SHL/"+name+"'isNew='false'>
<label>"+name+"</label>
<resourcePropertyname='PROP_PARENT_FOLDER'>
<value>/</value>
</resourceProperty>
</resourceDescriptor>" @logger.debug("resource_descriptor:#{resource_descriptor}")
#get resourceDescriptor before generatethe report
response_resource =RestClient.put("http://#{@host}:#{@port}/jasperserver/rest/resource",
resource_descriptor,
{:cookies =>{"JSESSIONID" => response_login.cookies["JSESSIONID"]}}
) if response_resource.code == 201
@logger.debug(" 201: Resourcecreated successfully")
return true
else
@logger.debug(" Failedcreating resource }")
return false
end
end
end

4.2.3.3.     JasperReports:Node 中bind方法

在Cloud Foundry中,bind操作意味着一个app应用与一个serviceinstance执行绑定,以便app应用在运行过程中,可以通过该service instance的credentials来访问这个service instance。

app应用和JasperReportsservice instance的绑定过程类似于JasperReports service instance的provision,也是通过NATS接收到主题,并由JasperReports:Node进行相应的操作。

bind方法也是在CloudFoundry中预留的开发接口,开发者可以根据自己的需求灵活编写这个接口。

JasperReportsservice 的bind操作的具体流程主要如下:

(1).   从数据库中找出存储的JasperReports service instance;

(2).   创建一个数据库存储绑定的app的信息,以备份信息;

(3).   将结果信息返回给JasperReports Service Gateway。

当bind请求的response到达ServiceGateway之后,Service Gateway将credentials信息和app应用做成一个droplet。当Cloud Foundry启动这个app应用时,相应的credentials信息已被写入该app应用的环境变量中,最终Cloud Foundry中的app应用通过这些信息直接访问JasperReports service instance。

以下为bind的具体代码实现:

def bind(name,binding_options, credential = nil)
instance = nil
if credential
@logger.debug("binding has a credential")
instance =get_instance(credential["name"])
else
@logger.debug("binding has NO credential")
instance = get_instance(name)
end
gen_credential(instance)
end

4.2.4.      配置CloudFoundry调用JasperReports service的 Service Gateway与Service Node启动脚本

JasperReportsservice的Service Node和Service Gateway设计完成并实现后,需要将这两个组件的启动添加到Cloud Foundry。

这一部分所需要做的工作并不多,主要还是让Cloud Foundry在启动组件的时候,检测到JasperReports service的Service Node以及Service Gateway。并到相应的目录下,找到Service Node和Service Gateway的启动脚本并执行,具体的脚本启动文件的目录为:~/cloudfoundry/vcap/services/jasper/bin/jasper_gateway和~/cloudfoundry/vcap/services/jasper/bin/jasper_node。以上为启动流程,停止等操作也是相同的原理。

CloudFoundry中注册的组件在vcap_components.json文件中,路径为:~/cloudfoundry/.deployment/devbox/config/vcap_components.json。在其中添加JasperReportsservice的Service Node和Service Gateway。

另外在执行的时候,CloudFoundry会执行vcap_components.rb文件。在该文件中,有具体启动时,需要启动的组件,则在该部分添加需要的Service Node和Service Gateway。具体代码如下:

## services: gateways & nodes
%w(redis mysql mongodb rabbitmq postgresql vblob neo4j memcached couchdb elasticsearch filesystemjasper).each do |service|
ServiceComponent.register("#{service}_gateway")
end %w(redis mysql mongodb rabbitmq postgresql vblob neo4j memcached couchdb elasticsearchjasper).each do |service|
ServiceComponent.register("#{service}_node")
end

5.   访问JasperReports Service的app应用案例

为体现JasperReportsservice 在Cloud Foundry中的可用性,需要4个步骤来体现:

(1).   用户发送provision a JasperReports service 的请求,Cloud Foundry创建一个JasperReportsservice instance;

(2).   用户对该JasperReports service instance进行设计,如添加数据源,添加报表模板和设置报表等;

(3).   用户发送请求,将该JasperReports service instance和部署在Cloud Foundry上的app应用进行绑定;

(4).   App应用运行过程中访问该JasperReportsservice instance。

由于CloudFoundry很好的支持Rails框架的应用程序,因此为验证JasperReports service的可用性,我编写了一个rails应用部署在Cloud Foundry上,并和provision完毕并内容设计完毕的JasperReports service instance进行绑定,最终成功完成app应用对JasperReports service instance的访问。

以下为简单的应用部署示意图:

1.通过CloudFoundry创建一个jasper的service instance,该演示案例中service instance名为:jasper-ca485 。

2.开发用户进入JasperReportsServer中进行service instance设计,包括上传数据源和报表模板,还有最终报表的配置,简要操作流程如下:

首先添加数据源,如图:

然后上传报表模板:

最后需要配置报表,主要为配置报表模板文件以及配置数据源关联,如下图:

通过以上步骤,关于JasperReportsservice instance就配置全部完成,接着需要app来访问。

3.将该JasperReportsservice instance与app进行绑定,绑定过程如下:

4.绑定完毕之后,app就可以运行了,下图为app应用的运行情况示意图:

5.下图为点击链接“Generatea report”所得到的结果:

6.当点击上述链接之后,可以在相应的app应用程序目录下产生一个报表文件report.pdf。

由于bind过程中,CloudFoundry只是将service instance的credentials信息写入app的环境变量中,所以在app运行的时候,很重要的一步就是要将app环境变量中关于credentials的信息读取出来,并供app应用需要的时候访问。

以下为以上演示案例中读取环境变量的ruby代码实现:

start = ENV["VCAP_SERVICES"].index('credentials')
original_length=ENV["VCAP_SERVICES"].length
credentials=ENV["VCAP_SERVICES"][start+14..original_length-5]
elements=credentials.delete('"').split(",")
a={}
elements.each do |element|
details=element.split(":")
a[details[0]]=details[1]
end
@host = a["host"]
@port = a["port"]
@name = a["name"]

 

6.   Cloud Foundry中通用service集成

上文已经谈到CloudFoundry已经集成了很多第三方的中间件服务,并且提供了用户添加自定义服务的接口。随着Cloud Foundry的发展,开发者势必会将更多的服务集成进Cloud Foundry,以供app使用,也扩展了app的功能。

本部分主要描述通用service集成进入CloudFoundry所需要做的设计以及实现。

6.1.    service概念的对应

将通用的service类型集成进CloudFoundry,需要做的第一个也是最重要的工作,就是须将待集成service的多种概念与Cloud Foundry中对于service的概念进行对应。

CloudFoundry中这些概念包括:service instance,credentials,provision和bind等。

CloudFoundry中service概念有很多,现将以上四种最为主要的概念进行具体的阐述:

  • service instance

serviceinstance是一项服务的具体事例。它是Cloud Foundry对于service操作的最终载体,存在于Cloud Foundry的Service Node中,上一层的管理者为Service Node。在实际应用过程中,service instance由Cloud Foundry上运行在DEA中的app访问,并且一个service instance可以被多个app同时访问。通用service的集成的首要任务就是,在通用service中抽象出service instance的具体表现形式。

  • credentials

credentials是serviceinstance的认证信息。当创建一个service instance的时候,Cloud Foundry会为这个service instance创建认证信息,也就是credentials,它的作用是:作为app访问这个service instance所必需的认证信息。在创建service instance的时候,Cloud Foundry将产生的credentials存在Cloud Controller,在执行app与service instance的时候,Cloud Foundry会重新生成一个credentials信息,然后由Cloud Controller在绑定app的时候,将这个credentials写入该app的环境变量中,以供app访问,app通过手持credentials信息直接通过RESTful接口,访问位于Service Node的service instance。通用service的集成中,credentials起到使得service instance之间互相独立的作用,另外还起到app访问service instance的凭证信息。

  • provision a service

provision a service是指,在Cloud Foundry中在相应的Service Node上创建一个service instance的过程。其中主要包括两个方面的操作:第一,在Service Node创建service instance;第二,将service instance的credentials的信息传递给Cloud Controller,并对该数据进行持久化。通用service 的集成过程中,首先要实现的就是provision a service的工作,只有实现provision,才会有service instance的概念,并有之后对于service instance的种种操作。

  • bind a service

bind a service是指,在CloudFoundry中app应用需要使用一项service时,app发送请求绑定一个或多个service instance,并最后完成绑定的整个过程。bind a service是app访问service instance之前最后一步Cloud Foundry接管的操作。bind a service具体的操作是Cloud Foundry将serviceinstance的crdentials信息在app应用打包的时候,写入app的环境变量中,最终由app应用启动会被读取,app通过这个crdentials信息直接访问service instance。在通用service的集成过程中,bind是最为重要的步骤之一,只有bind成功后,service instance才有存在的意义。

6.2.    通用service的迁移

在明确了CloudFoundry中service的概念之后,紧接着就是将通用service向Cloud Foundry中迁移的问题。

在Cloud Foundry中,将通用service集成进来,有两个方面需要设计实现:第一,service数据的表现形式与存储形式;第二,service整体框架的设计与实现。

6.2.1.      Service数据表现形式与存储形式

Service作为服务存在于CloudFoundry中,有很多种不同的类型。每一种类型的service在实现过程中,service instance都会以某种表现形式存在,Cloud Foundry对于service的操作全部都是限于这个service instance,关于service instance内部的具体操作,都是由app在访问这个service instance过程中来完成。

传统的关系型数据库中,serviceinstance的表现形式就是关系型数据库的一个db。这个db的创建由关系型数据库的server来创建。

这里db作为一种serviceinstance的表现形式,在Cloud Foundry中大多数的数据库服务中都有体现,不论是关系型数据库或者是NoSQL数据库。db的表现形式,只是Cloud Foundry中最常用的service表现形式,另外其他的service表现形式还有很多,也可以由Cloud Foundry service集成人员自行定义。

Service数据表现形式定义完备之后,还需要设计完成Cloud Foundry中service相关信息内容的具体存储形式。

首先,设计实现serviceinstance的存储,是Cloud Foundry中关于service存储的首要任务。Service instance的存储形式,主要是提供一个可靠的环境供Cloud Foundry中app的访问。传统的关系型数据库以及NoSQL数据库都是以db为表现形式,以db形式存储于存储介质中,具体的组织由数据库server端接管并存储。另外,service instance可以根据应用场景的不同,根据抽象出的service instance概念,将service instance存储于其他的系统中,比如某些文件系统中等。

其次,service相关信息的存储,还包括在service操作过程中产生的重要信息的存储。例如,在service instance创建完毕之后,产生的credentials需要在多个地方进行存储。在传统的关系型数据库及NoSQL数据库中,credentials在Service Node,Service Gateway以及Cloud Controller处均有存储。其中,credentials在Service Node处的存储是另外维护一个db来存储所有service instance的crdentials,而在Service Gateway处的存储是直接存储在内存中的一个hash队列中;最终在Cloud Controller处也会对credentials信息进行持久化,存储在Cloud Controller的postgres数据库中。

6.2.2.      Service框架实现

在Cloud Foundry中集成通用service的框架实现主要包括两个方面:ServiceNode实现与Service Gateway实现。当以上两个框架设计实现完毕之后,还需要将这个通用service的Service Node与Service Gateway作为Cloud Foundry的两个组件,将这两个组件的启动添加至整个Cloud Foundry的启动脚本中,以便Cloud Foundry的自动化启动。

6.2.2.1.     Service Node实现

ServiceNode的功能主要是实现接收Service Gateway的请求,并最终向service server发送最终关于service instance的操作请求,并将操作结果保存并返回给Service Gateway。

ServiceNode的实现主要包括两个层次,一个层次是该类型service的Service Node模块实现,另一个层次是所有service的基类Service Base的实现。

首先关于基类ServiceBase的实现,通用service继承开发者不需要做很多,在Cloud Foundry的安装过程中,基类已经以Gem包的形式被安装至该类型Service Node的节点处。

要集成的service的Service Node则须集成开发者自行设计并实现。在该部分的设计实现中,除了基本的配置初始化,添加周期任务,连接消息中间件只为,最为重要的就是如何与更底层的service server的通信,这体现在provision与bind等方法的具体实现上。如果底层service server对外暴露接口,则调用接口的具体实现就在provision和bind等方法中。

6.2.2.2.     Service Gateway实现

ServiceGateway的功能主要是从Cloud Controller处接收对于service instance的操作请求,将请求进行初步处理,通过处理后的结果,给相应的Service Node发送对于service instance的操作请求。

ServiceGateway的实现主要包括两个层次,一个层次是该类型service的Service Gateway模块实现,另一个层次是所有service的基类Service Base的实现。

所以通用service的ServiceGateway的实现,是由相应的Service Gateway创建一个继承Service Base的类。传统的关系型数据库以及NoSQL数据库服务,由于业务逻辑的缘故,只需要继承所有的Service Base类中的方法,即可实现所有的Service Gateway功能。

但是当继承某一项service的时候,由于service概念的差异,在ServiceGateway处具体实现的时候,并不是按着Service Base的业务逻辑来运行,所以需要在该类型service的Service Gateway处进行重写方法,或者新增方法实现。

6.2.2.3.     Service Node与Service Gateway的启动

一旦将需要集成的service的Service Node和ServiceGateway设计完成并实现后,则需要将这两个组件的启动添加到Cloud Foundry。

这一部分所需要做的工作并不多,主要还是让Cloud Foundry在启动组件的时候,检测到集成进去的service的Service Node以及Service Gateway。并到相应的目录下,找到Service Node和Service Gateway的启动脚本并执行。以上为启动流程,停止等操作也是相同的原理。

CloudFoundry中注册的组件在vcap_components.json文件中,路径为:~/cloudfoundry/.deployment/devbox/config/vcap_components.json。在其中添加需要继承service的Service Node和ServiceGateway。

另外在执行的时候,CloudFoundry会执行vcap_components.rb文件。在该文件中,有具体启动时,需要启动的组件,则在该部分添加需要的Service Node和Service Gateway。具体代码如下:

## services: gateways & nodes
%w(redis mysql mongodb rabbitmq postgresql vblob neo4j memcached couchdb elasticsearch filesystem).each do |service|
ServiceComponent.register("#{service}_gateway")
end %w(redis mysql mongodb rabbitmq postgresql vblob neo4j memcached couchdb elasticsearch).each do |service|
ServiceComponent.register("#{service}_node")
end

7.   总结

本文主要介绍了JasperReports报表引擎,JasperReports报表引擎在PaaS平台CloudFoundry中集成的可能性,以及JasperReports作为一项service集成进Cloud Foundry的技术实现,最后对Cloud Foundry中通用service的集成进行描述与分析。

由于JasperReports报表引擎与传统CloudFoundry中service概念存在差异性,故本文首先对于JapserReports 报表引擎的service概念进行抽象化,以符合Cloud Foundry对于service接口的要求。

本文还分析了CloudFoundry中service 模块中主要类的架构,并在这种类架构下进行集成JasperReports service。集成过程中使用JasperReports server作为JasperReports service instance的提供者。通过JasperReports Service Node发送基于RESTful的HTTP请求,进行创建JasperReports service instance;随后完成了unprovision、bind、unbind等操作的代码实现。

另外,JasperReports作为一项service集成进入Cloud Foundry,该service的备份、迁移和恢复等操作都十分具有实际意义,然而由于对JasperReports server中service instance的备份和迁移还没有抽象出有效可行的方案,故现有的设计中暂不支持对于已创建JasperReports service instance的备份、迁移和恢复等操作。

本文在最后阶段还总结了通用service想要集成进入CloudFoundry需要涉及的设计问题以及实现问题。首先需要完成service概念的对应,然后再完成Service Node和Service Gateway的框架以及代码实现,最后将启动脚本嵌入Cloud Foundry中。

转载清注明出处。

这篇文档更多出于我本人的理解,肯定在一些地方存在不足和错误。希望本文能够对开始接触Cloud Foundry中service的人有些帮助,如果你对这方面感兴趣,并有更好的想法和建议,也请联系我。

我的邮箱:shlallen@zju.edu.cn

新浪微博:@莲子弗如清

Cloud Foundry中 JasperReports service集成的更多相关文章

  1. Cloud Foundry中通用service的集成

    目前,CloudFoundry已经集成了很多第三方的中间件服务,并且提供了用户添加自定义服务的接口.随着Cloud Foundry的发展,开发者势必会将更多的服务集成进Cloud Foundry,以供 ...

  2. Cloud Foundry中warden的网络设计实现——iptable规则配置

    在Cloud Foundry v2版本号中,该平台使用warden技术来实现用户应用实例执行的资源控制与隔离. 简要的介绍下warden,就是dea_ng假设须要执行用户应用实例(本文暂不考虑ward ...

  3. Cloud Foundry中DEA启动应用实例时环境变量的使用

    在Cloud Foundry v2中,当应用用户须要启动应用的实例时.用户通过cf CLI向cloud controller发送请求,而cloud controller通过NATS向DEA转发启动请求 ...

  4. Cloud Foundry中vmc tunnel与caldecott原理

    在Cloud Foundry中,用户可以vmc create-service创建一个service instance,但是常规情况下,用户不能手动地进一步对service instance进行设计.以 ...

  5. Cloud Foundry中gorouter对StickySession的支持

    Cloud Foundry作为业界出众的PaaS平台,在应用的可扩展性方面做得很优秀. 详细来讲,在一个应用须要横向伸展的时候,Cloud Foundry能够轻松地帮助用户做好伸展工作,也就是创建出一 ...

  6. Cloud Foundry中DEA与warden通信完毕应用port监听

    在Cloud Foundry v2版本号中,DEA为一个用户应用执行的控制模块,而应用的真正执行都是依附于warden. 更详细的来说,是DEA接收到Cloud Controller的请求:DEA发送 ...

  7. Cloud Foundry技术资料汇总

    来自:http://cnblog.cloudfoundry.com/2012/05/ 本文是Cloud Foundry的一个简单上手指南和资料汇总,内容将根据产品的发布定期更新. Cloud Foun ...

  8. Cloud Foundry和微服务Meetup重磅来袭

    CF 同学们: Cloud Foundry 2016 上海 Meetup 将在10月22日在上海港汇广场进行! 想要参会的小伙伴,请直戳  ~ 在过去的一年,CF 的技术有很多进展,微服务也是2016 ...

  9. Cloud Foundry warden container 安全性探讨

    本文将从Cloud Foundry中warden container的几个方面探讨warden container的安全性. 1. warden container互訪 1.1.  互訪原理· 在Cl ...

随机推荐

  1. Gap Locks 区间锁

    Gap Locks 区间锁 1. 区间锁不能用于语句锁定记录使用一个唯一索引来搜索一个唯一的记录 2.READ COMMITTED 没有区间锁 区间锁是一个锁在一个在index记录间的区间,或者一个l ...

  2. Linux系统下用户行为审计

    以下内容在RHEL 6.4下测试通过. 1.编写脚本Command_history.sh,生产历史命令记录文件,内容如下 #!/bin/bash [ -d /usr/lib/.cmdlog ] || ...

  3. 【转】 jni.h头文件详解(二)

    原文网址:http://blog.csdn.net/shaohuazuo/article/details/42932813 作者:左少华 博客:http://blog.csdn.net/shaohua ...

  4. 【拓扑】【宽搜】CSU 1084 有向无环图 (2016湖南省第十二届大学生计算机程序设计竞赛)

    题目链接: http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1804 题目大意: 一个有向无环图(DAG),有N个点M条有向边(N,M<=105 ...

  5. 《算法问题实战策略》-chaper21-树的实现和遍历

    这一章节开始介绍一个数据结构中的一个基本概念——树. 我们从数据结构的解读来解释树结构的重要性,现实世界的数据除了最基本的线性结构(我们常用队列.数组和链表等结构表征),还有一个重要的特性——层级结构 ...

  6. angularJS 服务三

    路由 一 简介 1.路由机制 为了实现无刷新的视图切换,我们通常会用ajax请求从后台取数据,然后套上HTML模板渲染在页面上,然而ajax的一个致命缺点就是导致浏览器后退按钮失效,尽管我们可以在页面 ...

  7. Spring Boot(spring mvc升级版)

    周末挤出了一些时间,学了点东西,总结了一下,可能还有自己理解不到位的地方,希望大家一起交流学习,好东西要大家一起分享嘛~.时间有点紧张,所以样式没有来及做很好的调整,大家就凑活着看吧. Spring ...

  8. 一、cocos2d-x 3.0 final使用httpclient编译到android,须要用到的android.mk

    今天写一个网络框架,在vs上面非常欢快的执行车,心想,尼玛!cocos2d-x 3.0这么方便,预计不久的将来我就能回家种地了,由于不用程序猿了,直接cocos2dstudio拖界面了= =!!. 写 ...

  9. PHP ORM框架与简单代码实现(转)

    对象关系映射(Object Relational Mapping,简称ORM)是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术. 简单的说,ORM是通过使用描述对象和数据库之间映射的元数据 ...

  10. MYSQL 体系结构图-log (踏雪无痕) (UC技术博客)

    http://www.cnblogs.com/chenpingzhao/category/690116.html http://www.cnblogs.com/chenpingzhao/p/51074 ...