(浙江工業大學 計算機科學與技術學院,浙江 杭州 310023)
軟件復用被認為是解決軟件危機、實現軟件產業工業化生產方式的有效途徑。軟件產品線(Software product lines, SPLs)是實現系統化復用的基礎,它采用以資產庫為核心的架構平臺和完備的資產開發和管理工具,支持以復用為目的的組件設計、開發和維護,通過大粒度的組件裝配完成產品建造。
微服務架構是一種新興的軟件架構風格。它將軟件系統分解成一個個微型且互相連接的服務,每個服務都有自己特定的功能,使得整個系統變得更加模塊化。當系統需求發生變化時,只需改變服務的組合模式即可。但是系統的模塊化不代表各模塊完全獨立,相當多的服務需要在多個模塊配合下完成。當這些服務發生需求變化后,還是需要進行一些復雜的連帶的系統變動。因此,對于微服務架構軟件的可變性建模(Variability modeling)變得十分重要。筆者在特征模型和用例模型的基礎上,將可變性建模方法應用到目前廣泛應用的微服務架構中。通過其可變性建模方法中的可變性管理特性,來降低需求變更而導致的工作量。
復雜而巨大的單體式應用不利于持續性集成與部署。許多公司,比如Amazon,eBay,NetFlix,通過采用微處理結構模式解決上述問題。其思路是將應用分解為小的、互相連接的微服務。一個微服務一般完成某個特定的功能,比如下單管理、客戶管理等等。每一個微服務都是微型六角形應用,都有自己的業務邏輯和適配器。一些微服務還會發布API給其他微服務和應用客戶端使用[1-2]。
在微服務架構中,軟件系統各組件之間通過服務契約關系相互合作,簡單講就是服務輸入輸出的語義。在Restful service的實現中,消費者只需知道服務的根資源的URI,就可以由根資源引導到所需的資源。即消費者和發布者的耦合只在于根資源的URI以及各資源及其操作的語義。Restful service充分利用了HTTP協議本身語義的無狀態性,在調用一個接口(訪問、操作資源)的時候,可以不用考慮上下文,不用考慮當前狀態,極大的降低了復雜度,更容易開發、理解和維護。
軟件產品線是指具有一組可管理的公共特性的軟件密集性系統的合集,這些系統滿足特定的市場需求或任務需求,并且按預定義的方式從一個公共的核心資產集開發得到。基于軟件產品線的系統,需要按照指定方式進行公共資產庫的開發。與獨立開發、從零開始開發、隨機開發等方式相比較,可以獲得顯著的生產經濟效益。每個產品都由來自公共資產庫中的組件組成,然后按照預先定義的變化機制,如參數化或繼承,對這些組件進行必要的裁剪,添加任何必須的新組件,根據一個產品線范圍內的公共架構來組裝這些組件。
可變性建模是軟件產品線工程中的核心步驟。軟件產品線工程允許通過定義產品之間的共性和差異,在單個開發周期中開發多個軟件產品[3]。它將產品表示為一系列功能的組合,因此正好適用于微服務模塊化的特性。
可變性建模方法通過特征模型和用例模型來表現其可變性[4]。卡內基梅隆大學提出的FODA采用樹形結構將特征進行模塊化,通過特征之間的關聯進行領域分析。之后在FODA的基礎上也提出了許多特征建模方法。如Zhang提出的基于角色的特征模型組件方法[5],以及Rahman等提出的基于貝葉斯網絡的特征建模方法[6]。在用例模型方面,羅代忠等提出基于UML軟件產品線建模方法[7],將統一建模語言UML中的用例圖應用到可變性建模當中。張元鳴等提出了一種管理導向的可變性建模方法,在特征和用例的基礎上引入了管理導向的理念[8-10]。但這些方法都傾向于采用圖形元素的方式來描述可變性。
ABS(抽象行為規格化)是一種用于建模分布式的、面向對象的系統的建模語言,它是完全可執行的,并且高度支持可變性建模,因而特別適用于軟件產品線工程。歐盟2008年啟動ABS研發工作,并將其用于歐盟的“高可適應及可信任軟件”(Highly adaptable and trustworthy software, HATS)項目中[11]。ABS模型所包括的5 個語言層為
1)Core(核心)ABS:提供功能化的和面向對象的編程構造,用于說明對軟件產品線上所有產品都具有的“內核”產品。
2)特征模型:定義軟件產品線的特征及其相互依賴。
3)Delta(增量)模型:定義特征的實現。Delta模型通過在內核模型之上執行添加、刪除、修改操作(涉及類、接口、屬性、方法)來實現特征。
4)產品線配置:定義Delta模型和特征模型之間的連接關系。一個Delta可以被用在多個特征上,一個特征可能由多個Delta實現。產品線配置定義了兩者之間的關系。
5)產品選擇:將產品定義為特征的集合。
與面向對象的編程語言相比,ABS從數據結構的低級實現選擇中抽象出來。與UML圖等面向設計的語言相比,它可以執行并模擬系統的控制流程;通過分離執行的資源成本和(虛擬)位置的資源能力之間的關注來支持部署建模,可以在模型內部進行部署決策,因此特別適用于使用云API與云供應層進行交互的場合。
軟件產品線工程包含領域工程和應用工程兩個部分。領域實現是基于領域分析實現可重用工件的過程,在ABS中,此過程由核心和增量實現。核心是包含產品共性實施的基本產品。增量是一套實現產品變化的Delta模塊。增量模塊用于實現特征模型中的功能。Delta模塊和功能之間的相關性由產品線配置協調。其增量模型舉例描述如下:
Delta AlternativePath;
modifies class ClientJobImpl {
modifies Maybe
id = “data2/” + id;
Maybe
return res;
}
}
上述例子描述了一個ClientJobImpl的一個增量實現,和已有軟件模塊的不同之處是,它操作保存在另一文件夾里的備份數據。該例顯示它需要修改ClientJobImpl類中的方法file,這個方法帶有一個字符串參數id,并調用該類已有的方法。
與其他基于數學符號表示的可變性建模方法不同,ABS采用了類似編程語言的方式描述組件的可變化特性和可替換特性,可以更加迅速地應用于開發實踐。筆者在ABS語言的基礎上,針對web應用,提出了service Delta特征工程方法。
定義1特征集可表示為一個七元組,即
FG=
式中:Fname代表特征名稱,用以保證特征的唯一性;Ftype代表特征可選類別,即強制(Mandatory)和可選(Optional)兩種類型;Flayertype代表特征層級,包括根特征(Root)、對象特征(Object)、路由特征(Route)以及功能實現特征(Implement);Fdescription代表特征描述,用于對該特征功能進行描述;Fselect代表特征選擇情況;M代表該特征所屬模塊;Fc代表特征所包含的約束集,描述了該特征與其他特征之間的連接關系。
定義2特征間的關系可以表示為一個三元組,即
RG=
也可以表示為
式中Rtype表示特征約束或者關系類型,包括父子特征關系(如parent,child,aggregation,generalization,implemented-by)以及約束關系(如exclude,require),選擇關系(如alternative,mutiple)等等,比如
代表F1特征與F2特征之間存在需要關系。若
則代表F3與F4兩個特征之間擁有多選一的關系。
定義3模塊視圖集可定義為一個三元組,即
MG=
式中:Mname代表模塊名稱;Mselect代表模塊的選擇情況;Mtype用于表示該模塊為強制模塊還是可選模塊。每個模塊視圖下都會包含一個從根特征出發的特征樹,根特征中的Fselect屬性和Ftype屬性與模塊視圖中的Mselect屬性和Mtype屬性相互對應。
定義4特征模型定義為一個六元組,即
FM=
FT={Mandatory,Optional}
FLT={Root,Object,Route,Implement}
RT={parent,child,aggregation,generalization,implemented-by alternative,multiple,exlcude,require}
式中:FG表示構建特征模型所需的特征集合;MG表示特征模型中包含的模塊視圖集合;RG表示特征之間的約束集合;FT表示特征選擇類型集;FLT表示特征層級類型集;RT表示約束類型集。
由于領域特征模型在構建過程中往往存在特征節點繁多且依賴關系復雜的問題,使得特征建模時不可避免地出現相互矛盾的關系,需要對模型中的約束關系進行檢查。筆者構建了8 條領域特征模型約束規則來保證領域特征模型的完整性以及一致性,具體約束定義如下:
1)特征唯一性,即特征集合中的各個特征名稱互不相同,以維護特征的唯一性,其計算式為
?f1,f2∈FG,?f1.Fname=f2.Fname
2)對于所有約束,其起始特征與目標特征不能相同,即
?r∈RG,r.Fformer=r.Flatter
3)關系集合中所有處于關系中的特征都必須來源于特征集合中,即
?r∈RG,r.Fformer∈FG∧r.Flatter∈FG
4)除了根特征之外,特征集合中余下的所有特征都包含有父特征,即
?f∈FG,?r∈f.Fc∧f.Ftype≠root,?f2∈FG→r.Fformer=f2∧r.Rtype=parent
5)特征集合中的任意兩個特征,不能同時存在需要和互斥兩種關系,即
?r1,r2∈RG,?f1,f2∈FG→((r1.Fformer=f1)∧(r1.Flatter=f2)∧r1.Rtype=require)∧((r2.Fformer=f1)∧(r2.Flatter=f2)∧r2.Rtype=exclude)
6)對于特征集合中的同一批特征,它們不能同時擁有多選一以及多選多兩種約束機制,即
?f∈FG,?r1,r2∈f.Fc,?f2∈FG→(r1.Flatter=f2)∧(r1.Rtype=alternative)∧(r2.Flatter=f2)∧(r2.Rtype=multiple)
7)在特征集合中兩個強制特征之間不能存在排斥關系,即
?f1,f2∈FG,?r∈RG→r.Fformer=f1∧r.Flatter=f2∧r.Rtype=exclude∧f1.Ftype=Mandatory∧f2.Ftype=Mandatory
8)在多選一特征集合中,兩個特征之間不能存在需要關系,即
?f∈FG,?r1,r2∈f.Fc→(f1=r1.Flatter)∧(f2=r2.Flatter)∧(r1.Rtype=alternative)∧(r2.Rtype=alternative)∧(require(f1,f2)∨require(f2,f1))
式中require函數用于描述兩個特征之間存在需要關系。
這些規則采用一階邏輯表示,因此可以用通用的一階邏輯分析器進行驗證,如對強制性的檢查描述如下:
module feature/FeatureModel
sig Feature{}
disj sig Concept extends Feature {
holds: set Feature
} { this in holds}
func Mandatory(c: Concept, pf: Feature, s: set Feature)
{ pf !in s
pf in c.holds => all f:s | f in c.holds
pf ! in c.holds => all f:s | f !in c.holdes
}…
由于對象特征主要從業務需求中提取,而靜態資產則是數據模型的靜態描述,并為路由處理層提供面向對象的數據訪問方法,兩者之間具有一定的相似之處,因此對這兩者之間建立映射關系,即一個對象特征對應一個數據模型文件。圖1展示了對象特征與靜態資產間的映射關系。

