2021微服務實踐指南_第1頁
2021微服務實踐指南_第2頁
2021微服務實踐指南_第3頁
2021微服務實踐指南_第4頁
2021微服務實踐指南_第5頁
已閱讀5頁,還剩151頁未讀 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

第1第2第3第4第5第6第7第8第9第10第1章微服務架構簡介細細數來,軟件行業已經見證了從不共享架構(sharednothing),到單體架構(onoh),到客戶服務器架構(nv),到分布式多層架構(n),再到面向服務架構(vondhu,)等架構風格。微服務架構無疑就是這條演化鏈上的一個新節點。近年來,微服務這個詞的熱度在各種軟件開發者架構師社區中呈指數級增長。我們經常系列相對較小的服務,并且每個服務只負責自己分管的那一部分。微服務的核心是單一職責原則(SingleResponsibilityPrinciple,SRP)任何呈現出下面這6系統的數據庫應該是去中心化的。理想情況下,每個微服務應該享有自己的數據庫,系統中的每個組件都應該是內聚、獨立且可以自行部署的。任何一個組件在正常工作系統應該有自動化測試。微服務架構最值得稱道的特征就是快,在構建、測試到上線任何一個組件/服務的故障都應該是隔離的。單個服務的故障不應該拖垮整個應用,我們經常會遇到分解數據庫、API交互、大量的開發和運維工作以及統一團隊心態等問RepeatYourself,DRY)原則。Kafka和SNS。微服務可以用很多不同的方式進行部署。通常微服務是隨著容器(如pk、ok等)一起發布的。在使用亞馬遜云服務(onbv,)的時候可以用亞馬遜機器鏡像(onhneg,)來發布,但是創建和部署的時間會比ok長,ok是一個非常好的用來部署微服務的工具。除了部署之外,微服務架構中,還必須采用一些合適的版本規則,否則整個服務群會因為版本問題變得非常混隨著微服務數量的增加,不可避免地會增加運維(vp)的工作。例如,在微服務中勢主要有下面兩類:如,亞馬遜云有一個很酷的aa(匿名函數)功能。其主要的工作原理是,將一部分核心代碼以文件的形式存儲在3(的對象存儲服務)環境中,當某個特定事件被觸發時,會按照預定義參數啟動一個新的實例(也就是一臺云虛擬機),然后將3中的代碼復制到臨時實例中并開始執行這部分代碼。代碼執行完畢之后,標志著本次任務的結束,此時會自動關停該實例。在服務發現的模式下,這一概念變得協議與其他服務進行通信。微服務即微服務即平臺即服務(Platformasaservice,PaaS)可能是微服務未來發展的另外一條當我們在說單體架構的時候,指的是一個很大的代碼庫,某些代碼庫里面分了一些小整個代碼庫是用某一種語言寫的,所以所有開發人員都被限制在這個技術棧里了。在這個大的應用程序中,如果負載增加了,只能對整個應用程序進行擴容,哪怕負載隨著代碼的增加,在引入新功能的時候,更容易出漏洞。而且由于各個模塊可能有些在單體應用中,大家都不想對整個代碼庫負責,因為項目實在是太大了。于單體架構的情形)和發起代碼審查。由于持續集成持續交付流水線的存在,約翰提交的修改代碼會自動合并到相應的分支,比如叫vp,然后自動部署到開發環境。總地算下來,約翰在午餐之前就可以做好新版本上線的所有準備工作了。微服務架構同樣也會給組織帶來好處。由于微服務將整個應用程序拆解成了數個小的服程語言和一種不同的數據庫(、ongo和nd等)來實現。微服務架構可以言。是不是看起來與SOAServiceBus,ESB)發消息的方式來調用任何服務。與在微服務中的情形類似,SOA里面中的通信層是中間件,會演變成單點故障。如果出現了故障,則沒有一個服立原則設計,可以有效規避這種單點故障。微服務典型的通信方式是風格的,這種方式在極大程度上去除了這種重量級的作為通信層的中間件。微服務還去除了中的服務契約部分,由于在要修改接口才行,最后還需要把整個應用程序重新部署一遍才能生效。圖13是一個典型的應用的示意圖。的問題都能解決。SOA是一個非常廣義的概念,我們可以把微服務視為SOA的一個子上市時間(TimetoMarket,TTM)。市場上的需求始終是動態并隨時間變化著的,誰能功能,最后再圍繞這些功能和領域進行微服務的劃分和開發。業務領域的劃分可以使用領域驅動設計(onvngn,)的方式來進行。領域驅動設計是一種解決復雜需求的軟件開發方式,使用領域驅動設計可以將具體實現和演化模型很好地聯系起來。這個詞是2004年引入的,一并引入的還有這樣一些關鍵詞:建模、邊界上下文、實體、代碼庫和通用語言等。和微服務的理念很契合,因此也隨著微服務的流行而逐漸廣為人知。的理念是嘗試理解問題的領域,然后圍繞領域進行建模。建和邊界上下文。有這樣一些基本概念。通用語言:指的是所有的利益相關者(業務部門、運營人員、開發人員)應該使用邊界上下文:邊界上下文的主要目的是用來定義模型的范圍和清晰的上下文邊界,限定在清晰定義了的邊界內。每一個邊界上下文都應該是與其他的上下文相互獨立的,邊界上下文定義了不同領域所分擔的職責。某些業務可以設計多個領域,如貨運的概念可以幫助我們識別出來這些領域和他們的分離點。整個的概念都符合微服務的原則。圍繞模型進行思考,按照邊界上下文對模型進行封裝,這些上下文就清楚地定義了模型的邊界。果采用,我們可以通過邊界上下文將一個大的問題分解為一些小的問題,進而可以露,然后系統會在這些邊界不清楚的地方變成大泥球。可以幫助我們理解和定義邊界上下文。關于領域驅動設計有一本不錯的書可以推薦大家閱讀,叫pnngoanngn1,作者是ughnnon。通過這本書讀者可以大大加深對領域驅動設計的理解。確保所有即將參與到微服務轉型中的人都接受過相應的培訓。這個培訓不只是純技術技開發人員都需要知道一些vp知識。微服務架構中的開發人員對于其分管的微服務要維等劃分是很有必要的。團隊要時刻準備好自己搞定所有的問題。如果有專門的vps人員當然好,但是每個團隊還是要儲備好vp相關能力做到自給自足。體驗vp團隊來說是一項額外的職責。如果想做到快速獨立發布,就需要格外注意vp和監控系統,以確保上線無誤或者出現問題之后能夠及時回滾。自動化和微服務需要一些自動化測試工具和工具如nkn和my等來支持頻繁交付和上線。每一個服務應該有其自己單獨的部署流水線和配置管理。這又會增加一些vp的開銷。(測試驅動開發)在這個方面非常重要。微服務需要單元測試和集成測試等自動化測試手段來保證持續交付。x公司在2009年成功完成了跨越性的由傳統單體應用向微服務架構的轉型。而且他們把自己在轉型過程中得到的經驗分享給了全世界。時至今日,x有超過800個正在運行的微服務,每天會發生超過20億個請求,這20產生超過200億個內部的訪問。在x開始從單體應用向微服務應用轉型之時,微服務這個詞還沒有出現。x之前向客戶的組件。到2011年年底,他們的產品已經整個遷移到了上以眾多微服務的方式運行。x還開源了很多曾經幫助他們成功完成遷移的重要工具。示例項目(信用風險評估引擎各個微服務之間如何通信,等等。在我們的例子中,會使用SpringBoot作為我們的開發環png是一個非常令人稱奇的框架。不管是曾經用過png還是現在正在用png的v程序員都會領略到png的便捷。如果你用v但是還沒有了解過png,那么真的應該停下來反思一下了。png是一個基于(簡單v對象)的輕量級框架。png最大的特點是其依賴注入方式,在png中創建bn和對象的職責交給了png框架來統一管Spring代碼模塊;Springbean模塊;Spring上下文;SpringExpressionLanguage(SpEL)。基于上面的這些核心模塊,png還提供了很多其他的模塊,用于構建健壯的基于v的應用程序,如數據庫訪問模塊、事務管理模塊、安全模塊、png集成模塊、批處理模塊、消息模塊、社交插件、pngoud等。有很多組件可以讓開發變得更加簡單高效。單說png框架的好處,就可以寫上一整本書,但是這并不是本書的目的。概括來講,png的這些組件對于幫助v程序員高效開發大有幫助。但是png有一個小的瑕疵就是其較為復雜的配置系統。平均一個應用程序可能需要用到超過4個png模塊(如核心容器、png事務管理、數據庫訪問等必不可少的模塊),隨著項目的發展,用到的模塊數會更多,此時各個不同版本的組件之間的配置管理和兼容性就變得十分麻煩了。為了幫助開發人員解決這個問題,SpringBootSpringSpringBoot比Spring更適合快速啟動和易于開發,它省略了很多累贅的Spring配置。Spring低開發的工作量。SpringBoot為很多樣板代碼、XML配置和注解節省了開發的工作量。SpringBoot可以很容易地與其他Spring模塊集成,而且還為Web應用提供了一個內置的HTTPWeb服務器。SpringBoot還有命令行界面。SpringBoot可以使用多種構建工具,如使用SpringBoot下面我們舉個例子來證實我們前面對SpringBoot的贊美。這里我們開發一個只包含一個控Java8;STS(SpringToolsSuite);Maven。<?xmlversion="1.0"encoding="UTF-<projectxmlns="/POM/4.0.0"<version>0.0.1-<artifactId>spring-boot-starter-<artifactId>spring-boot-starter-<!--moredependencyifrequired,likejsonconverter--在/src/main/java/com/sample/firstboot/controllerpackageimportimportorg.springframework.boot.autoconfigure.*;importorg.springframework.stereotype.*;importpublicclassSampleControllerStringhome(@PathVariable("userName")StringuserName){return"Welcome,"+userName+"!";請求時,控制器會返回Welcome加一個用戶名。默認情況下處理的是HTTPGET方法。這是publicclassSampleApplicationpublicstaticvoidmain(String[]args)ApplicationContextctx=SpringApplication.run(SampleApplication.class,arSystem.out.println("ApplicationReadytoStart.Hitthebrowser.");的注解組合,這里我們無須再添加諸如@Configuration、@Component、@EnableAutoStartedSampleApplicationin2.174secondsApplicationReadytoStart.Hitthe此時此刻,這個SpringBoot應用程序已經可以進行測試了。要測試這個應用程序,需要打這里的localhost地址指向的是用戶的計算機,8080是SpringBoot中Tomcat啟動的默認端由此可見,使用SpringBoot開發應用程序是非常簡單的。使用SpringBoot之后,開發人員的每一章中。示例風險評估系統會使用SpringBoot作為開發框架,使用Docker來進行部險評估系統,將闡述怎么定義微服務和服務的發現。而且,我們還會使用SpringBoot開發 這本書中文版書名《實現領域驅動設計》,譯者是張逸和滕云。——第2章定義微服務組件微服務是面向服務型結構()的一種更具體、更現代化的理解,布式軟件系統。和類似的是,微服務架構()中的服務是一些通過網絡互相通信來實現目標的進程。(productowner)。因此在定義微服務的時候,一定要記住一點,如果某些特征總是一起可以對其自身進行注冊和撤銷注冊;可以將其自身的改動通知給其他所有服務;可以通過該服務定位到某個特殊服務的具體實例。ApacheZooKeeper;自注冊;第三方注冊。客戶端發現;服務端發現。載均衡算法從這些地址中選擇出合適的訪問服務的實例。NetflixEureka采用的就是這種模發現服務還有助于減少調用時的網絡延時。在之前的例子中可以看到,在查詢服務位置的時候,發現服務可以提供距離服務最近的實例地址。需要的某個版本的服務可能出現在同一個機器、能選擇最佳實例來進行通信。這樣最終可以在一定程度上減少網絡延時。們會選用Netflix的Eureka。SpringBoot(1.4.0)中內置支持了NetflixEureka。通過一些簡<artifactId>spring-cloud-starter-Spring也有zookeeper和consul的實現,這很容易作為spring-cloud-starterconsul-在主應用類中,用戶只需要簡單地添加一個@EnableEurekaServerpackageimportimportorg.springframework.boot.autoconfigure.SpringBootApplication;importflix.eureka.server.EnableEurekaServer;publicclassEurekaServerDemoApplicationpublicstaticvoidmain(String[]args){SpringApplication.run(EurekaServerDemoApplication.class,args);然后通過mvnspring-boot:runWARN24432[nfoReplicator-0]Therewasaproblemwiththeinstanceinfo正如所見,在InstancescurrentlyregisteredwithEureka標題下沒有任何條目,這是因為現在這個服務器還沒有注冊過任何服務實例。除此之外,在這個頁面上還可以在GeneralInfo工程,然后生成下面這些SpringBoot的模板代碼:packagecom.practicalMicroservcies;importjava.util.List;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;importorg.springframework.cloud.client.discovery.DiscoveryClient;importorg.springframework.cloud.client.discovery.EnableDiscoveryClient;importorg.springframework.web.bind.annotation.PathVariable;importorg.springframework.web.bind.annotation.RequestMapping;importpublicclassEurekaClientApplicationpublicstaticvoidmain(String[]args){SpringApplication.run(EurekaClientApplication.class,args);classServiceInstanceRestControllerprivateDiscoveryClientpublicStringserviceInstancesByApplicationName(@PathVariableStringUserName){return"Hello返回HelloAlice作為響應。這個鍵對應的值。這個例子中使用的是PracticalMicroservice-Demo這c.n.e.registry.AbstractInstanceRegistry:RegisteredinstancePRACTICALMICROSERVICE-DEMO/:PracticalMicroService-DemowithstatusUP(replication=false)png有很多支持微服務架構和微服務模式的功能。在pngoud中,加入了一大批處理微服務各種情況的組件,例如,作為客戶端負載均衡的bbon(uk負載均衡),作為斷路器的yx(也由x開發),等等。這些輔助工具的實現方法的討論已經超出了本書的范疇,讀者可以自行研究pngoud和其為微服務架構提供的可用功能。一個公共區域來存儲配置屬性值。DevOps人員可以在所有微服務實例上掛載一塊硬盤,因為本書是基于SpringBoot的,所以我們可以著重討論一下SpringBoot中的做法。Spring嘗試查找System.getProperties()查找RandomValuePropertySources{profile}.properties@PropertySourceSpringBoot會嘗試尋找使用SpringApplication.setDefaultProperties@PropertySources({privateStringconnectionIp;通過這兩行代碼,Spring會在前面提到的配置文件中查找到類似Connect.ip=@PropertySource("file:${CONFIG_PATH}/microserviceA.props")})下,可以通過exportCONFIG_PATH=/mnt/share/stag/config來設置。@PropertySource("file:${CONFIG_PATH}/ps")})如果我們使用SpringCloud,可以使用SpringCloud提供的配置服務器。配置服務器可以從spring.cloud.config.server.git.uri=/abc/microserviceA-務會查找到客戶端服務自己配置的名字(),然后在前面提到的中查找客戶端名字所對應的配置文件。在pngoud中,客戶端還可以通過注解來實現動態加載而無須重新啟動客戶端服務。使用了這個注解之后,客戶端服務會多一個端點e,通過訪問該端點1]就可以實現動態加載配置,重建pngn了。API2-4是一個簡單的同一頁面展示多個服務API等。因此,你可能會遇到這么個問題:每個客戶端在訪問不同的網絡服務并且與網絡服務保持聯系,開發人員必須保證服務向后兼容,因為APIURL被嵌入了移動端這就是網關想要解決的訴求。前面提到的所有這些問題都可以通過網關來解決。網關在消費者和服務器之間扮演了中轉代理的角色。為了解決第一個組合請求的問題,我們可以只定義一個總的,如叫,這個請求會直接發給網關。網關作為消費者發起訂單和用戶信息的請求之后,分別得到訂單和用戶詳情,然后將兩個結果結合之后一并返回給客戶端。因此,在這種情況下網關扮演了一個匯總的角色,它會在內部調用多個來保證對外只暴露一個。圖25展示了網關的工作模式。網關還可以用作負載均衡器來高效處理并發請求。網關可以跟蹤它發往某個具體衡。有了NGINXPlus之后,NGINX可以作為一個不錯的API網關的替代品,它有很多可請求轉發(包括服務發現高效的斷路器,它的默認閾值是5s內20個失敗請求。開發人員還可以針對斷路情況做一API可以根據客戶端需要對結果進行一些裁剪;可以處理局部失敗問題。如果API網關做的事情過多,可能會影響性能;要想使用API網關必須先有服務發現;有些時候,API網關會成為單點失敗的風險點,因為它承載了所有API的入口;需要額外增加管理路由的任務量;增加了額外的網絡請求,也就是說先從API網關走了一道;總的來說,API網關增加了系統復雜性;API幫助初學者生成基本SpringBoot代碼的工程。用戶只需要設置少量配置參數,然后點擊GenerateProject按鈕就可以下載初始工程代碼了。如果用戶想設置一些更詳細的項目參數,可以點擊Switchtothefullversion鏈接來展開更多配置項,如圖2-6所示。對Zuul而言,用戶需要點擊Advance,然后選中zuul勾選框,再點擊GenerateProject按<artifactId>spring-cloud-starter-publicclassProductDetailService@RequestMapping(value=publicProductDetailgetAllProduct(@PathParam("id")Stringreturn上面的代碼中假設了pdService這個bean可以與Spring數據代碼庫(Springdatarepository)對于API網關,我們將另建一個支持Zuul的SpringBoot應用。激活Zuul只需要添加一個簡publicclasspublicstaticvoidmain(String[]SpringApplication.run(ApiGatewayExampleInSpring.class,duct.path=/product/**),因為Zuul默認會把也就前面已經提過,我們會在本書中貫穿一個信用風險評估引擎的應用。風險引擎其實是一要考慮風險這個因素。要一筆貸款來購買某個產品,產品價格從5000到40000不等。在ZestMoney網站上有很多用戶注冊。財務詳情:收入詳情;債務詳情。信用評分。與前面的例子中一樣,還是采用start.spring.io來創建一個SpringBoot應用。這一應用服務提供配置信息。創建一個名為configServer并且選中cloudstarter選項的SpringBoot應<?xmlversion="1.0"encoding="UTF-<projectxmlns="/POM/4.0.0"<version>0.0.1-<description>ConfigurationServertoprovideconfigurationfordifferentservices</description><artifactId>spring-boot-starter-<relativePath/><!--lookupparentfromrepository--<artifactId>spring-cloud-starter-<artifactId>spring-cloud-config-<artifactId>spring-cloud-<artifactId>spring-boot-starter-<artifactId>spring-boot-starter-<artifactId>spring-cloud-<artifactId>spring-boot-maven-戶端。在本書撰寫時,SpringBoot最穩定的版本是1.4.1,所以這段代碼里面使用這個版packageimportimportorg.springframework.boot.autoconfigure.SpringBootApplication;importorg.springframework.cloud.config.server.EnableConfigServer;publicclassConfigServerApplication{publicstaticvoidmain(String[]args){SpringApplication.run(ConfigServerApplication.class,性告訴png所有的配置文件應該在類路徑下。在這里,所有的配置文件都放在ouresources目錄下創建一些新的屬性文件。現在先創建一個名#TogenerateSQLoptimizedforaMysqlperties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect#Namingstrategy,itcanresolvetheissueofuppercasetableorcolumnnamespring.jpa.hibernate.naming-strategy=這個配置文件定義了userService了。在終端上運行mvnspring-boot:run就可以啟動應用程序了。通過一個curl命令就能curllocalhost:8888:這是服務器地址和端口。userService:這是我們需要獲取屬性的服務名稱。"name":"userService","profiles":["label":null,"version":null,"state":null,"propertySources":["name":"classpath:/userSperties","source":{"spring.jpa.show-sql":"true","spring.datasource.username":"root","spring.datasource.password":"root123","Connect.database":"practicalMicroservice",在<source>標簽下,就能看到我們定義在userSpertiesUser用戶服務是什么呢?它負責所有與用戶相關的操作,包括用戶注冊、修改、查找和刪除等。相應的會有ono、pooy、v和od等包,還會有一個控制器來處理獲數據等功能。這里的刪除并不會真的從數據庫中刪除記錄,而是通過數據庫中一個deleteOn用戶服務會用到兩張表。一張表用來記錄用戶的個人數據,如名字、唯一號、出生日期、性別等。第二張表用來保存用戶的地址信息。我們會使用和下面的文件來創建模式(h)和表結構。yay是一個開源的數據庫遷移工具,yy有相應的Maven插件可以完美契合SpringBoot。在SpringBoot中使用Flyway需要在POM文件中添<artifactId>flyway-為V1_0DESCRIPTION.sql的文件。Flyway數據庫會在數據庫中的存儲模式的修訂。CREATETABLE`user_details``id`int(11)NOTNULL`user_id`char(36)NOT`first_name`varchar(250)NOT`middle_name`varchar(250)DEFAULT`last_name`varchar(250)DEFAULT`created_on`TIMESTAMPDEFAULT`deleted_on`TIMESTAMPDEFAULT`leagal_id`varchar(10)NOT`date_of_birth`TIMESTAMPNOT`gender`int(11)NOTNULL,PRIMARYKEY(`id`),KEY`user_id`KEY`user_details_userId_DeletedOn`(`user_id`,`deleted_on`),KEY`user_id_deleted`(`deleted_on`))ENGINE=InnoDBDEFAULT創建addresses表的SQL代碼如下,其中table.user_id是關聯的user_details表中的CREATETABLE`addresses``id`int(11)NOTNULL`user_id`char(36)NOT`city`varchar(25)NOT`address_line_1`varchar(250)NOT`address_line_2`varchar(250)DEFAULT`pincode`char(6)NOT`created_on`TIMESTAMPDEFAULT`deleted_on`TIMESTAMPDEFAULTNULL,PRIMARYKEY(`id`),KEY`user_id`KEY`addresses_user_id_DeletedOn`)ENGINE=InnoDBDEFAULT<?xmlversion="1.0"encoding="UTF-<projectxmlns="/POM/4.0.0"<version>0.0.1-<description>DemoprojectforSpring<artifactId>spring-boot-starter-<relativePath/><!--lookupparentfromrepository--<project.build.sourceEncoding>UTF-<project.reporting.outputEncoding>UTF-<artifactId>spring-cloud-<artifactId>spring-cloud-config-<artifactId>spring-boot-starter-<artifactId>spring-boot-starter-data-<artifactId>mysql-connector-<artifactId>spring-boot-starter-<artifactId>flyway-<artifactId>spring-boot-maven-packageimportimportorg.springframework.beans.factory.annotation.Value;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;importorg.springframework.context.annotation.Bean;publicclassUserServiceApplication{privateStringurl;privateStringuserName;privateStringpassword;privateString@Bean(initMethod="migrate")publicFlywayflyway(){AsflywayDbneedurlseperetalythatiswhyweextractingonlyurlfromgivenstringStringurlWithoutDatabaseName=url.substring(0,url.lastIndexOf("/"));Flywayflyway=newFlyway();flyway.setDataSource(urlWithoutDatabaseName,userName,password);returnflyway;publicstaticvoidmain(String[]args){SpringApplication.run(UserServiceApplication.class,args);下面這個以spring.datasource開頭的屬性是十分重要的,SpringBoot會使用datasource開頭的3個屬性自動創建一個數據源而無須寫任何代碼。connect.database不是SpringBoot在flywayDbbean中,我們設置了這個版本Flyway數據庫下的模式(schema)名。如果模式因此,如果在終端運行mvnspring-boot:run,應該能看到在MySQL中創建的數據庫和entityAddress,另一個是UserDetail。packageimportstaticimportjava.io.Serializable;importjava.util.Date;importjavax.persistence.Column;importimportjavax.persistence.GeneratedValue;importjavax.persistence.Id;importjavax.persistence.Table;importjavax.persistence.Temporal;importjavax.persistence.TemporalType;@Table(name=publicclassAddressimplementsSerializable*SerialIdforprivatestaticfinallongserialVersionUID=*uniqueIdforeach@GeneratedValue(strategy=IDENTITY)@Column(name="id")privateint*UserId,uniqueforevery@Column(name="user_id",nullable=false,unique=true,length=36)privateStringuserId;*City,Stayingcityof@Column(name="city",nullable=false,length=25)privateStringcity;*AddressLine1forUser@Column(name="address_line_1",nullable=false,length=250)privateStringaddressLine1;*AddressLine2forUser@Column(name="address_line_2",nullable=false,length=250)privateStringaddressLine2;*Pincodeforuser@Column(name="pincode",nullable=false,length=36)privateStringpinCode;*Dateonwhichuseris@Column(name="created_on",columnDefinition="TIMESTAMPDEFAULTCURRENT_TIMESTAMP",length=0)privateDate*Dateonwhichuseris@Column(name="deleted_on",columnDefinition="TIMESTAMPDEFAULTNULL",length=0)privateDate這里還應該給類變量創建一些getter和setter方法[2]toStringpublicStringtoString()return"Address[id="+id+",userId="+userId+",city="+city+",addressLine1="+addressLine1+",addressLine2="+addressLine2+",PinCode="+pinCode+",createdOn="+createdOn+",deletedOn="+deletedOn+"]";packageimportorg.springframework.data.jpa.repository.JpaRepository;importcom.practicalMicroservcies.entity.Address;publicinterfaceAddressRepositoryextendsJpaRepository<Address,AddressfindByUserId(Stringpackagecom.practicalMicroservcies.entity;importstaticimportjava.io.Serializable;importjava.util.Date;importjavax.persistence.Column;importimportjavax.persistence.GeneratedValue;importjavax.persistence.Id;importjavax.persistence.Table;importjavax.persistence.Temporal;importjavax.persistence.TemporalType;@Table(name=publicclassUserDetailimplementsSerializableprivatestaticfinallongserialVersionUID=*uniqueIdforeach@GeneratedValue(strategy=IDENTITY)@Column(name="id")privateint*UserId,uniqueforevery@Column(name="user_id",nullable=false,unique=true,length=36)privateStringuserId;*FirstNameof@Column(name="first_name",nullable=false,unique=true,length=250)privateStringfirstName;*LastNameof@Column(name="last_name",nullable=false,unique=true,length=250)privateStringlastName;*MiddleNameof@Column(name="middle_name",nullable=false,unique=true,length=250)privateStringmiddleName;*LegalidOfuser(couldbesocialsecuritynumber@Column(name="legal_id",nullable=false,unique=true,length=20)privateStringlegalId;*Genderof@Column(name="gender",nullable=false,unique=true,length=20)privateStringgender;*Dateofbirthof@Column(name="date_of_birth",columnDefinition="TIMESTAMP",length=0)privateDatedateOfBirth;*Dateonwhichuseris@Column(name="created_on",columnDefinition="TIMESTAMPDEFAULTCURRENT_TIMESTAMP",length=0)privateDate*Dateonwhichuseris@Column(name="deleted_on",columnDefinition="TIMESTAMPDEFAULTNULL",length=0)privateDatepublicStringtoString()return"UserDetail[id="+id+",userId="+userId+",firstName="+firstName+",lastName="+lastName+",middleName="+middleName+",legalId="+legalId+",gender="+gender+",createdOn="+createdOn+",deletedOn="+deletedOn+packageimportorg.springframework.data.jpa.repository.JpaRepository;importcom.practicalMicroservcies.entity.UserDetail;publicinterfaceUserDetailRepositoryextendsJpaRepository<UserDetail,Integer>{UserDetailfindByUserId(String同時需要一個servicepackageimportjava.util.Date;importimportjavax.annotation.Resource;importjavax.transaction.Transactional;importimportcom.practicalMicroservcies.entity.Address;importcom.practicalMicroservcies.entity.UserDetail;importcom.practicalMicroservcies.repo.AddressRepository;importcom.practicalMicroservcies.repo.UserDetailRepository;publicclassUserDetailServicesAddressRepositoryUserDetailRepositoryuserRepo;publicvoidsaveAddress(Addressaddress){System.out.println("UserSaved!");publicvoidsaveUser(UserDetailuserDetail){System.out.println("UserSaved!");publicAddressgetAddress(UUIDuserId){AddressreturnAddressObject=returnreturnAddressObject;publicUserDetailgetUser(UUIDuserId){UserDetailuserObjectToRetrun=System.out.println("UserSaved!");returnpublicvoiddeleteUser(UUIDuserId){AddressaddressObject=addressObject.setDeletedOn(newDate());UserDetailuserObject=userObject.setDeletedOn(newDate());System.out.println("UserDeleted!");publicclassUserController{UserDetailServicesObjectMappermapper;@RequestMapping(method=RequestMethod.POST,value="{userId}/address",produces="application/json",consumes="application/json")publicResponseEntity<String>createUserAddress(@RequestBodyAddressaddress,@PathVariable("userId")UUIDuserId){logger.debug(createUserAddress+"AddressforuserId"+userId+"isupdatedas"+address);returnnew//BelowMethodisresponsibleforcreatingauser.publicstaticfinalStringcreateUser="createUser():";@RequestMapping(method=RequestMethod.POST,value="{userId}",produces="application/json",consumes="application/json")publicResponseEntity<String>createUser(@RequestBodyUserDetailuserDetail,@PathVariable("userId")UUIDuserId){logger.debug(createUser+"creatinguserwithId"+userId+"anddetails:"+userDetail);returnnew@RequestMapping(method=RequestMethod.DELETE,value="{userId}",produces="application/json",consumes="application/json")publicResponseEntity<String>deleteUser(@RequestBodyUserDetailuserDetail,@PathVariable("userId")UUIDuserId){logger.debug(deleteUser+"deletinguserwithId"+userId);returnnew@RequestMapping(method=RequestMethod.GET,value="{userId}",produces="application/json",consumes="application/json")publicResponseEntity<UserDetail>getUser(@PathVariable("userId")UUIDuserId){logger.debug(getUser+"gettinginformationforuserId"+userId);UserDetailobjectToReturn=userService.getUser(userId);if(objectToReturn==returnnewResponseEntity<>(HttpStatus.NOT_FOUND);returnnewResponseEntity<>(objectToReturn,@RequestMapping(method=RequestMethod.GET,value="{userId}/address",produces="application/json",consumes=publicResponseEntity<Address>getAddress(@PathVariable("userId")UUIDuserId){logger.debug(getAddress+"gettingaddressforuserId:"+AddressobjectToReturn=userService.getAddress(userId);if(objectToReturn==null)returnnewResponseEntity<>(HttpStatus.NOT_FOUND);returnnewResponseEntity<>(objectToReturn,PostPosthttp://localhost:8080/PM/user/<user_Id>/addressGetPosthttp://localhost:8080/PM/user/<user_Id>GetPosthttp://localhost:8080/PM/user/<user_Id>/addressDeletePosthttp://localhost:8080/PM/user/<user_Id>至此,我們可以測試一下這個簡單的用戶服務了。先在不同的終端通過mvnspring-"userId":"93a52ce4-9331-43b5-8b56-"city":"NewYork","addressLine1":"4719FermunRoad","addressLine2":"YorktownHeights","pinCode":"10004"要創建用戶,可以使用下面這樣一個"userId":"93a52ce4-9331-43b5-8b56-"firstName":"John","lastName":"Montgomery","middleName":"Allen","legalId":"B053-62-64087","gender":"dateOfBirth":"2010-10-子。因為本書會以SpringBoot作為編碼技術基礎,所以這些例子也是基于SpringBoot實現第3章微服務端點之間的通信第2章主要討論了服務發現和網關模式。本章將講解如何發現微服務,但重點是挖掘議有、、等。本章還會就微服務間通信的主流模式進行講解,最后會以一種模式為例講解微服務間的通信。微服務間應該如何通信;編制(orchestration)和編排(choreography);同步通信和異步通信;使用消息代理的事件驅動微服務;財務微服務的開發。ohon(編制)這個詞又指管弦樂編曲,編制這種模式是從管弦樂隊的指揮這種角樂家就知道什么時候演奏哪個樂器,最終完成一次完整的演出,如圖31所示。3-2所示。這個時候很容易可以知道其貸款的申請進度,但是由于核心邏輯現在以根據調用狀態做出相應的決策。同步通信實現起來較為簡單。基于同步通信的這種請求響應的架構,最佳選擇就是協議。雖然微服務開發人員將基于和格式的通信方式視為一等公民,但是、以及ok編程等方案也可以用來做同步通信。為了加深理解,下面我們以一個庫存管理系統作為例子來進行講解。假設太陽鏡的網站hpyy.o的墨鏡非常流行。再假設該網站要在圣誕節期間搞促銷活動,活動期間會有很多用戶會將同一個商品搶到自己的購物車中。在此種情況種方式,這里為了示例的需要,假設微服務之間是以的方式通信的,那么整個過程關閉狀態;開啟狀態;半開啟狀態。Spring中的NetflixHystrix就是作斷路器的。只需要添加幾個注解就可以用這個現成的斷路公開的URLhttp://<應用IP:端口號>/bookingapplication/{userId}/{movieId}來完成訂packageimportimportorg.springframework.boot.autoconfigure.SpringBootApplication;importorg.springframework.web.bind.annotation.RestController;importorg.springframework.web.bind.annotation.RequestMapping;publicclassConsumingApplicationprivateConsumingService@RequestMapping(method=RequestMethod.POST,value="/book/{movieId}",produces="application/json")publicStringbookticket(@PathVariable("userId")StringuserId,@PathVariable("movieId")StringmovieId){returnpublicstaticvoidmain(String[]args){SpringApplication.run(ConsumingApplication.class,args);這里的@EnableCircuitBreaker啟用了SpringBoot應用中的斷路器模式。為了在應用中使publicclassConsumingServiceprivateRestTemplate@HystrixCommand(fallbackMethod="getAnotherCurentlyShowingMovie")publicStringbookAndRespond(){URIuri=returnthis.restTemplate.getForObject(uri,publicStringgetAnotherCurentlyShwoingMovie()return"Weareexperiencingheavyloadonbookingofyourmovie.Therearesomeothermoviesarerunningonsametime,pleasecheckifyouareinterestedinanyone."+getSameShowtimeMovieList();publicStringgetSameShowtimeMovieList(){return"fastandFurious8,TheWolverine3";getAnotherCurrentlyShowingMovie的方法基于消息/言基于消息的通信是點對點通信的,而基于事件的通信通常是一種發布訂閱模式。在消于命令的通信方式中,預先定義的格式應該是所有服務共享的。消息驅動模式如圖35所示,服務向預定義的隊列發布了一條消息,也就是說,邏輯上來講服務此時是知道服務的存在的,服務知道服務會監聽這個隊列中的消息然后根據消息內容進行后續動作。而在事件驅動的通信方式中,服務發出一個事件之后,就可以忘記該事件了。這個發布出來的事件可能會被服務使用,也可能被服務使用,還有可能會被一個最近才剛啟動與基于消息的通信模式是略有些不同的。事件驅動的通信方式如圖36所示,服務完成中服務是知道服務一定會監聽消息的,因此服務放到消息中的數據應該只有服務能夠解析,或者這部分數據只對有用。而在事件驅動方式中,完成的工作應該有一個統一有服務都是一樣的統一格式。圖36描述了事件驅動的這個場景。來接收一些消息事件,然后有針對性地做出一些行為。消息的發起方根本就不用關心接收消息的到底有哪些微服務。運行的復雜性會增加運維成本,因為需要額外安裝、配置和維護高可用的消息代理。觀察系統中某個時間點的準確狀態變得更加困難,因為消息流和事件會分發到很多不某些時候,將原本請求/響應結構轉換成基于消息的模式是困難的。以一個火車票訂使用REST為了講解消息代理的異步通信實現方式,讓我們以一個名為“CrispyBun”的漢堡店作為例bb是目前最為流行的實現了高級消息隊列協議(dvndgeuungoo,)協議的消息代理解決方案之一。它是基于ng語言的,跟普通消息代理直接將消息發布到隊列中不同的是,bb中需要先經過一個消息交換機。交換機不同的隊列,通過不同的路由鍵進行區分。例如,在我們的例子中,訂單中可能會有飲料,然后相應地我們需要兩種綁定鍵來區分,即綁定h_ky和綁定_ky。其中綁定_ky會發給廚師和飲料兩個隊列,而綁定h_ky則只發給廚師隊列。如果訂單中沒有飲料的話,只需要發給廚師隊列,會使用綁定h_ky,如圖38所示。執行sudoapt-getupdateecho"deb/debian/testingmain"curl/rabbitmq-signing-key-public.asc|sudoapt-keyadd使用sudoapt-getupdate運行sudoapt-getinstallrabbitmq-server命令來安裝RabbitMQ務。如果服務沒有自動啟動,也可以使用servicerabbitmq-serverstart命令來手動啟sudorabbotmq-pluginsenable目前bb已經啟動,還需要給生產者寫一點代碼,讓它能真正產生訂單,并且將訂單消息提交到默認的交換機中去。默認的交換機沒有名字,是一個空字符串,此時消息會被轉發到名為的隊列中去。訂單生產者的pom.xml<?xmlversion="1.0"encoding="UTF-<projectxmlns="/POM/4.0.0"<version>0.0.1-<description>com.practicalMircorservice<artifactId>spring-boot-starter-<relativePath/><!--lookupparentfromrepository--<project.build.sourceEncoding>UTF-<project.reporting.outputEncoding>UTF-<artifactId>spring-cloud-starter-stream-<artifactId>spring-boot-starter-<artifactId>spring-cloud-<artifactId>spring-boot-maven-EventProducerApplication.java:這個文件是應用程序的主應用類,我們會將RESTCrispyBunOrder.java:這個是用來定義訂單對象的類。EventProducerApplication.javapublicclassEventProducerApplicationprivatefinalStringQueue="crispyBunOrder";privateRabbitTemplatepublicstaticvoidmain(String[]args){SpringApplication.run(EventProducerApplication.class,args);@RequestMapping(method=RequestMethod.POST,value="/orders/{orderId}")publicvoidplaceOrder(@PathVariable("orderId")UUIDorderId,@RequestParam("itemId")IntegeritemId,@RequestParam("userName")StringuserName)CrispyBunOrderorderObject=createOrder(orderId,itemId,userName);privateCrispyBunOrdercreateOrder(UUIDorderId,IntegeritemId,StringuserName){CrispyBunOrderorder=newCrispyBunOrder();order.setOrderPlacedTime(newDate());returnorder;象模型(ProjectObjectModel,POM)文件中添加了與RabbitMQ相關的依賴,SpringBootCrispyBunOrder類有4packageimportjava.io.Serializable;importjava.util.Date;importjava.util.UUID;publicclassCrispyBunOrderimplementsprivatestaticfinallongserialVersionUID=privateUUIDorderId;privateIntegeritemId;privateDateorderPlacedTime;privateStringuserName;publicUUIDgetOrderId(){returnpublicvoidsetOrderId(UUIDorderId){this.orderId=orderId;publicIntegergetItemId(){returnitemId;publicvoidsetItemId(IntegeritemId){this.itemId=itemId;publicDategetOrderPlacedTime(){returnorderPlacedTime;publicvoidsetOrderPlacedTime(DateorderPlacedTime){this.orderPlacedTime=orderPlacedTime;publicStringgetUserName(){returnuserName;publicvoidsetUserName(StringuserName){this.userName=userName;該應用的pertiespackageimportimportorg.springframework.amqp.rabbit.annotation.RabbitHandler;importorg.springframework.amqp.rabbit.annotation.RabbitListener;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.EnableAutoConfiguration;importorg.springframework.boot.autoconfigure.SpringBootApplication;importorg.springframework.context.annotation.Bean;import@RabbitListener(queues="crispyBunOrder")publicclassEventConsumerApplicationpublicQueuecrispyBunOrderQueue(){retu

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論