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的人有些幫助,如果你對這方面感興趣,並有更好的想法和建議,也請聯繫我。

我的郵箱:[email protected]

新浪微博:@蓮子弗如清 


 

發佈了47 篇原創文章 · 獲贊 10 · 訪問量 22萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章