圖1 對象特征映射策略
路由特征從屬于對象特征且主要用于表示業務對象的功能需求,而業務資產中的API則對應于系統中的各個業務請求,每個業務請求都會對應于控制器文件中的單個業務邏輯函數。因此可以將路由特征同業務資產中的API以及API對應的業務邏輯函數進行映射。此外,通用資產不直接與特征模型進行映射,它通過與業務資產之間的調用關系間接地與特征模型相關聯。路由特征與業務資產之間的映射策略以及業務資產與通用資產間的需求關系如圖2所示。

圖2 路由特征映射策略
當路由特征存在子特征時,則根據功能實現特征之間的選擇約束又可以分為兩種映射情況。第一種為當功能實現特征之間為多選一約束時,如圖3中的特征a與特征b,他們分別與不同的業務邏輯函數進行映射,但是它們使用的是同一個API,即父特征(路由特征)映射的API。第二種是當選擇約束為多選多時,如圖3中的特征c與特征d,其分別對應各自的API以及業務邏輯函數,而它們的父特征則不進行特征映射。

圖3 功能實現特征映射策略
以企業ERP軟件中的生產訂單管理為例。BYMQ是一家專業設計、生產、安裝建筑幕墻的企業,隨著建筑的個性化特征日益明顯,要求企業ERP系統需要具備足夠的靈活性,能夠更廣泛地支持客戶的個性化需求。在對企業原有的單體結構ERP系統進行改造的過程,筆者分步實現了整體架構的微服務化,新增了客戶網上下單與更改管理、供應商網上接單與更改管理等模塊。新系統設計時,使用ABS語言進行模型定義,包括遺留系統的反向建模和新增模塊的建模工作,再由模型語言自動生成web服務框架腳本。
ERP中,生產訂單除了普通訂單(原料采購、加工制造、成品發貨都由本企業完成)外,還包括來料加工(OEM)、委托外加工(Outsourcing)等可變性。通過對用戶(領域專家)的多次訪談,列出生產訂單管理的4 個特征:Type,Normal,OEM,Out。軟件產品線要能夠生成3 種微服務產品: normalOrder(普通訂單),OEMOrder(來料加工訂單),委外加工訂單(OutOrder)。normalOrder服務包含Type和Normal特征,OEMOrder服務包含Type和OEM特征,委外加工訂單服務包含Type和Out特征。
首先,為產品線定義一個特征模型。ABS中的特征模型本質上是將特征圖進行文本表示。圖4為生產訂單的特征圖,它具有Type特性,具有兩個子特性Normal和OEM。

