桂 俊,沈迎春
(武漢數(shù)字工程研究所,武漢 430205)
傳統(tǒng)ERP 體系結(jié)構(gòu)往往基于單體的三層架構(gòu),伴隨著業(yè)務(wù)功能的擴(kuò)展,組件之間依賴越來復(fù)雜,業(yè)務(wù)數(shù)據(jù)的大量積累等導(dǎo)致系統(tǒng)的運(yùn)行速度和用戶體驗(yàn)越來越差,如何解決上面困境,以滿足高可用、高并發(fā)的要求,迫切需要一種新的軟件架構(gòu)模式來解決上述問題.文獻(xiàn)[1]指出傳統(tǒng)單體架構(gòu)設(shè)計(jì)的系統(tǒng)內(nèi)部緊密耦合,靈活性差,以及基于面向服務(wù)架構(gòu)SOA的應(yīng)用系統(tǒng)的安全性不足、負(fù)載均衡等局限性.文獻(xiàn)[2]采用新型微服務(wù)架構(gòu)的應(yīng)用系統(tǒng)可以有效解決ERP 系統(tǒng)暴露出的軟件復(fù)雜性問題,各個(gè)微服務(wù)能夠單獨(dú)部署,每個(gè)獨(dú)立的微服務(wù)模塊負(fù)責(zé)傳統(tǒng)ERP 系統(tǒng)中一個(gè)或多個(gè)關(guān)聯(lián)的業(yè)務(wù)系統(tǒng).為了適應(yīng)應(yīng)用系統(tǒng)性能要求、需求快速變化,傳統(tǒng)單體架構(gòu)由于耦合度強(qiáng)、維護(hù)復(fù)雜,不利于新功能擴(kuò)展、快速交付,隨著開源技術(shù)的成熟,進(jìn)化出滿足開發(fā)敏捷性、持續(xù)交付、可伸縮、最終一致的新興系統(tǒng)架構(gòu)即微服務(wù)架構(gòu)[3].本文首先研究了微服務(wù)架構(gòu)的理論和技術(shù)基礎(chǔ),分析了基于微服務(wù)構(gòu)造應(yīng)用的優(yōu)勢(shì),從基礎(chǔ)服務(wù)、業(yè)務(wù)服務(wù)、公共服務(wù)、接入服務(wù)等幾個(gè)維度來描敘基于微服務(wù)的應(yīng)用架構(gòu),針對(duì)ERP的具體需求設(shè)計(jì)了基于微服務(wù)架構(gòu)的企業(yè)ERP架構(gòu),詳細(xì)介紹了微服務(wù)的實(shí)現(xiàn)技術(shù)Spring Boot、Spring Cloud、負(fù)載均衡、網(wǎng)關(guān)架構(gòu)設(shè)計(jì)、微服務(wù)間調(diào)用Feign,以訂單子系統(tǒng)為例,詳細(xì)論述了微服務(wù)的開發(fā)過程.
微服務(wù)架構(gòu)是一種軟件架構(gòu)模式,它將一個(gè)完整的應(yīng)用從數(shù)據(jù)存儲(chǔ)到業(yè)務(wù)邏輯的開發(fā)垂直切分多個(gè)不同的服務(wù),服務(wù)運(yùn)行于獨(dú)立的進(jìn)程之中,具有自己獨(dú)立的生命周期,服務(wù)之間采用統(tǒng)一的輕量級(jí)通信協(xié)議相互通訊,具有獨(dú)立開發(fā)、維護(hù)、部署和擴(kuò)展的優(yōu)勢(shì)[4].一個(gè)基于微服務(wù)架構(gòu)的應(yīng)用平臺(tái)通常由業(yè)務(wù)服務(wù)、接入服務(wù)、公共服務(wù)、基礎(chǔ)服務(wù)等4 部分組成,微服務(wù)應(yīng)用架構(gòu)設(shè)計(jì)如圖1所示.
圖1 微服務(wù)應(yīng)用架構(gòu)設(shè)計(jì)
其中業(yè)務(wù)服務(wù)負(fù)責(zé)實(shí)現(xiàn)各個(gè)服務(wù),同時(shí)提供運(yùn)維監(jiān)控手段對(duì)服務(wù)進(jìn)行監(jiān)控,業(yè)務(wù)服務(wù)層將各個(gè)業(yè)務(wù)系統(tǒng)提供的服務(wù)接口進(jìn)行集成,形成統(tǒng)一的服務(wù)標(biāo)準(zhǔn)為接入層提供服務(wù).接入服務(wù)層通過服務(wù)路由、負(fù)載均衡等手段負(fù)責(zé)服務(wù)的分發(fā),基礎(chǔ)服務(wù)包括使用標(biāo)準(zhǔn)的中間件進(jìn)行服務(wù)的注冊(cè),公共服務(wù)包括基礎(chǔ)數(shù)據(jù)存儲(chǔ)服務(wù)MongoDB、關(guān)系型RDB、緩存服務(wù)Redis,服務(wù)接口之間采用輕量級(jí)RESTful 格式消息交互機(jī)制.統(tǒng)一門戶負(fù)責(zé)管理監(jiān)控業(yè)務(wù)服務(wù).
ERP 系統(tǒng)的功能之一是實(shí)現(xiàn)企業(yè)業(yè)務(wù)流程的自動(dòng)化.當(dāng)企業(yè)客戶下達(dá)訂單之后,銷售部門結(jié)合企業(yè)的庫存和產(chǎn)能審核客戶提交的訂單是否能被滿足,如果庫存物料和生產(chǎn)產(chǎn)能同時(shí)能夠滿足訂單需求,對(duì)客戶訂單予以確認(rèn),然后根據(jù)訂單上所列產(chǎn)品,向倉(cāng)儲(chǔ)部提出產(chǎn)品出庫申請(qǐng),若庫存產(chǎn)品能夠滿足訂單需求,直接出庫由配送人員將產(chǎn)品交付給客戶.如果庫存不滿足情況下,由生產(chǎn)部根據(jù)訂單確認(rèn)生產(chǎn),生產(chǎn)出產(chǎn)品提交倉(cāng)儲(chǔ)部進(jìn)行產(chǎn)品入庫,當(dāng)生產(chǎn)部申領(lǐng)的物料,當(dāng)前的庫存無法滿足時(shí),需要向采購(gòu)部提出采購(gòu)申請(qǐng),采購(gòu)申請(qǐng)經(jīng)過審批確認(rèn)之后,由采購(gòu)人員選擇合適的供應(yīng)商進(jìn)行物料的采購(gòu),并將采購(gòu)回來的物料提交給倉(cāng)儲(chǔ)部進(jìn)行物料入庫,入庫之后的物料由倉(cāng)儲(chǔ)部交付給申領(lǐng)物料的生產(chǎn)人員.同時(shí)采購(gòu)人員將采購(gòu)單提交給財(cái)務(wù)部,由財(cái)務(wù)部進(jìn)行付款確認(rèn),進(jìn)行財(cái)務(wù)結(jié)算,打款給供應(yīng)商.客戶簽收之后,銷售人員將訂單提交給財(cái)務(wù)部由財(cái)務(wù)部進(jìn)行收款確認(rèn),財(cái)務(wù)部再進(jìn)行財(cái)務(wù)結(jié)算,最后整個(gè)業(yè)務(wù)流程結(jié)束.
通過對(duì)業(yè)務(wù)流程的分析,接下來需要解決微服務(wù)中的領(lǐng)域劃分及微服務(wù)粒度劃分問題,遵循面向服務(wù)的領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)原則進(jìn)行領(lǐng)域建模,通過業(yè)務(wù)領(lǐng)域拆分出一組領(lǐng)域模塊微服務(wù),每個(gè)微服務(wù)必須是高度內(nèi)聚,符合開閉原則、自治等,僅聚集自己的業(yè)務(wù)、領(lǐng)域服務(wù)間通過接口交互達(dá)到松散耦合.通過領(lǐng)域分解識(shí)別出若干個(gè)業(yè)務(wù)功能子域,每個(gè)業(yè)務(wù)功能域?qū)?yīng)一個(gè)子系統(tǒng),通過子系統(tǒng)再次分解最終產(chǎn)生子流程再以迭代的方式逐步分解細(xì)化,最后達(dá)到與用戶交互的級(jí)別的子流程稱為原子流程.分析上述ERP 銷售訂單業(yè)務(wù)流程后,通過領(lǐng)域分解可以識(shí)別出銷售訂單、庫存、生產(chǎn)、采購(gòu)、財(cái)務(wù)等幾個(gè)子域,每個(gè)子域?qū)⑵湓O(shè)計(jì)為一個(gè)領(lǐng)域服務(wù),每個(gè)領(lǐng)域服務(wù)對(duì)應(yīng)一個(gè)功能集合,比如訂單微服務(wù)包括訂單創(chuàng)建、訂單查詢、訂單審核等操作原子微服務(wù),而每個(gè)原子微服務(wù)又可以被其他微服務(wù)共享,每個(gè)微服務(wù)可以看做是多個(gè)原子微服務(wù)的聚合服務(wù).接下來需要定義微服務(wù)的接口,每個(gè)接口里封裝了若干操作,包含接口名稱、請(qǐng)求url、request 參數(shù)、封裝返回結(jié)果等.
上面詳細(xì)分析了整個(gè)ERP的業(yè)務(wù)流程,大致梳理出ERP的業(yè)務(wù)功能,從業(yè)務(wù)領(lǐng)域中識(shí)別出若干微服務(wù)模塊,包括庫存管理、生產(chǎn)管理、銷售管理及財(cái)務(wù)管理等,每個(gè)模塊繼續(xù)以迭代的方式分解為更細(xì)粒度的業(yè)務(wù)服務(wù).基于微服務(wù)架構(gòu)的ERP 系統(tǒng)架構(gòu)如圖2所示,整個(gè)體系結(jié)構(gòu)自底向上分為4 層,基設(shè)施層提供以數(shù)據(jù)存儲(chǔ)為主的基礎(chǔ)服務(wù),包括內(nèi)存數(shù)據(jù)庫Redis 提供緩存服務(wù)及關(guān)系型數(shù)據(jù)庫資源,微服務(wù)層包括所有業(yè)務(wù)微服務(wù)模塊的基礎(chǔ)業(yè)務(wù)服務(wù)及由基礎(chǔ)服務(wù)組合而成的聚合微服務(wù),基礎(chǔ)微服務(wù)通過操作業(yè)務(wù)數(shù)據(jù)集來實(shí)現(xiàn)單一的業(yè)務(wù)規(guī)則,而聚合微服務(wù)往往實(shí)現(xiàn)跨業(yè)務(wù)模塊的復(fù)雜業(yè)務(wù)規(guī)則.各個(gè)微服務(wù)在注冊(cè)中心組件完成注冊(cè)部署,微服務(wù)之間通過Feign 方式交互,并向上層提供接口服務(wù).服務(wù)網(wǎng)關(guān)提供外部訪問的統(tǒng)一入口,外部通過網(wǎng)關(guān)接入微服務(wù),同時(shí)提供動(dòng)態(tài)路由、授權(quán)安全、調(diào)度、監(jiān)控等的服務(wù)網(wǎng)關(guān)功能及Nginx 反向代理實(shí)現(xiàn)服務(wù)器的負(fù)載均衡.應(yīng)用交互層包括Web 頁面、APP 頁面及調(diào)用的第三方系統(tǒng),往往采用前后端分離技術(shù),基于RESTful 風(fēng)格交互,后端提供Rest 接口,前后端基于HTTP 協(xié)議通信、JSON 格式數(shù)據(jù)傳遞.接下來根據(jù)系統(tǒng)架構(gòu)來進(jìn)行實(shí)現(xiàn)框架的選型,Spring Cloud是J2EE 環(huán)境下最流行、先進(jìn)的微服務(wù)實(shí)現(xiàn)框架,它是一序列微服務(wù)開發(fā)工具集,包括微服務(wù)的分布式配置、服務(wù)發(fā)現(xiàn)、路由、負(fù)載均衡、斷路器、服務(wù)網(wǎng)關(guān)、消息傳遞等應(yīng)用組件的提供.微服務(wù)架構(gòu)是一序列單體微服務(wù)應(yīng)用的集合,Spring Boot 框架通過簡(jiǎn)化配置快速簡(jiǎn)單開發(fā)Spring 應(yīng)用,可以利用Spring Boot 專注于單個(gè)應(yīng)用的快速構(gòu)建.采購(gòu)、生產(chǎn)、庫存各個(gè)子系統(tǒng)都是獨(dú)立的微服務(wù),通過Eureka 進(jìn)行服務(wù)治理,各個(gè)子系統(tǒng)將提供的服務(wù)注冊(cè)到Eureka Server中,作為服務(wù)提供端,同時(shí)各個(gè)子系統(tǒng)作為服務(wù)消費(fèi)端使用Rest接口對(duì)服務(wù)提供端進(jìn)行調(diào)用.API 網(wǎng)關(guān)為客戶端提供統(tǒng)一接口,服務(wù)網(wǎng)關(guān)Gateway是一種基于MVC 模式的響應(yīng)式Web 框架,它簡(jiǎn)化前端調(diào)用邏輯,根據(jù)請(qǐng)求代理到不同的服務(wù),通過轉(zhuǎn)發(fā)將前端請(qǐng)求路由到后端服務(wù)中,調(diào)用單個(gè)服務(wù)或通過API 組合調(diào)用多個(gè)微服務(wù)返回結(jié)果[5].網(wǎng)關(guān)使用Nginx 做路由,能夠?qū)⑶岸说恼?qǐng)求分流分量的發(fā)送給后端服務(wù),實(shí)現(xiàn)負(fù)載均衡,減少后端服務(wù)壓力.
圖2 基于微服務(wù)的ERP 系統(tǒng)架構(gòu)圖
定義協(xié)作接口是規(guī)定團(tuán)隊(duì)合作的契約,保證開發(fā)不同限界上下文的特性團(tuán)隊(duì)能夠并行開發(fā).本文設(shè)計(jì)的ERP 系統(tǒng)涉及到訂單、客戶、員工、文件等實(shí)體,同時(shí)還要與第三方OA 系統(tǒng)的集成工作.訂單的上下文映射如圖3所示.
圖3 訂單的上下文映射
圖3中確定除與OA 集成上下文之間采用“發(fā)布者/訂閱者”模式,無需引入防腐層和開放主機(jī)服務(wù),其余限界上下文之間的協(xié)作都是“客戶方/供應(yīng)方”模式,要定義的協(xié)作接口其實(shí)就是各個(gè)限界上下文的應(yīng)用服務(wù)接口.協(xié)作接口采用事件機(jī)制,定義的是限界上下文之間協(xié)作的接口,協(xié)作接口完全可以根據(jù)之前確定的上下文映射獲得,每個(gè)協(xié)作關(guān)系都意味著一個(gè)接口,不同的上下文映射模式可能會(huì)影響到對(duì)這些接口的設(shè)計(jì).以生產(chǎn)訂單上下文為例,與前面上下文映射的不同之處是將訂單與OA 集成之間的協(xié)作改為了事件機(jī)制,記錄與訂單上下文相關(guān)的協(xié)作接口如圖4所示.
圖4 記錄與訂單上下文相關(guān)的協(xié)作接口
使用生產(chǎn)者(Producer)與消費(fèi)者(Consumer)來抽象客戶方/供應(yīng)方模式與發(fā)布者/訂閱者模式,多個(gè)模式的組合后面的服務(wù)定義就應(yīng)該是遵循RESTful 服務(wù)定義的接口,開放主機(jī)服務(wù),客戶方/供應(yīng)方與開放主機(jī)服務(wù)之間的組合.回顧OA 集成上下文的上下文映射,是將事件持有的內(nèi)容轉(zhuǎn)換為要發(fā)送消息通知的內(nèi)容以及送達(dá)的地址,作為訂閱者的OA 集成上下文在接收到事件,然后發(fā)送消息通知.訂閱的事件應(yīng)該是相同的,應(yīng)該將Order Completed 修改為Notification Ready 事件,處理事件的邏輯完全相同.
開源環(huán)境下微服務(wù)的開發(fā)采用基于Spring 框架全棧技術(shù),包括Web 開發(fā)框架SpringMVC、服務(wù)開發(fā)框架Spring Boot、服務(wù)治理框架Spring Cloud 及ORM持久框架Mybatis,建立統(tǒng)分布式緩存Redis 集群.首先我們需要?jiǎng)?chuàng)建maven 項(xiàng)目,在pom.xml中增加項(xiàng)目中使用到的服務(wù)模塊module,比如配置、服務(wù)注冊(cè)及各種業(yè)務(wù)服務(wù),并添加相關(guān)依賴,便可啟動(dòng)服務(wù)注冊(cè)中心.由于各個(gè)微服務(wù)可以獨(dú)立的開發(fā)和部署,但每個(gè)服務(wù)的數(shù)據(jù)實(shí)體,控制層、實(shí)現(xiàn)層和數(shù)據(jù)持久層的風(fēng)格都是一致的,Spring Boot 用來快速構(gòu)建單個(gè)微服務(wù)應(yīng)用,首先,在application.properties 進(jìn)行配置,包括數(shù)據(jù)源及Mybatis 配置.然后是業(yè)務(wù)系統(tǒng)的開發(fā),這里以訂單子系統(tǒng)為例,訂單管理包括產(chǎn)品出庫和產(chǎn)品入庫兩大類,每種都包括訂單的創(chuàng)建、查詢、審核及生成收揀貨任務(wù),同時(shí)訂單子系統(tǒng)與客戶子系統(tǒng)及產(chǎn)品子系統(tǒng)產(chǎn)生關(guān)聯(lián),首先定義好訂單相關(guān)接口,并且接口請(qǐng)求URL 以RESTful 風(fēng)格呈現(xiàn),整個(gè)訂單子系統(tǒng)包括訂單實(shí)體類、訂單控制類、業(yè)務(wù)服務(wù)接口類用以提供多樣的方法供控制層調(diào)用、數(shù)據(jù)庫映射關(guān)系類等4 種,用戶進(jìn)入訂單查詢頁面,輸入訂單編號(hào)、倉(cāng)庫信息等,SpringMVC 控制層接受到查詢參數(shù)后,調(diào)用訂單服務(wù)接口中的查詢方法,訂單服務(wù)實(shí)現(xiàn)類封裝了查詢客戶和產(chǎn)品信息的方法,通過調(diào)用DAO 組件的Mapper 接口類返回?cái)?shù)據(jù),在服務(wù)實(shí)現(xiàn)類進(jìn)行組裝再返回給前端用戶.最后啟動(dòng)Spring 應(yīng)用主程序,啟動(dòng)嵌入式的Tomcat 并初始化Spring 組件.基于Spring Boot 構(gòu)建訂單服務(wù)模塊如圖5所示.搭建Redis 集群后,創(chuàng)建主從節(jié)點(diǎn),Spring Boot 添加Redis 依賴后,在application.yml 配置nodes、poolConfig 等信息,在Redis 配置類中修改Redis 序列化方式,然后在服務(wù)類中注入Redis-Tempate 就可以完成數(shù)據(jù)對(duì)象的讀寫操作,比如可以將產(chǎn)品類以JSON 格式存儲(chǔ)到Redis中,或者將Redis中以JSON對(duì)象形式返回的產(chǎn)品list 轉(zhuǎn)換為JSON 字符串.
圖5 訂單服務(wù)時(shí)序圖
基于Spring Boot的服務(wù)開發(fā)完畢后,需要利用Eureka 搭建一個(gè)服務(wù)注冊(cè)發(fā)現(xiàn)架構(gòu),首先創(chuàng)建服務(wù)注冊(cè)中心,在Spring Boot 工程下添加Eureka-server 依賴及相關(guān)配置,創(chuàng)建產(chǎn)品服務(wù)product-service,通過在啟動(dòng)類中使用@EnableEurekaServer 注解聲明該服務(wù)是Eureka的服務(wù)端,再添加端口,修改主機(jī)名,配置服務(wù)相關(guān)地址,接下來服務(wù)提供者需要將服務(wù)注冊(cè)到服務(wù)注冊(cè)中心供服務(wù)消費(fèi)者訂閱,啟動(dòng)服務(wù)端后就可以通過URL 訪問Eureka 查看注冊(cè)服務(wù)信息.客戶端將自身注冊(cè)到服務(wù)中心,同時(shí)從服務(wù)中心獲取服務(wù),服務(wù)消費(fèi)者在Pom 文件中,添加起步Eureka 依賴,在啟動(dòng)類注解@EnableDiscoverClient,創(chuàng)建接口來獲取產(chǎn)品服務(wù)實(shí)例,然后啟動(dòng)網(wǎng)關(guān)服務(wù),客戶端統(tǒng)一通過Zuul 網(wǎng)關(guān)訪問內(nèi)部服務(wù),網(wǎng)關(guān)接受發(fā)送請(qǐng)求,從Eureka 獲取可用服務(wù),由Ribbon 進(jìn)行負(fù)載均衡,分發(fā)到后端服務(wù)實(shí)例去調(diào)用[6,7].負(fù)載均衡技術(shù)將來自客戶端大量訪問流量捕獲到負(fù)載均衡服務(wù)器中,通過調(diào)用特定的調(diào)度算法向不同服務(wù)器分發(fā)訪問流量.在微服務(wù)架構(gòu)中,服務(wù)消費(fèi)者EurekaClient 向Rabbion 發(fā)起RestTemplate 請(qǐng)求后會(huì)被LoadBalancer 攔截,根據(jù)URL 獲取服務(wù)名,根據(jù)EurekaClient中服務(wù)狀態(tài)返回到Rabbion的服務(wù)注冊(cè)表中的信息找到匹配的服務(wù).具體配置如下,首先添加Ribbon的起步依賴,在application.yml中制定端口號(hào)及服務(wù)注冊(cè)地址URL,通過在RibbonConfig 類加上@LoadBalanced 注解來注入RestTemplate 開啟負(fù)載均衡功能.服務(wù)注冊(cè)發(fā)現(xiàn)與負(fù)載均衡架構(gòu)如圖6所示,具體代碼如下:
圖6 服務(wù)注冊(cè)發(fā)現(xiàn)與負(fù)載均衡架構(gòu)
@Configuraton
public class RibbonConfig{
@LoadBalanced
RestTemplate restTemplate ()
{return new RestTemplate();}
}
定義一個(gè)ProductService 類,注入restTemplate對(duì)象,在該類的QueryStock() 方法以Rest 方式調(diào)用Eureka client API 接口,服務(wù)層代碼如下:
@Service
public class ProductService {
@Autowired
RestTemplate restTemplate ;
public String QueryStock (String cPdCode) {
Return restTemplate.getForObject(
“http://eureka-client/QueryStock?cPdCode=”+cPdCode,String.class);
}}
在OrderController 類加上@RestController 注解,開啟 RestController的功能,添加Get 方法的接口,調(diào)用productService 類的QueryStock()方法,代碼如下:
@RestController
public class OrderController {
@Autowired
private ProductService productService;
@GetMapping("/QueryStock")
public String QueryStock(@RequestParam (required=false,defaultValue="83010042") String cPdCode) {
return ribbonService.QueryStock(cPdCode);}
}
網(wǎng)關(guān)服務(wù)作為最上層服務(wù),提供統(tǒng)一入口,承擔(dān)權(quán)限身份認(rèn)證、限流、監(jiān)控、接口轉(zhuǎn)發(fā)工作,調(diào)用下游的基礎(chǔ)接口服務(wù),返回?cái)?shù)據(jù)呈現(xiàn)給用戶.Spring Cloud中通過響應(yīng)式的Spring Webflux 來實(shí)現(xiàn)API Gateway,執(zhí)行請(qǐng)求路由到后端服務(wù),執(zhí)行API 組合、協(xié)議轉(zhuǎn)換等操作.Spring Cloud 服務(wù)網(wǎng)關(guān)架構(gòu)如圖7所示,它包括Main 包、API 包、代理包,Cofiguration 類定義了Spring beans,它負(fù)責(zé)路由與Order 相關(guān)的請(qǐng)求[8-10],OrderHandlers 類實(shí)現(xiàn)各種請(qǐng)求處理程序方法,使用API 組合獲取訂單的詳細(xì)信息.處理程序使用遠(yuǎn)程代理調(diào)用后端服務(wù).具體實(shí)現(xiàn)代碼如下所示:
圖7 所示 API Gateway 架構(gòu)
public class OrderHandlers {
private OrderService orderService;
private ProductService productService;
private CustomerService customerService;
public OrderHandlers(OrderService orderService,
ProductService productService,
CustomerService customerService) {
this.orderService=orderService;
this.productService=productService;
this.customerService=customerService;}
Public Moo<ServerResponse>
getOrderDetails(ServerRequest serverRequest) {
String orderid=serverRequest.pathVarable("orderId");
Mono<Orderinfo>orderinfo=orderService.findOrder Byid(orderid);
Mono<Optional<Productinfo>>productinfo=productService.findProdyctByOrderid(orderid);
…
Mono<Tuple4<0rderinfo,productinfo,custoinfo>>combined=Mono.when(orderinfo,productinfo,cusinfo);
Mono<OrderDetails> orderDetails=
combined.map(OrderDetails:makeOrderDetails);
…}
getOrderDetails()實(shí)現(xiàn)API 組合,以獲取訂單詳細(xì)信息,它并行調(diào)用3 個(gè)服務(wù),并將結(jié)果組合在一起,創(chuàng)建一個(gè)OrderDetails對(duì)象轉(zhuǎn)換為ServerResponse.
OrderService 通過WebClient 調(diào)用OrderService 遠(yuǎn)程代理,將JSON 響應(yīng)反序列為OrderInfo對(duì)象.
@Service
public class OrderService {
private OrderIntent orderIntent;
private WebClient client;
Public OrderService(OrderIntent orderIntent,WebClient client)
{this.orderIntent=orderIntent;
this.client=client;}
public Mono<OrderInfo>findOrderById(String orderId){
Mono<ClientResponse>result=client.get()
.uri(orderIntent.orderServiceUrl+"/orders/{order}",orderId).exchange();
Return result.flatMap(res->res.bodyToMono(OrderInfo class)).block();}
}
微服務(wù)之間調(diào)用采用Feign 方式,這里以訂單服務(wù)與庫存服務(wù)交互為例,定義好Feign的起步依賴,在application.yml中配置eureka-feign-client、端口號(hào)、服務(wù)注冊(cè)地址等,再在啟動(dòng)類中加上注解等.接下來創(chuàng)建接口使用@FeignClient 注解,其中value 表示要遠(yuǎn)程調(diào)用的其他服務(wù)名稱,接口中方法QueryStock 通過Fegin 來調(diào)用eureka-client 服務(wù)中的接口[11-13],然后在訂單服務(wù)層OrderService中注入EurekaClientFeign 接口實(shí)例,再創(chuàng)建一個(gè)控制器注入服務(wù)接口實(shí)例,調(diào)用查詢庫存方法,就可以實(shí)現(xiàn)遠(yuǎn)程調(diào)用Feign 客戶端的服務(wù).服務(wù)間調(diào)用代碼如下:
@FeignClient (value="eureka-client",configuration=FeignConfig.class)
public interface EurekaClientFeign
{
@GetMapping(value="/ QueryStock")
String QueryStockFromClientEureka
(@RequestParam(value="name") String name) ;
}
控制層與服務(wù)層代碼類似已省略.
Swagger 規(guī)范用于生成描述文件和接口文檔,Spring Boot 使用swagger 構(gòu)建RESTful APIs,首先在業(yè)務(wù)服務(wù)的pom 文件中添加Springfox、Swagger UI 依賴后,再在控制層和啟動(dòng)類添加注解、層中增加方法及參數(shù),最后在UI 配置類中配置接口,代碼如下:
@Controller
@RequestMapping("/product")
Public class OrderController
{
@Autowired
private OrderService orderService;
@ApiOperation(value="根據(jù)訂單ID 取產(chǎn)品信息")
@RequestMapping(value="/info/{orderId}",method=RequestMethod.GET,charset="utf-8")
@ResponseBody
Public Product getproductInfo(@ApiParam(name=
"orderId",value="訂單ID") @PathVariable
Long orderId) throws Exception {
Return orderService.getProductInfo(orderId);
}}
本文以銷售微服務(wù)為例,對(duì)其訂單管理部分功能進(jìn)行測(cè)試,訂單創(chuàng)建界面如圖8所示,選擇客戶、訂單日期和商品后即可創(chuàng)建成功.
圖8 訂單創(chuàng)建
訂單查詢界面如圖9所示,選擇起止日期,按訂單號(hào)或客戶號(hào)輸入關(guān)鍵字即可查詢訂單.
圖9 訂單查詢
此次測(cè)試對(duì)基于微服務(wù)架構(gòu)的ERP 重構(gòu)前后的系統(tǒng)分別使用Jmeter 工具在相同的并發(fā)量下測(cè)試其性能.將模擬并發(fā)量提高到1000,持續(xù)時(shí)間30 s 條件下,分別對(duì)產(chǎn)品信息返回結(jié)果的響應(yīng)時(shí)間如圖10所示.
圖10 系統(tǒng)響應(yīng)時(shí)間百分比
圖10可以看出ERP 系統(tǒng)在微服務(wù)重構(gòu)前后,當(dāng)請(qǐng)求響應(yīng)量達(dá)到50%時(shí),采用單體架構(gòu)和微服務(wù)架構(gòu)的響應(yīng)時(shí)間分別為2.3 s和0.098 s,重構(gòu)后的系統(tǒng)在高并發(fā)情況下平均響應(yīng)時(shí)間更短.
本文首先分析傳統(tǒng)單體架構(gòu)的缺陷,提出采用新型的微服務(wù)架構(gòu)的特點(diǎn)和優(yōu)勢(shì).基于微服務(wù)的架構(gòu)設(shè)計(jì)因業(yè)務(wù)模塊具有各自的數(shù)據(jù)庫、實(shí)體、服務(wù)、API組件等,可獨(dú)立或者單獨(dú)部署多個(gè)微服務(wù),具有靈活、可擴(kuò)展、去中心化、敏捷性、自治等優(yōu)勢(shì)[14].本文提出基于微服務(wù)架構(gòu)來構(gòu)建企業(yè)ERP,分析微服務(wù)的分解模式,設(shè)計(jì)了基于微服務(wù)的分層架構(gòu),采用基于開源的微服務(wù)實(shí)現(xiàn)框架Spring Boot 開發(fā)服務(wù)、Spring Cloud來管理服務(wù),使用Redis 做分布式數(shù)據(jù)緩存.雖然微服務(wù)相關(guān)技術(shù)不斷發(fā)展創(chuàng)新,微服務(wù)之間如何準(zhǔn)確通信,以滿足用戶快速響應(yīng)的需求,微服務(wù)的部署、測(cè)試、跨服務(wù)實(shí)現(xiàn)問題,如何在通信中保證數(shù)據(jù)的安全性,采用什么樣身份認(rèn)證策略、數(shù)據(jù)加密方法都值得我們未來去探索[15].
計(jì)算機(jī)系統(tǒng)應(yīng)用2021年8期