zoukankan      html  css  js  c++  java
  • Cloud Foundry中 JasperReports service集成

           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

    新浪微博:@莲子弗如清 

  • 相关阅读:
    Linux w命令
    01.drf文档及外键字段反序列化
    redis的参数解释
    redis集群复制和故障转移
    codis原理及部署_01
    redis 主从哨兵02
    redis 主从哨兵01
    redis持久化
    redis python操作
    redis cluster
  • 原文地址:https://www.cnblogs.com/aukle/p/3235299.html
Copyright © 2011-2022 走看看