圖4 訂單特征模型
如果選擇Normal特征,則加工費屬性的值必須為0,而選擇OEM特征時,加工費屬性則必須大于0。Normal和OEM之間的弧意味著只允許選擇一個特征,也就是說普通訂單和來料訂單中只能選擇一個,其ABS特征模型代碼如下:
Root Order {
group allof {
Type {
Int fee;
group oneof {
Normal {
ifin: Type.fee=0;
}
OEM {
ifin: Type.fee>0;
}
}
}
}
}
微服務模塊主要是進行一個或多個微服務的實現。例如,在生產訂單模塊中,其中包含兩個微服務,即receive(發票接收),cancel(撤銷訂單)。該模塊包括資源層(MOrderResource),服務層(MOrderService),領域層(MOrderModel),持久層(MOrderDbImpl和ABS ORM)。筆者只關注服務層。
生產訂單模塊的服務層實現提供了兩個微服務,發票接收和撤銷訂單。撤銷訂單的實施應根據發票ID對數據庫執行查詢以獲取訂單模型,然后在發票模型上調用cancel方法并將其保存回數據庫。發票接收服務遵循相同的模式來執行操作。撤銷訂單的實施描述如下:
class OrderServiceImpl implements OrderService {
Order cancel(String id){
OrderDb odb = new local OrderDbImpl();
Order o = odb.findById("MOrderModel.orderImpl",id);
o.cancel();
o.update();
return o;
}
}
這一步中為特征模型中定義的每一個特征創建Delta模塊。將各個Delta模塊命名為DeltaType,DeltaNormal,DeltaOEM。以DeltaOEM模塊為例,它通過刪除原本的費用變量并重新添加賦予新值的變量來修改類OrderImpl,其實施描述如下:
delta DeltaOEM(Int f);
uses MOrderModel;
modifies class OrderImpl {
removes Int fee;
adds Int fee = f;
}
路由配置用于配置每個微服務的URL。路由配置的格式定義為
“
式中:URL為用戶調用微服務功能的地址;資源類名為負責處理微服務請求的類的名稱;方法名為微服務中方法的名稱。生成訂單的路由配置代碼(例如,/ order/ cancel.abs是訪問撤銷訂單的URL)描述如下:
class RouteConfigImpl implements RouteConfig {
String route(String url){
String r = case url {
"/order/receive.abs" => "MOrderResource.OrderResourceImpl@receive";
"/order/cancel.abs" => "MorderResource.OrderResourceImpl@cancel";
}
return r
}
}
該步驟中,采用一階邏輯方法對模型進行驗證,若發現問題,則退回到4.1節步驟,修改模型,并重新驗證。若驗證通過,則進行產品配置,定義特征和Delta模塊之間的關系。例如,如果Type被選中,則Delta模塊DeltaType將應用于Type特征,其實施描述如下:
productline Order;
features Type, Normal, Incoming;
delta DeltaType(Type.fee)when Type;
delta DeltaNormal after DeltaType when Normal;
delta DeltaOEM(Type.fee)after DeltaType when OEM;
這一步需要定義生成的所有產品。本例中,將生成兩個產品,即NormalOrder和OEMOrder。NormalOrder中包含有Type及Normal特征。OEMOrder則具有Type和OEM特征,其實施描述如下:
product NormalOrder(Type(fee=0), Normal);
product OEMOrder(Type(fee>0), OEM);
如果所有步驟都已完成,則使用命令ant-Dabsproduct = <產品名稱> abs.deploy生成產品。產品名稱為上一步定義的名稱。例如,如果要生成NormalOrder,輸入ant-Dabsproduct = NormalOrder abs.deploy即可。
基于特征的建模方法可以提供產品家族產品特征的簡潔表示,特征模型的結構良好,通過識別特征之間的關系,將各個單獨的特征組織成一個有機的整體。但是該方法的一個缺點是無法在特征模型中實現特征到核心資產的映射。為了實現特征到核心資產的映射,提出了利用基于抽象行為規格化語言,利用特征模型和UML模型模板實現可變性的建模,每個模型模板中的元素與特征關聯。同一個特征跟需求規格、設計模型和實現等核心資產都有關聯。從開發實踐結果分析,采用形式化方法描述軟件產品線建模,可以通過一致的視圖、靈活的抽象層次、準確地描述軟件產品線中的主要要素及其動態行為,使得產品線模型與實際開發過程之間的粒度和抽象層次更趨于一致,有利于軟件工程環境和實際開發環境更加緊密地集成。