吳俠,艾芳菊
(湖北大學(xué)計(jì)算機(jī)與信息工程學(xué)院,湖北 武漢 430062)
圖1 單體架
眾籌系統(tǒng)是用來(lái)管理眾籌網(wǎng)的核心業(yè)務(wù)平臺(tái),其主要功能包括基礎(chǔ)模塊、會(huì)員模塊和項(xiàng)目模塊.由于目前傳統(tǒng)的眾籌系統(tǒng)都采用單體架構(gòu)進(jìn)行模塊化設(shè)計(jì)、采用統(tǒng)一的編程語(yǔ)言進(jìn)行開(kāi)發(fā)、打包及部署,具體如圖1所示.與此同時(shí),單體架構(gòu)的缺點(diǎn)也逐漸顯現(xiàn)出來(lái).
首先,隨著業(yè)務(wù)的不斷發(fā)展,整個(gè)系統(tǒng)的復(fù)雜度和更新頻率不斷提高,面對(duì)這種情況,傳統(tǒng)的單體架構(gòu)已經(jīng)無(wú)法滿足業(yè)務(wù)發(fā)展的需要.在保證當(dāng)前系統(tǒng)能夠穩(wěn)定運(yùn)行的前提下,如果對(duì)單體架構(gòu)應(yīng)用程序做任何細(xì)微的修改,那么對(duì)于整個(gè)應(yīng)用來(lái)說(shuō)就必須進(jìn)行重新部署,這樣會(huì)極大降低系統(tǒng)的構(gòu)建效率[1].其次,由于系統(tǒng)采用單體架構(gòu),采用新框架或者新技術(shù)需要重新編寫(xiě)程序,會(huì)極大增加開(kāi)發(fā)成本.與此同時(shí),系統(tǒng)部署環(huán)境的配置無(wú)法根據(jù)業(yè)務(wù)模塊進(jìn)行動(dòng)態(tài)伸縮,只能對(duì)各個(gè)模塊進(jìn)行統(tǒng)一配置,無(wú)法為每個(gè)模塊設(shè)置最優(yōu)的配置.最后,由于每次升級(jí)系統(tǒng)或者缺陷修復(fù)都需要對(duì)整個(gè)WAR包進(jìn)行重新部署,造成整個(gè)系統(tǒng)全量部署耗時(shí)長(zhǎng)、影響范圍大、風(fēng)險(xiǎn)高[2].
因此,筆者提出了基于微服務(wù)構(gòu)建眾籌系統(tǒng)的技術(shù)方案,并針對(duì)在構(gòu)建系統(tǒng)時(shí)出現(xiàn)的問(wèn)題如:服務(wù)治理、配置集中管理、路由網(wǎng)關(guān)、服務(wù)鏈路追蹤等,采用Spring Cloud的一系列組件作為最佳解決方案,在眾籌系統(tǒng)搭建的過(guò)程中逐漸證明了其可行性.
1.1 使用微服務(wù)架構(gòu)搭建系統(tǒng)將整個(gè)眾籌系統(tǒng)按照業(yè)務(wù)劃分成3個(gè)模塊分別是基礎(chǔ)模塊、會(huì)員模塊、項(xiàng)目模塊.模塊之間通過(guò)HTTP協(xié)議或者輕量級(jí)的消息組件進(jìn)行通信.這些微服務(wù)單元是高度組件化的模塊,模塊之間擁有穩(wěn)定的邊界,模塊之間沒(méi)有任何耦合,有非常好的擴(kuò)展性和復(fù)用性[3].每個(gè)模塊都可以擁有自己的編程語(yǔ)言和存儲(chǔ)技術(shù),它們之間相互獨(dú)立不受影響.
按照業(yè)務(wù)將系統(tǒng)劃分為多個(gè)服務(wù)單元,能更好地進(jìn)行開(kāi)發(fā)和維護(hù),主要因?yàn)閯澐种竺總€(gè)服務(wù)的代碼量小,業(yè)務(wù)清晰可見(jiàn),每個(gè)服務(wù)都可以選擇適合自己業(yè)務(wù)的技術(shù).同時(shí)能夠?qū)γ總€(gè)服務(wù)進(jìn)行差異化的配置如:為不同的服務(wù)單獨(dú)增加內(nèi)存、提高CPU或者其他配置來(lái)滿足業(yè)務(wù)需求.
同時(shí)為了提高代碼的可讀性、可擴(kuò)展性和可維護(hù)性[4],將每個(gè)模塊的前后端分離,實(shí)現(xiàn)更細(xì)粒度的劃分.使得前端微服務(wù)注重于向用戶展示數(shù)據(jù),后端微服務(wù)注重于數(shù)據(jù)獲取.兩者分離各司其職,使系統(tǒng)更加高效穩(wěn)定的運(yùn)行.
1.2 選擇Spring Cloud實(shí)現(xiàn)整體架構(gòu)Spring Framework為 Java 企業(yè)級(jí)開(kāi)發(fā)提供了一站式的輕量級(jí)解決方案,目前已經(jīng)成為了Java企業(yè)級(jí)領(lǐng)域事實(shí)上的標(biāo)準(zhǔn)[5].Spring Cloud作為Spring Framework的一部分,是一個(gè)基于Spring Boot實(shí)現(xiàn)的微服務(wù)開(kāi)發(fā)架構(gòu),它的子項(xiàng)目涵蓋了所有分布式系統(tǒng)所需要的基礎(chǔ)設(shè)施,而且作為 Spring的項(xiàng)目,能夠與 Spring Boot、Spring Data等其他Spring項(xiàng)目完美融合,極大減少了已有項(xiàng)目遷移成本[6],也能結(jié)合企業(yè)信息技術(shù)進(jìn)行的實(shí)際情況,及時(shí)跟隨開(kāi)源社區(qū)組件的最新動(dòng)態(tài)和技術(shù)優(yōu)化,引入最穩(wěn)定的前沿技術(shù)對(duì)已有技術(shù)進(jìn)行不斷改造升級(jí)[7].
它為服務(wù)治理、配置集中管理、路由網(wǎng)關(guān)、服務(wù)鏈路追蹤等提供了一種相對(duì)簡(jiǎn)單的開(kāi)發(fā)方式,而Spring Cloud是基于Spring Boot的,這樣就可以避免繁雜的配置和依賴管理,簡(jiǎn)化了開(kāi)發(fā)和部署的過(guò)程.同時(shí)由于Spring Cloud來(lái)自于Spring Resouces社區(qū),經(jīng)過(guò)社區(qū)大量的兼容性測(cè)試,使其穩(wěn)定性不斷提高.
基于Spring Cloud框架的系統(tǒng)架構(gòu)如圖2所示.
圖2 Spring Cloud架
在整個(gè)Spring Cloud框架體系中,根據(jù)不同的功能需要,選擇相應(yīng)的組件來(lái)支持系統(tǒng)的微服務(wù),在實(shí)際開(kāi)發(fā)過(guò)程中極大降低了溝通成本,極大提高了研發(fā)和測(cè)試效率.主要組件如下:
Eureka:服務(wù)注冊(cè)與發(fā)現(xiàn)組件,包含服務(wù)注冊(cè)中心、服務(wù)提供者和服務(wù)消費(fèi)者[8].
Zuul:路由網(wǎng)關(guān),主要功能是路由轉(zhuǎn)發(fā)和過(guò)濾器.
Feign:聲明式、模板化的HTTP客戶端[9].
Ribbon:客戶端負(fù)載均衡.
Hystrix:在SOA/微服務(wù)架構(gòu)中提供服務(wù)隔離、熔斷、降級(jí)機(jī)制的工具/框架[10].
Spring Cloud Config:分布式配置中心,它支持配置文件放在遠(yuǎn)程Git倉(cāng)庫(kù)中,同時(shí)支持客戶端配置信息的刷新、加密/解密等.
Sleuth:在分布式系統(tǒng)中提供服務(wù)鏈路追蹤的解決方案.
Zipkin:分布式實(shí)時(shí)數(shù)據(jù)追蹤系統(tǒng),聚集來(lái)自各個(gè)異構(gòu)系統(tǒng)的實(shí)時(shí)監(jiān)控?cái)?shù)據(jù)[11].
由于整個(gè)系統(tǒng)是基于Spring Cloud來(lái)實(shí)現(xiàn)的,嚴(yán)格按照微服務(wù)的設(shè)計(jì)原則,服務(wù)之間的調(diào)用采用Feign.服務(wù)之間相互獨(dú)立無(wú)耦合,具有快速交付和彈性伸縮的能力[12].
另外需要注意的是針對(duì)不同版本的Spring Cloud,具體的依賴和配置文件會(huì)有較大差別,本系統(tǒng)統(tǒng)一采用Dalston.RELEASE版本的Spring Cloud.
采用上述架構(gòu)方案實(shí)現(xiàn)眾籌系統(tǒng),主要實(shí)現(xiàn)環(huán)節(jié)和配置介紹如下.
2.1 服務(wù)注冊(cè)與發(fā)現(xiàn)中心(Eureka)的應(yīng)用在實(shí)際系統(tǒng)中往往要對(duì)Eureka Server進(jìn)行高可用配置,以防止系統(tǒng)故障導(dǎo)致服務(wù)注冊(cè)中心不可用,從而導(dǎo)致整個(gè)系統(tǒng)不可用.不同的服務(wù)注冊(cè)中心的信息是共享的,無(wú)論在哪個(gè)中心注冊(cè)都會(huì)及時(shí)同步到其他注冊(cè)中心.另外,選用yml文件來(lái)維護(hù)系統(tǒng)的配置文件,因?yàn)閥ml文件層次清晰,易于編寫(xiě)和瀏覽.
此外,需要在服務(wù)注冊(cè)與發(fā)現(xiàn)服務(wù)的啟動(dòng)類上添加@EnableEurekaServer注解開(kāi)啟Eureka Server的功能;在所有的服務(wù)消費(fèi)者的啟動(dòng)類上添加@EnableEurekaClient注解開(kāi)啟Eureka Client的功能.
以會(huì)員模塊為例,相關(guān)服務(wù)啟動(dòng)后會(huì)自動(dòng)注冊(cè)到服務(wù)中心,如圖3所示.每個(gè)微服務(wù)都對(duì)應(yīng)其特有的功能,各個(gè)微服務(wù)的業(yè)務(wù)說(shuō)明如表1.
圖3 微服務(wù)列
表1 微服務(wù)業(yè)務(wù)說(shuō)明
2.2 配置的集中管理Spring Cloud Config默認(rèn)將配置信息存儲(chǔ)到遠(yuǎn)程的Git倉(cāng)庫(kù).
2.2.1 服務(wù)端(ConfigServer)配置 以眾籌項(xiàng)目系統(tǒng)后臺(tái)部分crowdfunding-project-service為例,在生產(chǎn)環(huán)境中將ConfigServer注冊(cè)到eureka-server,其他客戶端即可通過(guò)微服務(wù)的服務(wù)名從Git倉(cāng)庫(kù)中遠(yuǎn)程獲取配置信息.具體步驟如下:
首先,在服務(wù)端增加Eureka的依賴spring-cloud-starter-eureka.
其次,在yml文件中設(shè)置連接Git倉(cāng)庫(kù)的參數(shù).
最后,需要在ConfigServer的啟動(dòng)類上添加@EnableConfigServer注解,開(kāi)啟ConfigServer的功能.
2.2.2 客戶端(ConfigClient)配置 首先,在客戶端增加Eureka的依賴spring-cloud-starter-eureka.
其次,客戶端需要在yml文件中做相關(guān)配置即可.
2.3 路由網(wǎng)關(guān)(Zuul)的應(yīng)用在Zuul的啟動(dòng)類上添加@EnableEurekaClient注解即可實(shí)現(xiàn)Zuul向Eureka進(jìn)行注冊(cè),由于Zuul向Eureka中進(jìn)行了注冊(cè),所以API網(wǎng)關(guān)可以被看作是Eureka治理下的一個(gè)微服務(wù)應(yīng)用,它可以從注冊(cè)中心中獲取所有服務(wù)以及它們的實(shí)例信息.與此同時(shí)通過(guò)這些實(shí)例信息和API網(wǎng)關(guān)的配置,維護(hù)了系統(tǒng)中所有serviceId和實(shí)例地址的一對(duì)一的映射關(guān)系.當(dāng)外部請(qǐng)求到達(dá)API網(wǎng)關(guān)時(shí),根據(jù)請(qǐng)求URL找到對(duì)應(yīng)的path,此時(shí)API網(wǎng)關(guān)就知道將請(qǐng)求轉(zhuǎn)發(fā)到哪個(gè)具體的serviceId上.
如果某個(gè)服務(wù)存在多個(gè)實(shí)例,則Zuul默認(rèn)在路由轉(zhuǎn)發(fā)做了負(fù)載均衡,負(fù)載均衡策略為輪詢,即將請(qǐng)求依次轉(zhuǎn)發(fā)到不同的服務(wù)實(shí)例.
2.3.1 路由配置 以實(shí)名認(rèn)證功能后臺(tái)部分為例,為實(shí)名認(rèn)證后臺(tái)服務(wù)crowdfunding-authname-service配置路由,通過(guò)zuul.routes.< route>.path和zuul.routes.< route>.serviceId這兩個(gè)參數(shù)對(duì)服務(wù)進(jìn)行配置,其中< route>表示根據(jù)服務(wù)自定義的名稱,例如:
zuul.routes.crowdfunding-authname-service.path=/crowdfunding-authname-service/**
zuul.routes.crowdfunding-authname-service.serviceId=crowdfunding-authname-service
通過(guò)這兩個(gè)配置就可以將所有符合/crowdfunding-authname-service/**規(guī)則的請(qǐng)求轉(zhuǎn)發(fā)到crowdfunding-authname-service所對(duì)應(yīng)的服務(wù)實(shí)例上去.
2.3.2 熔斷器配置 在Zuul中實(shí)現(xiàn)熔斷器的功能需要實(shí)現(xiàn)ZuulFallbackProvider接口,這個(gè)接口有兩個(gè)重要的方法,一個(gè)是getRoute()方法,主要用于指定熔斷器的功能應(yīng)用于哪些路由的服務(wù);另一個(gè)方法是fallbackResponse(),它主要是進(jìn)入熔斷功能時(shí)執(zhí)行的邏輯[13].
2.3.3 請(qǐng)求過(guò)濾 繼承ZuulFilter這個(gè)類并且實(shí)現(xiàn)這個(gè)類中filterType()和filterOrder(),以及IZuulFilter的shouldFilter()和Object run()這4個(gè)抽象方法即可完成對(duì)請(qǐng)求的過(guò)濾和攔截.
在系統(tǒng)中自定義了一個(gè)Zuul過(guò)濾器CrowdfundingFilter,它繼承了ZuulFilter并實(shí)現(xiàn)了ZuulFilter的4個(gè)方法.同時(shí)還要在啟動(dòng)類中為其創(chuàng)建具體的Bean之后才能夠啟動(dòng)該過(guò)濾器.
2.4 服務(wù)之間的調(diào)用(Feign)Feign自身就是一個(gè)聲明式的偽HTTP客戶端,寫(xiě)起來(lái)思路更加清晰和方便,同時(shí)Feign是采用基于接口注解的編程方式,使用更加簡(jiǎn)便.以眾籌項(xiàng)目系統(tǒng)前臺(tái)部分crowdfunding-project為例,具體使用如下:
首先,為crowdfunding-project添加依賴spring-cloud-starter-feign.
其次,在crowdfunding-project 的yml文件中添加相關(guān)配置.
接著,在crowdfunding-project的啟動(dòng)類上添加@EnableFeignClients注解開(kāi)啟Feign的遠(yuǎn)程調(diào)用功能.
最后,編寫(xiě)接口類實(shí)現(xiàn)遠(yuǎn)程調(diào)用其他服務(wù).
2.5 服務(wù)鏈路追蹤與分析(Sleuth和Zipkin)在微服務(wù)架構(gòu)下的眾籌系統(tǒng)中一個(gè)服務(wù)功能的實(shí)現(xiàn)往往需要依賴其他服務(wù),多個(gè)服務(wù)相互協(xié)調(diào)共同運(yùn)行,最后才能產(chǎn)生請(qǐng)求結(jié)果.以實(shí)名認(rèn)證前臺(tái)部分crowdfunding-authname為例,它需要調(diào)用實(shí)名認(rèn)證后臺(tái)部分crowdfunding-authname-service.
2.5.1 Zipkin Server配置 首先,添加相關(guān)依賴spring-cloud-sleuth-zipkin-stream、spring-cloud-starter-stream-rabbit、zipkin-autoconfigure-ui、mysql-connector-java、spring-boot-starter-jdbc.
其次,在啟動(dòng)類上添加@EnableZipkinStreamServer注解開(kāi)啟Zipkin Server功能.
最后,在Zipkin Server的yml文件中添加相關(guān)配置.
2.5.2 Zipkin Client配置 Zipkin Client包含兩個(gè)部分,分別是crowdfunding-authname和crowdfunding-authname-service.
首先,為兩個(gè)服務(wù)添加依賴spring-cloud-sleuth-zipkin-stream和spring-cloud-starter-stream-rabbit.
其次,以crowdfunding-authname為例,在其yml文件中添加相關(guān)配置即可.
通過(guò)訪問(wèn)Zipkin Server可以看到具體的訪問(wèn)過(guò)程,服務(wù)之間的相互依賴關(guān)系如圖4所示.服務(wù)請(qǐng)求的調(diào)用情況如圖5所示,從圖中可知請(qǐng)求的調(diào)用情況,如請(qǐng)求的調(diào)用時(shí)間、消耗時(shí)間、以及請(qǐng)求調(diào)用的鏈路情況.
圖4 服務(wù)之間的依賴關(guān)
圖5 請(qǐng)求調(diào)用情
本系統(tǒng)采用ApacheBench作為壓力測(cè)試工具.以同一個(gè)請(qǐng)求為例,設(shè)置發(fā)起的總請(qǐng)求數(shù)為100、并發(fā)數(shù)也為100.從整體性能上看基于微服務(wù)架構(gòu)的系統(tǒng)從整個(gè)測(cè)試持續(xù)的時(shí)間、每秒請(qǐng)求的數(shù)量、每個(gè)請(qǐng)求消耗的時(shí)間和平均每秒網(wǎng)絡(luò)傳輸速率都比基于單體架構(gòu)的系統(tǒng)有較大提升.具體數(shù)據(jù)如表2所示.
表2 兩種架構(gòu)下系統(tǒng)運(yùn)行數(shù)據(jù)
圖6 兩種架構(gòu)內(nèi)存消耗
眾籌系統(tǒng)部分功能在高并發(fā)的情況下兩種架構(gòu)的內(nèi)存消耗如圖6所示.
兩者相比不僅僅是系統(tǒng)性能上的提升,同時(shí)基于微服務(wù)架構(gòu)的系統(tǒng)還具有更好的可維護(hù)性、可擴(kuò)展性,代碼的可讀性也極大提升;業(yè)務(wù)交接的難度也降低了很多;測(cè)試難度也極大地降低.小組中的每個(gè)人只需要關(guān)注自己的微服務(wù)即可,無(wú)論其他微服務(wù)做了哪些修改,都不會(huì)影響自身的微服務(wù),極大提升了開(kāi)發(fā)效率.
隨著互聯(lián)網(wǎng)技術(shù)的飛速發(fā)展,傳統(tǒng)的單體架構(gòu)已經(jīng)很難滿足互聯(lián)網(wǎng)技術(shù)的發(fā)展要求,微服務(wù)架構(gòu)應(yīng)運(yùn)而生.相比于單體架構(gòu),微服務(wù)架構(gòu)能夠?qū)?fù)雜問(wèn)題簡(jiǎn)單化,具有很強(qiáng)的橫向擴(kuò)展能力,服務(wù)之間相互獨(dú)立,無(wú)耦合,更重要的是微服務(wù)在CAP架構(gòu)中采用的是AP架構(gòu)即高可用和分區(qū)容錯(cuò),因此微服務(wù)架構(gòu)具有很好的負(fù)載能力,同時(shí)系統(tǒng)也很健壯[13].
通過(guò)對(duì)傳統(tǒng)眾籌系統(tǒng)的改造,使得新系統(tǒng)的維護(hù)效率大大提高,應(yīng)用發(fā)布和更新的時(shí)間也被極大地縮短,因此從長(zhǎng)遠(yuǎn)來(lái)看,該系統(tǒng)具有很好的推廣價(jià)值和應(yīng)用場(chǎng)景.
另外,微服務(wù)架構(gòu)是系統(tǒng)架構(gòu)不斷演進(jìn)的一個(gè)產(chǎn)物,任何軟件從一開(kāi)始就不應(yīng)該被設(shè)計(jì)成微服務(wù)架構(gòu),而是需要根據(jù)具體的業(yè)務(wù)漸進(jìn)式的發(fā)展與改進(jìn),任何脫離實(shí)際業(yè)務(wù)的技術(shù)都是無(wú)用的.目前整個(gè)系統(tǒng)還未實(shí)現(xiàn)真正意義上的微服務(wù),有些微服務(wù)并不是嚴(yán)格按照業(yè)務(wù)來(lái)劃分.下一階段就是需要結(jié)合具體的生產(chǎn)數(shù)據(jù)以及鏈路追蹤收集的數(shù)據(jù)對(duì)系統(tǒng)進(jìn)行更進(jìn)一步的調(diào)整包括將服務(wù)繼續(xù)劃分以及更改服務(wù)的配置,使得系統(tǒng)能夠更加高效穩(wěn)定的運(yùn)行.