https://www.bilibili.com/video/BV1PE411i7CV?spm_id_from=333.999.0.0
javase:OOP
mysql:持久化
html+css+Jquery+框架:檢視
javaweb:獨立開發mvc三層架構的網站:原始
SSM:框架:簡化了開發流程:設定也開始較為複雜;
war:tomcat 執行
Spring再簡化:Spring Boot;jar :內嵌tomcat;微服務架構!
服務越來越多:Spring Cloud 整理服務
培訓講師
(面向面試培訓,叫你如何使用,快速上手!)
做教育的
如何學習新東西,如何持續學習,如果關注這個行業!
知道這個東西的來龍去脈,歷史,理論;(用來做什麼?怎麼用?)
程式 = 資料結構+演演算法(集合框架);(程式猿)
程式 = 物件導向+框架;(碼農)
什麼是Spring ?
Spring 是一個開源框架,2003年興起的一個輕量級java 開發框架,Rod Johnson(音樂學的博士,本科不是計算機,頭髮挺多的)
Spring 是為了解決企業級應用開發的複雜性而建立,簡化開發
Spring是如何簡化開發的?
過javawed的就知道,開發一個web應用,從最初開始接觸Servlet結合tomcat 。跑出一個hello world程式,是要經歷特別多的步驟;後面就用來Spring MVC框架,到現在的Spring Boot,過一兩年可能又會有其他框架出現;有經歷過框架的不斷演進,然後自己開發專案所有的技術也在不斷的變化、改造嗎?
什麼是Spring Boot呢?就是一個javawevb的開發框架,和SpringMVC類似,對比其他javaweb框架的好處,官方說是「簡化開發」,約定大於設定,能迅速的開發web應用,幾行程式碼開發一個http介面。
所有的技術框架的發展似乎都遵循了一條主線規律:從一個複雜應用場景 衍生 一種規範框架,人們只需要進行各種設定而不需要自己去實現它,這時候強大的設定功能成了優點;發展到一定程度之後,人們根據實際生產應用情況,選取其中實用功能和設計精華,重構出一些輕量級的框架;之後為了提高開發效率,嫌棄原先的各類設定過於麻煩,於是開始提倡「約定大於設定」,進而衍生出一些一站式的解決方案。
是的這就是Java企業級應用->J2EE->spring->springboot的過程。
隨著 Spring 不斷的發展,涉及的領域越來越多,專案整合開發需要配合各種各樣的檔案,慢慢變得不那麼易用簡單,違背了最初的理念,甚至人稱設定地獄。Spring Boot 正是在這樣的一個背景下被抽象出來的開發框架,目的為了讓大家更容易的使用 Spring 、更容易的整合各種常用的中介軟體、開源軟體;
Spring Boot 基於 Spring 開發,Spirng Boot 本身並不提供 Spring 框架的核心特性以及擴充套件功能,只是用於快速、敏捷地開發新一代基於 Spring 框架的應用程式。也就是說,它並不是用來替代 Spring 的解決方案,而是和 Spring 框架緊密結合用於提升 Spring 開發者體驗的工具。Spring Boot 以約定大於設定的核心思想,預設幫我們進行了很多設定,多數 Spring Boot 應用只需要很少的 Spring 設定。同時它整合了大量常用的第三方庫設定(例如 Redis、MongoDB、Jpa、RabbitMQ、Quartz 等等),Spring Boot 應用中這些第三方庫幾乎可以零設定的開箱即用。
簡單來說就是SpringBoot其實不是什麼新的框架,它預設設定了很多框架的使用方式,就像maven整合了所有的jar包,spring boot整合了所有的框架 。
Spring Boot 出生名門(Spring已經很火了),從一開始就站在一個比較高的起點,又經過這幾年的發展,生態足夠完善,Spring Boot 已經當之無愧成為 Java 領域最熱門的技術。
Spring Boot的主要優點:
1.為所有Spring開發者更快的入門
2.開箱即用,提供各種預設設定來簡化專案設定
3.內嵌式容器簡化Web專案
4.沒有冗餘程式碼生成和XML設定的要求
SpringBoot 簡化Spring的 ,和Spring本質上是一樣的。
「微服務架構(Microservice Architecture)」一詞在過去幾年裡廣泛的傳播,它用於描述一種設計應用程式的特別方式,作為一套獨立可部署的服務。目前,這種架構方式還沒有準確的定義,但是在圍繞業務能力的組織、自動部署(automated deployment)、端智慧(intelligence in the endpoints)、語言和資料的分散控制,卻有著某種共同的特徵。
「微服務(Microservices)」——只不過在滿大街充斥的軟體架構中的一新名詞而已。儘管我們非常鄙視 這樣的東西,但是這玩意所描述的軟體風格,越來越引起我們的注意。在過去幾年裡,我們發現越來越多的專案開始使用這種風格,以至於我們身邊的同事在構建企業級應用時,把它理所當然的認為這是一種預設開發形式。然而,很不幸,微服務風格是什麼,應該怎麼開發,關於這樣的理論描述卻很難找到。
簡而言之,微服務架構風格,就像是把一個單獨的應用程式開發為一套小服務,每個小服務執行在自己的程序中,並使用輕量級機制通訊,通常是 HTTP API。這些服務圍繞業務能力來構建,並通過完全自動化部署機制來獨立部署。這些服務使用不同的程式語言書寫,以及不同資料儲存技術,並保持最低限度的集中式管理。
在開始介紹微服務風格(microservice style)前,比較一下整體風格(monolithic style)是很有幫助的:一個完整應用程式(monolithic application)構建成一個單獨的單元。企業級應用通常被構建成三個主要部分:使用者端使用者介面(由執行在客戶機器上的瀏覽器的 HTML 頁面、Javascript 組成)、資料庫(由許多的表構成一個通用的、相互關聯的資料管理系統)、伺服器端應用。伺服器端應用處理 HTTP 請求,執行領域邏輯(domain logic),檢索並更新資料庫中的資料,使用適當的 HTML 檢視傳送給瀏覽器。伺服器端應用是完整的 ,是一個單獨的的邏輯執行。任何對系統的改變都涉及到重新構建和部署一個新版本的伺服器端應用程式。
這樣的整體服務(monolithic server)是一種構建系統很自然的方式。雖然你可以利用開發語基礎特性把應用程式劃分成類、函數、名稱空間,但所有你處理請求的邏輯都執行在一個單獨的程序中。在某些場景中,開發者可以在的筆計本上開發、測試應用,然後利用部署通道來保證經過正常測試的變更,釋出到產品中。你也可以使用橫向擴充套件,通過負載均衡將多個應用部署到多臺伺服器上。
整體應用程式(Monolithic applications)相當成功,但是越來越多的人感覺到有點不妥,特別是在雲中部署時。變更釋出週期被繫結了——只是變更應用程式的一小部分,卻要求整個重新構建和部署。隨著時間的推移,很難再保持一個好的模組化結構,使得一個模組的變更很難不影響到其它模組。擴充套件就需要整個應用程式的擴充套件,而不能進行部分擴充套件。
微服務風格的特性
這導致了微服務架構風格(microservice architectural style)的出現:把應用程式構建為一套服務。事實是,服務可以獨立部署和擴充套件,每個服務提供了一個堅實的模組邊界,甚至不同的服務可以用不同的程式語言編寫。它們可以被不同的團隊管理。
我們必須說,微服務風格不是什麼新東西,它至少可以追溯到 Unix 的設計原則。但是並沒有太多人考慮微服務架構,如果他們用了,那麼很多軟體都會更好。
元件化(Componentization )與服務(Services)
自從我們開始軟體行業以來,一直希望由元件構建系統,就像我們在物理世界所看到的一樣。在過去的幾十年裡,我們已經看到了公共庫的大量簡編取得了相當的進步,這些庫是大部分語言平臺的一部分。
當我們談論元件時,可能會陷入一個困境——什麼是元件。我們的定義是,元件(component)是一個可獨立替換和升級的軟體單元。
微服務架構(Microservice architectures)會使用庫(libraries),但元件化軟體的主要方式是把它拆分成服務。我們把庫(libraries)定義為元件,這些元件被連結到程式,並通過記憶體中函數呼叫(in-memory function calls)來呼叫,而服務(services )是程序外元件(out-of-process components),他們利用某個機制通訊,比如 WebService 請求,或遠端過程呼叫(remote procedure call)。元件和服務在很多物件導向程式設計中是不同的概念。
把服務當成元件(而不是元件庫)的一個主要原因是,服務可以獨立部署。如果你的應用程式是由一個單獨程序中的很多庫組成,那麼對任何一個元件的改變都將導致必須重新部署整個應用程式。但是如果你把應用程式拆分成很多服務,那你只需要重新部署那個改變的服務。當然,這也不是絕對的,有些服務會改變導致協調的服務介面,但是一個好的微服務架構的目標就是通過在服務契約(service contracts)中解耦服務的邊界和進化機制來避免這些。
另一個考慮是,把服務當元件將擁有更清晰的元件介面。大多數開發語言都沒有一個良好的機制來定義一個釋出的介面(Published Interface)。釋出的介面是指一個類向外公開的成員,比如 Java 中的宣告為 Public 的成員,C# 中宣告為非 Internal 的成員。通常只有在檔案和規範中會說明,這是為了讓避免使用者端破壞元件的封裝性,阻止元件間緊耦合。服務通過使用公開遠端呼叫機制可以很容易避免這些。
像這樣使用服務也有不足之處。遠端呼叫比進位制內呼叫更消耗資源,因此遠端 API 需要粗粒度(coarser-grained),但這會比較難使用。如果你需要調整元件間的職責分配,當跨越程序邊界時,這樣做將會很難。
一個可能是,我們看到,服務可以對映到執行時程序(runtime processes)上,但也只是一個可能。服務可以由多個行程群組成,它們會同時開發和部署,例如一個應用程式程序和一個只能由這個服務使用的資料庫。
圍繞業務功能的組織
當尋找把一個大的應用程式拆分成小的部分時,通常管理都會集中在技術層面,UI團隊、伺服器端業務邏輯團隊和資料庫團隊。當使用這種標準對團隊進行劃分時,甚至小小的更變都將導致跨團隊專案共同作業,從而消耗時間和預算審批。一個高效的團隊會針對這種情況進行改善,兩權相害取其輕。業務邏輯無處不在。實踐中,這就是 Conway’s Law 的一個例子。
設計一個系統的任何組織(廣義上)都會產生這樣一種設計,其結構是組織交流結構的複製。
——Melvyn Conway, 1967
Melvyn Conway 的意識是,像下圖所展示的,設計一個系統時,將人員劃分為 UI 團隊,中介軟體團隊,DBA 團隊,那麼相應地,軟體系統也就會自然地被劃分為 UI 介面,中介軟體系統,資料庫。
微服務(microservice )的劃分方法不同,它傾向圍繞業務功能的組織來分割服務。這些服務實現商業領域的軟體,包括使用者介面,持久化儲存,任何的外部共同作業。因此,團隊是跨職能的(cross-functional),包含開發過程所要求的所有技能:使用者體驗(user-experience)、資料庫(database)和專案管理(project management)。
www.comparethemarket.com就是採用這種組織形式。跨職能的團隊同時負責構建和運營每個產品,每個產品被分割成許多單個的服務,這些服務通過訊息匯流排(Message Bus)通訊。
大型的整體應用程式(monolithic applications)也可以按照業務功能進行模組化(modularized),儘管這樣情況不常見。當然,我們可以敦促一個構建整體應用程式(monolithic application )的大型團隊,按業務線來分割自己。我們已經看到的主要問題是,這種元件形式會導致很多的依賴。如果整體應用程式(monolithic applications)跨越很多模組邊界(modular boundaries ),那麼對於團隊的每個成員短期內修復它們是很困難的。此外,我們發現,模組化需要大量的強制規範。服務元件所要求的必需的更明確的分離使得保持團隊邊界清晰更加容易。
產品不是專案
大部分的軟體開發者都使用這樣的專案模式:至力於提供一些被認為是完整的軟體。交付一個他們認為完成的軟體。軟體移交給運維組織,然後,解散構建軟體的團隊。
微服務(Microservice )的支援者認為這種做法是不可取的,並提議團隊應該負責產品的整個生命週期。Amazon 理念是「你構建,你運維(you build, you run it)」,要求開發團隊對軟體產品的整個生命週期負責。這要求開發者每天都關注他們的軟體執行如何,增加更使用者的聯絡,同時承擔一些售後支援。
產品的理念,跟業務能力聯絡起來。不是著眼於完成一套功能的軟體,而是有一個持續的關係,是如何能夠幫助軟體及其使用者提升業務能力。
為什麼相同的方法不能用在整體應用程式(monolithic applications),但更小的服務粒度能夠使建立服務的開發者與使用者之間的個人聯絡更容易。
強化終端及弱化通道
當構建不同的程序間通訊機制的時候,我們發現有許多的產品和方法能夠把更加有效方法強加入的通訊機制中。比如企業服務匯流排(ESB),這樣的產品提供更有效的方式改進通訊過程中的路由、編碼、傳輸、以及業務處理規則。
微服務傾向於做如下的選擇:強化終端及弱化通道。微服務的應用致力鬆耦合和高內聚:採用單獨的業務邏輯,表現的更像經典Unix意義上的過濾器一樣,接受請求、處理業務邏輯、返回響應。它們更喜歡簡單的REST風格,而不是複雜的協定,如WS或者BPEL或者集中式框架。
有兩種協定最經常被使用到:包含資源API的HTTP的請求-響應和輕量級訊息通訊協定。最為重要的建議為:
善於利用網路,而不是限制(Be of the web, not behind the web)。
——Ian Robinson
微服務團隊採用這樣的原則和規範:基於網際網路(廣義上,包含Unix系統)構建系統。這樣經常使用的資源幾乎不用什麼的代價就可以被開發者或者執行商快取。
第二種做法是通過輕量級訊息匯流排來發布訊息。這種的通訊協定非常的單一(單一到只負責訊息路由),像RabbitMQ或者ZeroMQ這樣的簡單的實現甚至像可靠的非同步機制都沒提供,以至於需要依賴產生或者消費訊息的終端或者服務來處理這類問題。
在整體工風格中,元件在程序內執行,程序間的訊息通訊通常通過呼叫方法或者回撥函數。從整體式風格到微服務架構最大的問題在於通訊方式的變更。從記憶體內部原始的呼叫變成遠端呼叫,產生的大量的不可靠通訊。因此,你需要把粗粒度的方法成更加細粒度的通訊。
分散治理
集中治理的一種好處是在單一平臺上進行標準化。經驗表明這種趨勢的好處在縮小,因為並不是所有的問題都相同,而且解決方案並不是萬能的。我們更加傾向於採用適當的工具解決適當的問題,整體式的應用在一定程度上比多語言環境更有優勢,但也適合所有的情況。
把整體式框架中的元件,拆分成不同的服務,我們在構建它們時有更多的選擇。你想用Node.js去開發報表頁面嗎?做吧。用C++來構建時時性要求高的元件?很好。你想以在不同型別的資料庫中切換,來提高元件的讀取效能?我們現在有技術手段來實現它了。
當然,你是可以做更多的選擇,但也不意味的你就可以這樣做,因為你的系統使用這種方式進行侵害意味著你已經有的決定。
採用微服務的團隊更喜歡不同的標準。他們不會把這些標準寫在紙上,而是喜歡這樣的思想:開發有用的工具來解決開發者遇到的相似的問題。這些工具通常從實現中成長起來,並進行的廣泛範圍內分享,當然,它們有時,並不一定,會採用開源模式。現在開源的做法也變得越來越普遍,git或者github成為了它們事實上的版本控制系統。
Netfix就是這樣的一個組織,它是非常好的一個例子。分享有用的、尤其是經過實踐的程式碼庫激勵著其它的開發著也使用相似的方式來解決相似的問題,當然,也保留著根據需要使用不同的方法的權力。共用庫更關注於資料儲存、程序內通訊以及我們接下來做討論到的自動化等這些問題上。
微服務社群中,開銷問題特別引人注意。這並不是說,社群不認為服務互動的價值。相反,正是因為發現到它的價值。這使得他們在尋找各種方法來解決它們。如Tolearant Reader和Consumer-Driven Contracts這樣的設計模式就經常被微服務使用。這些模式解決了獨立服務在互動過程中的消耗問題。使用Consumer-Driven Contracts增加了你的信心,並實現了快速的反饋機制。事實上,我們知道澳大利亞的一個團隊致力使用Consumer-Drvien Contracts開發新的服務。他們使用簡單的工程,幫助他們定義服務的介面。使得在新服務的程式碼開始編寫之前,這些介面就成為自動化構建的一個部分。構建出來的服務,只需要指出這些介面適用的範圍,一個優雅的方法避免了新軟體中的’YAGNI '困境。這些技術和工具在使用過程中完善,通過減少服務間的耦合,限制了集中式管理的需求。
也許分散治理普及於亞馬遜「編譯它,運維它」的理念。團隊為他們開發的軟體負全部責任,也包含7*24小時的執行。全責任的方式並不常見,但是我們確實發現越來越多的公司在他們的團隊中所推廣。Netfix是另外一個接受這種理念的元件。每天凌晨3點被鬧鐘吵醒,因為你非常的關注寫的程式碼品質。這在傳統的集中式治理中這是一樣多麼不思議的事情呀。
分散資料管理
對資料的分散管理有多種不同的表現形式。最為抽象層次,它意味著不同系統中的通用概念是不同的。這帶來的覺問題是大型的跨系統整合時,使用者使用不同的售後支援將得到不同的促銷資訊。這種情況叫做並沒有給使用者顯示所有的促銷手段。不同的語法確實存在相同的詞義或者(更差)相同的詞義。
應用之間這個問題很普遍,但應用內部這個問題也存在,特別是當應用拆分成不同的元件時。對待這個問題非常有用的方式為Bounded Context的領域驅動設計。DDD把複雜的領域拆分成不同上下文邊界以及它們之間的關係。這樣的過程對於整體架構和微服務架構都很有用,但是服務間存在著明顯的關係,幫助我們對上下文邊界進行區分,同時也像我們在業務功能中談到的,強行拆分。
當對概念模式下決心進行分散管理時,微服務也決定著分散資料管理。當整體式的應用使用單一邏輯資料庫對資料持久化時,企業通常選擇在應用的範圍內使用一個資料庫,這些決定也受廠商的商業許可權模式驅動。微服務讓每個服務管理自己的資料庫:無論是相同資料庫的不同範例,或者是不同的資料庫系統。這種方法叫Polyglot Persistence。你可以把這種方法用在整體架構中,但是它更常見於微服務架構中。
微服務音分散資料現任意味著管理資料更新。處理資料更新的常用方法是使用事務來保證不同的資源修改資料庫的一致性。這種方法通常在整體架構中使用。
使用事務是因為它能夠幫助處理一至性問題,但對時間的消耗是嚴重的,這給跨服務操作帶來難題。分散式事務非常難以實施,因此微服務架構強調服務間事務的協調,並清楚的認識一致性只能是最終一致性以及通過補償運算處理問題。
選擇處理不一致問題對於開發團隊來說是新的挑戰,但是也是一個常見的業務實踐模式。通常業務上允許一定的不一致以滿足快速響應的需求,但同時也採用一些恢復的程序來處理這種錯誤。當業務上處理強一致性消耗比處理錯誤的消耗少時,這種付出是值的的。
基礎設施自動化
基礎設施自動化技術在過去幾年中得到了長足的發展:雲端計算,特別是AWS的發展,減少了構建、釋出、運維微服務的複雜性。
許多使用微服務架構的產品或者系統,它們的團隊擁有豐富的持集部署以及它的前任持續整合的經驗。團隊使用這種方式構建軟體致使更廣泛的依賴基礎設施自動化技術。下圖說明這種構建的流程:
儘管這不是介紹自動部署的文章,但我們也打算介紹一下它的主要特徵。我們希望我們的軟體應該這樣方便的工作,因此我們需要更多的自動化測試。流程中工作的軟體改進意味著我們能自動的部署到各種新的環境中。
整體風格的應用相當開心的在各種環境中構建、測試、釋出。事實證明,一旦你打算投資一條整體架構應用自動化的的生產線,那麼你會發現釋出更多的應用似乎非不那麼的可怕。記住,CD(持續部署)的一個目標在於讓釋出變得無趣,因此無論是一個還是三個應用,它都一樣的無趣。
另一個方面,我們發現使用微服務的團隊更加依賴於基礎設施的自動化。相比之下,在整體架構也微服務架構中,儘管釋出的場景不同,但釋出工作的無趣並沒有多大的區別。
容錯性設計
使用服務作為元件的一個結果在於應用需要有能容忍服務的故障的設計。任務服務可能因為供應商的不可靠而故障,使用者端需要儘可能的優化這種場景的響應。跟整體構架相比,這是一個缺點,因為它帶來的額外的複雜性。這將讓微服務團隊時刻的想到服務故障的情況下使用者的體驗。Netflix 的Simian Army可以為每個應用的服務及資料中心提供日常故障檢測和恢復。
這種產品中的自動化測試可以讓大部分的運維團隊正常的上下班。這並不意味著整體構架的應用沒有這麼精巧的監控設定,只是在我們的經驗中它並不常見。
由於服務可以隨時故障,快速故障檢測,乃至,自動恢復變更非常重要。微服務應用把實時的監控放在應用的各個階段中,檢測構架元素(每秒資料庫的接收的請求數)和業務相關的指標(把分鐘接收的定單數)。監控系統可以提供一種早期故障告警系統,讓開發團隊跟進並調查。
對於微服務架構來說,這相當重要,因為微服務相互的通訊可能導致緊急意外行為。許多專家車稱讚這種緊急事件的價值,但事實是這種緊急行為有時是災難。監控是至關重要的,它能快速發現這種緊急不良行為,讓我們迅速修復它。
整體架構,跟微服務一樣,在構建時是通明的,實情上,它們就是這樣子的。它們不同之處在於,你需要清楚的認識到不同程序間執行的服務是不相關的。庫對於同一程序是透明的,也因此不那麼重要了。
微服務團隊期望清楚的監控和記錄每個服務的設定,比如使用儀表盤顯示上/下線狀態、各種運維和業務相關的指標。對斷路器(circuit breaker)狀態、目前的吞吐量和時延細節,我們也會經常遇到。
設計改進
微服務實踐者,通常有不斷改進設計的背景,他們把服務分解成進一步的工具。這些工具可以讓應用開發者在不改變速度情況下,控制都他們的應用的需求變更。變更控制不意味首減少變更,而是使用適當的方式和工具,讓它更加頻繁,至少,很好讓它變得可控。
不論如何,當你試圖軟體系統拆分成元件時,你將面臨著如何拆分的問題。那麼我們的決定拆分我們應用的原則是什麼呢?首要的因素,元件可以被獨立替換和更新的,這意味著我們尋找的關鍵在於,我們要想象著重寫一個元件而不影響它們之前的共同作業關係。事實上,許多的微服務小組給它進一步的預期:服務應該能夠報廢的,而不是要長久的發展的。
Guardian網站就是這方面的一個優秀的例子,它初期被設計和構建成一個整體架構,但它已經向微服務的發展了。整體構架仍然是它網站的核心,但是他們使用微服務來增加那些使用整體架構API的新特性。這種方法增加這些臨時的特性非常方便,比如運動新聞的特稿。這樣站點的一個部分可以使用快速的開發語言迅速整合起來,當它過時後可以一次性移除。我們發現一家金融機構用相似的方法增加新的市場行銷活動,數週或者幾個月後把它復原。
可代替是模組化開發中的一個特例,它是用模組來應對需要變更的。你希望讓變更是相同模組,相同週期中進行變化而已。系統的某些很小做變更部分,也應該放在不同的服務中,這樣它們更容易讓它們消亡。如果你發現兩個服務一直重複的變更時,這就是一個要合併它們的訊號了。
把元件改成服務,增加了細化釋出計劃的一個機會。整體構架的任務變更需要整個應用的完整的構建和釋出。然而,使用微服務,你只需要釋出你要修改的服務就可以了。這將簡化和加速你的釋出週期。缺點是你需要為一個變更服務釋出可能中斷使用者的體驗而擔心。傳統的整合方法是使用版本來處理這些問題,但是微服務版本僅是最後的通告手段。我們需要在設計服務時儘可能的容忍供應商的變更,以避擴音供多個版本。
微服務是未來嗎?
我們寫這篇文章的主要目的在於解釋微服務的主要思想和原則。但是發時間做這事的時候,我們清醒的認識到微服務構架風格是一個非常重要的想法:一個值得企業應用中認真考慮的東西。我們最近使用這種風格構建了幾個系統,認識那些也使用和喜歡這種方法的愛好者。
我們認識的使用這種方式的先行者,包含亞馬遜、Netflix、The Guardian、The UK Government Digital Service、realestate.com.au、Forward和comparethemarket.com。2013看的巡迴會議充滿了向正在想成為微服務一分子的公司,包含Travis CI。此外,大量的元件正在從事我們認為是微服務的事,只是沒有使用微服務的名字而已。(通常,它們被打上SOA的標籤,儘管,我們認為SOA有許多不同的地方。)
儘管有這些積極的經驗,然後,我們也不急於確認微服務是未來軟體架構方向。至今為止,我們的經驗與整體風格的應該中相比出來的是有優勢的,但是我們意識知這樣的事實,我們並沒有足夠的時間來證明我們的論證。
你所使用的架構通常是你開發出來後,使用的幾年的實際成果。我們看到這些工程是在一個優秀的團隊,帶著對模組化的強烈追求,使用在過去幾年中已經衰退的整體架構構建出來的。許多人相信,這種衰退不太可能與微服務有關,因為服務邊界是清晰的並且很難再完善的。然而,當我們還沒看到足夠多的系統執行足夠長時間時,我們不能肯定微服務構架是成熟的。
當然,還有原因就是,有人期望微服務構架不夠成熟。在元件化方面的任何努力,其成功都依賴於軟體如何拆分成適合的元件。指出元件化的準確邊界應該在那,這是非常困難的。改良設計要承認邊界的權益困境和因此帶來的易於重構的重要性。但是當你的元件是被遠端通訊的服務時,重構比程序內的庫又要困難的多。服務邊界上的程式碼遷移是困難的,任務介面的變更需要參與者的共同共同作業,向後相容的層次需要被增加,測試也變更更加複雜。
另一個問題在於,如果元件並沒有清晰的劃分,你的工作的複雜性將從元件內部轉向元件間的關係。做這事不僅要圍繞著複雜,它也要面對著不清晰和更難控制的地方。很容易想到,當你在一個小的、簡單的元件內找東西,總比在沒有關係的混亂的服務間要容易。
最後,團隊技能也是重要的因素。新的技術傾向於被掌握更多的技能的團隊使用。但是掌握多技能的團隊中使用的技巧在較少技能的團隊中並不是必需的。我們發現大量的少技能的團隊構建混亂的整合構架,但是它要發時間去證明使用微服務在這種情況下會發生什麼。一個糟糕的團隊通常開發糟糕的系統:很難說,微服務在這種情況下是否能幫助它們,還是破壞它們。
一個理性的爭議在於,我們聽說,你不應該從微服務構架開始做。最好從整體構架開發,做模組化開發,然後當整體構架出現問題是再把模組化拆分成服務。(儘管這種建議不是好主意,因為一個好的程序內介面並不是一個好的服務介面。)
因此我們持這種謹慎的樂觀。到目前為止,我們還沒有足夠認識,關於微構架能否被大範圍的推廣。我們不能肯定的說,我們要終結什麼,但是軟體開發的挑戰在於你只能在不完整的資訊中決定你目前要處理的問題。
微服務系統多大?
儘管「微服務」一詞在架構風格中越來越流行,它的名字很不辛讓人關注它的服務大小,以及對「微」這個組成的爭議。在我們與微服務實踐者的談話中,我們發現了服務的大小範圍。被報道的最大團隊遵循亞馬遜Tow Pizaa團隊理念(比如,一個團隊吃兩個比薩就可以了。),這意味著不超過20號(一打)人。我們發現最小設定是半打的團隊支撐起一打的服務。
這也引發這樣的考慮:規模為一個服務一打人到一個服務一個人的團隊打上微服務的標籤。此刻我們認為,它們是一樣的,但是隨著對這種風格的深入研究,也存在我們改變我們的想法的可能。
微服務與SOA
當前我們談到微服務時,通常會問,這是不是我們20年前討論的面向服務架構(SOA)。這是一個很好的觀點,因為微服務風格也SOA所提倡的一些優勢非常相似。儘管如此,問題在於SOA意味的太多不同的東西了,因此通常時候我們談的所謂「SOA」時,它與我們談論的風格不一至,因為它通常是指在整體風格應用中的ESB。
此外,我們發現面向服務的風格是這麼的拙劣:從試圖使用ESB隱藏複雜性, 到使用多年才認識到發費數百美元卻沒產生任務價值這樣的失敗,到集中治理模式抑制變更。而且這些問題往往很難發現。
可以肯定的時,微服務社群中使用的許多的技術都開發者是從大型電腦構的整合服務經驗中發展來的。
Tolerant Reader模式就是這樣的一個例子。由於網際網路的發展,利用簡單的協定這種方法,讓它從這些經驗傳達的出來。這是從已經很複雜的集中式標準中的一種反模式,坦白的說,真讓人驚歎。(無論何時,當你需要用一個服務來管理你的所有的服務,你就知道這很麻煩。)
SOA的這種常見行為讓微服務的提倡者拒絕打上SOA的標籤,儘管有人認為微服務是從SOA中發展而來的,或許面向服務是對的。無論如何,事實上SOA表達這麼多的含義,它給一個團隊清醒的認識到這種構架風格就已經值的了。
多語言,多選擇
JVM做為一個平臺,它的增長就是一個平臺中執行多語言的最大的例子。過去二十年中,它通常做為更高層次語言的殼,以達到更高層次的抽象。比如,研究它的內部結構,、使用低階的語言寫更高效的程式碼。儘管如此,許多整體風格並不需要這種層次的效能優化或者在語法及高層次上的抽象,這很常見(讓我們很失望)。此外整體構架通常意味著使用單一的語言,這也限制著使用技術的數量。
實踐標準和強制標準
它有點尷尬,微服務團隊傾向於避免這種通常由企業架構隊伍客製化的僵硬的強制標準,但是它們卻非常樂於甚至推廣這些開放的標準,如HTTP、ATOM、其它微規範。
關鍵的不同在這些標準是怎麼開發出來的,以及它們是怎麼被推廣的。標準被一些元件管理,如IETF認證標準,僅當它們在網際網路上有幾個在用的實現,通常源自於開源工程的成功應用。
這些標準單獨分離出來,與那種在企業中通常有沒有什麼編碼經驗的或者沒有什麼影響力的廠商標準進行區別。
讓做對事更容易
一方面,我們發現在持續釋出、部署越來越多的使用自動化,是很多有用的工具開發出來幫助開發者和運營商的努力結果。為打包、程式碼管理、支撐服務的工具,或者增加標準監控的記錄的工具,現在都非常常見了。網路中最好的,可能就是Netflix’s的開源工具,但是包含Dropwizard在內的其它工具也被廣泛的使用著。
斷路器(circuit breaker)和產品中現有的程式碼
斷路器(circuit breaker)出現在《Realease It!》一書中,與Bulkhead和Timeout這樣的模式放在一起。實施起來,這些模式用於構建通訊應用時相當的重要。Netflix的部落格在解釋它們的應用時,做了大量的工作。
同步是有害的
任務時候,你在服務間的呼叫使用同步的方法,都會遇到宕機時間的乘積效應。簡單的說,你的系統宕機時間是你係統的單獨元件的宕機時間的乘積。你面臨的選擇使用非同步或者管理宕機時間。在www.guardian.co.uk中,它們在新平臺中使用一種簡單的規則來實現它:在Netflix中每次使用者請求的同步呼叫,他們重新設計的平臺API都會把它構建成非同步的API來執行。
總結
MVC:三層架構(控制層,業務層,持久層)
MVVM:
微服務架構:
官方說法:
微服務是一種架構風格,它要求我們在開發一個應用的時候,這個應用必須構建成一系列小服務的組合;可以通過http的方式進行互通。RPC
隨著網際網路的發展,網站應用的規模不斷擴大,常規的垂直應用架構已無法應對,分散式服務架構以及流動計算架構勢在必行,亟需一個治理系統確保架構有條不紊的演進。
單體應用架構
微服務架構
5. 把所有的功能單元放在一個應用裡面,然後我們整個應用部署到伺服器上。如果負載能力不行,我們將整個應用進行水平復制,進行擴充套件,然後再負載均衡。
6. 所謂微服務架構,就是打破之前單體應用的架構方式,把每個功能元素獨立出來。把獨立出來的功能元素的動態組合,需要的功能元素才去拿來組合,需要多一些時可以整合多個功能元素。所有微服務是對功能元素進行復制,而沒有對整個應用進行復制
7. 這樣做的好處是:
8. 節省了呼叫資源
9. 每個功能元素的服務都是一個可替換的、可獨立升級的軟體程式碼。
10. 以下這張 很形象的描述出了 微服務
11. 把每個功能元素獨立出來
12. 把獨立出來的功能元素的動態組合
13. 需要的功能元素才去拿來組合
使用者下單:controller 1s
倉庫凍結:資金凍結:驗證:購買成功,倉庫數量減少,倉庫解凍,服務解凍,資金解凍 10s
程式:高內聚,低耦合
文章推薦
原文:https://martinfowler.com/articles/microservices.html
論文:https://www.cnblogs.com/liuning8023/p/4493156.html
一個大型的微服務架構,就像一個複雜交織的神經網路,每一個神經元就是一個功能元素,他們各自完成註解的功能,然後通過http相互請求呼叫。
比如一個電商系統,查快取、連資料庫、瀏覽頁面、結賬、支付等服務都是一個個獨立的功能服務,都被微化了,它們作為一個個微服務共同構建了一個龐大的系統。修改其中的一個功能,只需升級其中一個功能服務單元即可。
但是這種龐大的系統架構給部署和運維帶來很大的難度。於是,Spring為我們帶來了構建大型分散式微服務的全套、全程產品
官網:https://spring.io/projects/spring-boot
版本
官方提供一個快速構建專案的網站
https://start.spring.io/
構建Spring boot 專案 新增web 依賴
下載
Spring boot 預設結構
直接使用idea 建立一個Spring Boot專案(一般開發直接在idea建立)
spring-boot-starter 所有的Springboot 依賴都是使用這個開頭的
<!--web 依賴 tomcat,dispatcherServlet,xml 都沒有了-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--單元測試-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--打jar 包外掛-->
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
只需一步:到專案下的 resources 目錄下新建一個banner.txt 即可。
圖案可以到:https://www.bootschool.net/ascii 這個網站生成,然後拷貝到檔案中即可!
// _ooOoo_ //
// o8888888o //
// 88" . "88 //
// (| ^_^ |) //
// O\ = /O //
// ____/`---'\____ //
// .' \\| |// `. //
// / \\||| : |||// \ //
// / _||||| -:- |||||- \ //
// | | \\\ - /// | | //
// | \_| ''\---/'' | | //
// \ .-\__ `-` ___/-. / //
// ___`. .' /--.--\ `. . ___ //
// ."" '< `.___\_<|>_/___.' >'"". //
// | | : `- \`.;`\ _ /`;.`/ - ` : | | //
// \ \ `-. \_ __\ /__ _/ .-` / / //
// ========`-.____`-.___\_____/___.-`____.-'======== //
// `=---=' //
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //
// 佛祖保佑 永不宕機 永無BUG //
選擇Spring Initalizr
選擇版本
選擇要新增的依賴
設定專案名
編寫一個Controller測試
測試
SpringBoot使用一個全域性的組態檔 , 組態檔名稱是固定的
application.properties
語法結構 :key=value
application.yml
語法結構 :key:空格 value
組態檔的作用 :修改SpringBoot自動設定的預設值,因為SpringBoot在底層都給我們自動設定好了;
比如我們可以在組態檔中修改Tomcat 預設啟動的埠號!測試一下!
server:
port: 8081
yaml概述
YAML是 「YAML Ain’t a Markup Language」 (YAML不是一種標示語言)的遞迴縮寫。在開發的這種語言時,YAML 的意思其實是:「Yet Another Markup Language」(仍是一種標示語言)
這種語言以資料作為中心,而不是以標示語言為重點!
以前的組態檔,大多數都是使用xml來設定;比如一個簡單的埠設定,我們來對比下yaml和xml
傳統xml設定:
<server>
<port>8081<port>
</server>
yaml設定:
server:
port: 8081
properties 和yml 對比
yml可以注入到我們的設定類中
yml 使用 物件、Map(鍵值對)
#物件、Map格式
k: v1:
v2:
1、在springboot專案中的resources目錄下新建一個檔案 application.yml
2、編寫一個實體類 Dog;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Component
public class Dog {
private String name;
private Integer age;
}
3、編寫一個實體類 Person;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
private String name;
private Integer age;
private Boolean happy;
private Date birth;
private Map<String,Object> maps;
private List<Object> lists;
private Dog dog;
}
4、在yml檔案中設定person類
person:
name: xy
age: 18
happy: false
birth: 2021/5/3
maps:
k1: v1
k2: v2
lists:
- code
- music
- girl
dog:
name: 旺財
age: 3
5、測試
@Test
void contextLoads() {
System.out.println(person);
}
6、結果
Person(name=xy, age=18, happy=false, birth=Mon May 03 00:00:00 CST 2021, maps={k1=v1, k2=v2}, lists=[code, music, girl], dog=Dog{name='旺財', age=3})
IDEA 提示,springboot設定註解處理器沒有找到,讓我們看檔案,我們可以檢視檔案,找到一個依賴!
<!-- 匯入組態檔處理器,組態檔進行繫結就會有提示,需要重新啟動 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
@ConfigurationProperties
作用:
@PropertySource註解
用於載入指定的組態檔
dog類
@Component
@NoArgsConstructor
@AllArgsConstructor
@Data
@PropertySource(value = "classpath:xy.properties")
public class Dog {
//SPEL表示式讀取組態檔的值
@Value("${dog.name}")
private String name;
@Value("${dog.age}")
private Integer age;
}
xy.properties檔案
dog.name=旺財
dog.age=3
測試
properties組態檔在寫中文的時候,會有亂碼 , 我們需要去IDEA中設定編碼格式為UTF-8;
對比小結:
@Value
這個使用起來並不友好!我們需要為每個屬性單獨註解賦值
@ConfigurationProperties
只需要寫一次即可 , @Value
則需要每個欄位都新增鬆散繫結
dog類
@Component
@NoArgsConstructor
@AllArgsConstructor
@Data
@ConfigurationProperties(prefix = "dog")
public class Dog {
private String firstName;
private Integer age;
}
yml檔案
dog:
first-name: 來福
age: 3
測試類
@Test
void contextLoads() {
System.out.println(dog);
}
結果
Dog(firstName=來福, age=3)
結論:
@configurationProperties
,不要猶豫!Springboot中可以用@validated來校驗資料,如果資料異常則會統一丟擲異常,方便異常中心統一處理。我們這裡來寫個註解讓我們的name只能支援Email格式;
JSR 303 基本的校驗規則
空檢查
@Null
驗證物件是否為null
@NotNull
驗證物件是否不為null, 無法查檢長度為0的字串
@NotBlank
檢查約束字串是不是Null還有被Trim的長度是否大於0,只對字串,且會去掉前後空格.
@NotEmpty
檢查約束元素是否為NULL或者是EMPTY.
Booelan檢查
@AssertTrue
驗證 Boolean 物件是否為 true
@AssertFalse
驗證 Boolean 物件是否為 false
長度檢查
@Size(min=, max=)
驗證物件(Array,Collection,Map,String)長度是否在給定的範圍之內
@Length(min=, max=)
Validates that the annotated string is between min and max included.
日期檢查
@Past
驗證 Date 和 Calendar 物件是否在當前時間之前,驗證成立的話被註釋的元素一定是一個過去的日期
@Future
驗證 Date 和 Calendar 物件是否在當前時間之後 ,驗證成立的話被註釋的元素一定是一個將來的日期
@Pattern
驗證 String 物件是否符合正規表示式的規則,被註釋的元素符合制定的正規表示式,regexp:正規表示式 flags: 指定 Pattern.Flag 的陣列,表示正規表示式的相關選項。
數值檢查
建議使用在Stirng,Integer型別,不建議使用在int型別上,因為表單值為「」時無法轉換為int,但可以轉換為Stirng為」「,Integer為null
@Min
驗證 Number 和 String 物件是否大等於指定的值
@Max
驗證 Number 和 String 物件是否小等於指定的值
@DecimalMax
被標註的值必須不大於約束中指定的最大值. 這個約束的引數是一個通過BigDecimal定義的最大值的字串表示.小數存在精度
@DecimalMin
被標註的值必須不小於約束中指定的最小值. 這個約束的引數是一個通過BigDecimal定義的最小值的字串表示.小數存在精度
@Digits
驗證 Number 和 String 的構成是否合法
@Digits(integer=,fraction=)
驗證字串是否是符合指定格式的數位,interger指定整數精度,fraction指定小數精度。
@Range(min=, max=)
被指定的元素必須在合適的範圍內
@Range
(min=10000,max=50000,message=」range.bean.wage」)
@Valid
遞迴的對關聯物件進行校驗, 如果關聯物件是個集合或者陣列,那麼對其中的元素進行遞迴校驗,如果是一個map,則對其中的值部分進行校驗.(是否進行遞迴驗證)
@CreditCardNumber
信用卡驗證
@Email
驗證是否是郵件地址,如果為null,不進行驗證,算通過驗證。
@ScriptAssert
(lang= ,script=, alias=)
@URL
(protocol=,host=, port=,regexp=, flags=)
person類
@Data
@AllArgsConstructor
@NoArgsConstructor
@Component
@ConfigurationProperties(prefix = "person")
@Validated//資料校驗
public class Person {
@NotNull
private String name;
private Integer age;
private Boolean happy;
private Date birth;
private Map<String,Object> maps;
private List<Object> lists;
private Dog dog;
}
yml 檔案
person:
age: 18
happy: false
birth: 2021/5/3
maps:
k1: v1
k2: v2
lists:
- code
- music
- girl
dog:
name: 旺財
age: 3
測試
@Test
void contextLoads() {
System.out.println(person);
}
測試結果:
我的name資料為賦值,所以為null,驗證不通過,丟擲異常
SpringBoot會從這四個位置全部載入主組態檔;互補設定;
優先順序1:專案路徑下的config資料夾組態檔
優先順序2:專案路徑下組態檔
優先順序3:資源路徑下的config資料夾組態檔
優先順序4:資源路徑下組態檔
多組態檔
我們在主組態檔編寫的時候,檔名可以是 application-{profile}.properties/yml , 用來指定多個環境版本;
#比如在組態檔中指定使用dev環境,我們可以通過設定不同的埠號進行測試;
#我們啟動SpringBoot,就可以看到已經切換到dev下的設定了;
spring:
profiles:
active: dev
19.靜態資源匯入探究
注意研究物件:WebMVCAutoConfiguration 類
研究方法 public void addResourceHandlers(ResourceHandlerRegistry registry)
在pom.xml檔案中引入
找到jquery
//如果有自動設定就失效了
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//如果這個靜態資源的東西已經被自定義了,直接return
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
}
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
//能找到靜態資源的地方
if (!registry.hasMappingForPattern("/webjars/**")) {
customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/")
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
//第二種,獲取靜態資源的路徑
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
.addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
}
以上是去webjars 中找靜態資源(第一種)很少使用
http://localhost:8080/webjars/jquery/3.6.0/jquery.js
第二種
/META-INF/resources/
/resources/
優先順序第一/static/
優先順序第二/public/
優先順序第三http://localhost:8080/**
20.首頁如何客製化
注意研究物件:WebMVCAutoConfiguration 類
index.html
檔案即可。template 目錄
下controller 跳轉
的匯入thymeleaf 依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
前端交給我們的頁面,是html頁面。如果是我們以前開發,我們需要把他們轉成jsp頁面,jsp好處就是當我們查出一些資料轉發到JSP頁面以後,我們可以用jsp輕鬆實現資料的顯示,及互動等。
jsp支援非常強大的功能,包括能寫Java程式碼,但是呢,我們現在的這種情況,SpringBoot這個專案首先是以jar的方式,不是war,像第二,我們用的還是嵌入式的Tomcat,所以呢,他現在預設是不支援jsp的。
那不支援jsp,如果我們直接用純靜態頁面的方式,那給我們開發會帶來非常大的麻煩,那怎麼辦呢?
SpringBoot推薦你可以來使用模板引擎:
模板引擎,我們其實大家聽到很多,其實jsp就是一個模板引擎,還有用的比較多的freemarker,包括SpringBoot給我們推薦的Thymeleaf,模板引擎有非常多,但再多的模板引擎,他們的思想都是一樣的,什麼樣一個思想呢我們來看一下這張圖:
模板引擎的作用就是我們來寫一個頁面模板,比如有些值呢,是動態的,我們寫一些表示式。
而這些值,從哪來呢,就是我們在後臺封裝一些資料。然後把這個模板和這個資料交給我們模板引擎,模板引擎按照我們這個資料幫你把這表示式解析、填充到我們指定的位置,然後把這個資料最終生成一個我們想要的內容給我們寫出去,這就是我們這個模板引擎,不管是jsp還是其他模板引擎,都是這個思想。
只不過呢,就是說不同模板引擎之間,他們可能這個語法有點不一樣。
SpringBoot給我們推薦的Thymeleaf模板引擎,這模板引擎呢,是一個高階語言的模板引擎,他的這個語法更簡單。而且呢,功能更強大。
引入Thymeleaf
1.匯入依賴
<!-- 引入thymeleaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
2.編寫controller
@Controller
public class ThymeleafController {
@RequestMapping("thymeleaf/test1")
public String test1(Model model){
model.addAttribute("msg","hello thymeleaf");
return "test1";
}
}
3.在html頁面引入thymeleaf 名稱空間
<html lang="en" xmlns:th="http://www.thymeleaf.org">
4.編寫html頁面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>test1</h1>
<!--取出值-->
<p th:text="${msg}"></p>
</body>
</html>
5.測試
http://localhost:8080/thymeleaf/test1
Thymeleaf語法規則
普通字串
@RequestMapping("thymeleaf/test1")
public String test1(Model model){
model.addAttribute("msg","hello thymeleaf");
return "test1";
}
<p th:if="${msg == null}">
沒有資料
</p>
<p th:if="${msg != null}">
<p th:text="${msg}"></p>
</p>
獲取物件
@RequestMapping("thymeleaf/test4")
public String test4(Model model){
model.addAttribute("user",new User(1,"閒言",18));
return "test1";
}
<p th:if="${user != null}">
<p th:text="${user.id}"></p>
<p th:text="${user.name}"></p>
<p th:text="${user.age}"></p>
</p>
遍歷 list
@RequestMapping("thymeleaf/test2")
public String test2(Model model){
List<String> lists = new ArrayList<>();
lists.add("Jsp");
lists.add("SpringBoot");
lists.add("Spring");
lists.add("JavaSE");
model.addAttribute("lists",lists);
return "test1";
}
<table border="1">
<tr>
<th>Name</th>
</tr>
<tr th:each="list : ${lists}">
<td th:text="${list}"></td>
</tr>
</table>
遍歷map
@RequestMapping("thymeleaf/test3")
public String test3(Model model){
Map<String,Object> maps = new HashMap<>();
maps.put("閒言","csdn閒言");
maps.put("k2","v2");
maps.put("k3","v3");
maps.put("k4","v4");
model.addAttribute("maps",maps);
return "test1";
}
<table class="layui-table">
<tr th:each="item,map:${maps}">
<td th:text="${map.index}+1"></td>
<td th:text="${map.current.key}"></td><!-- key-->
<td th:text="${map.current.value}"></td><!-- value-->
</tr>
</table>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
在模板引擎引入css樣式 @{/css/bootstrap.min.css}
<link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
引入圖片樣式 @{/img/bootstrap-solid.svg}
<img class="mb-4" th:src="@{/img/bootstrap-solid.svg}" alt="" width="72" height="72">
國際化引入 #{login.tip}
<h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">Please sign in</h1>
設定國際化,因為button 標籤比較特殊 不能通過 th:value="${}" 設定
需要使用[[ #{ }]]
<button class="btn btn-lg btn-primary btn-block" type="submit" >[[#{login.btn}]]</button>
<input type="text" class="form-control" th:placeholder="#{login.username}" required="" autofocus="">
<label class="sr-only">Password</label>
<input type="password" class="form-control" th:placeholder="#{login.password}" required="">
thymeleaf 提取公共頁面
th:fragment=「sidebar」
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<!--頭部導航欄-->
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0" th:fragment="navbar">
<a class="navbar-brand col-sm-3 col-md-2 mr-0" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">[[${session.loginUser}]]</a>
<input class="form-control form-control-dark w-100" type="text" placeholder="Search" aria-label="Search">
<ul class="navbar-nav px-3">
<li class="nav-item text-nowrap">
<a class="nav-link" href="#">登出</a>
</li>
</ul>
</nav>
<!--側邊欄-->
<!--th:fragment="sidebar" 將責編了抽取出來,名稱為 sidebar -->
<nav class="col-md-2 d-none d-md-block bg-light sidebar" th:fragment="sidebar">
<div class="sidebar-sticky">
<ul class="nav flex-column">
<li class="nav-item">
<a class="nav-link active" th:href="@{/index}">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-home">
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path>
<polyline points="9 22 9 12 15 12 15 22"></polyline>
</svg>
首頁<span class="sr-only">(current)</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" th:href="@{/employees/list}">
員工管理
</a>
</li>
</ul>
</div>
</nav>
</html>
使用公共頁面
th:replace="~{commons/commons.html::navbar}
<div th:replace="~{commons/commons.html::navbar}"></div>
1.設定i18n 檔案
前臺頁面使用
2.如果需要在專案中進行按鈕自動切換,需要自定義一個元件,並將其注入Spring容器
public class MyLocalResolver implements LocaleResolver {
/**
* 解析請求
* @param request
* @return
*/
@Override
public Locale resolveLocale(HttpServletRequest request) {
//獲取請求中的語言引數
String language = request.getParameter("l");
//如果沒有就使用預設的
Locale locale = Locale.getDefault();
if (!StringUtils.isEmpty(language)){
String[] split = language.split("_");
//國家-地區
locale = new Locale(split[0], split[1]);
}
return locale;
}
@Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
}
}
/**
* 自定義的國際化元件
* @return
*/
@Bean
public LocaleResolver localeResolver(){
return new MyLocalResolver();
}
步處理還是非常常用的,比如我們在網站上傳送郵件,後臺會去傳送郵件,此時前臺會造成響應不動,直到郵件傳送完畢,響應才會成功,所以我們一般會採用多執行緒的方式去處理這些任務。
編寫方法,假裝正在處理資料,使用執行緒設定一些延時,模擬同步等待的情況;
@EnableAsync
開啟非同步註解功能@Async
告訴spring 這是一個非同步方法
啟動類
@EnableAsync//開啟非同步註解功能
@SpringBootApplication
public class Demo6TestApplication {
public static void main(String[] args) {
SpringApplication.run(Demo6TestApplication.class, args);
}
}
service層
SpringBoot就會自己開一個執行緒池,進行呼叫!但是要讓這個註解生效,還需要在主程式上新增一個註解@EnableAsync
,開啟非同步註解功能;
@Service
public class AsyncService {
//告訴spring 這是一個非同步方法
@Async
public void hello(){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("資料正在處理");
}
}
controller層
@RestController
public class AsyncController {
@Autowired
private AsyncService asyncService;
/**
* hello 請求
* @return
*/
@RequestMapping("/hello")
public String hello(){
asyncService.hello();//停止3秒
return "OK";
}
}
郵件傳送,在我們的日常開發中,也非常的多,Springboot也幫我們做了支援
spring-boot-start-mail
MailSenderAutoConfiguration
MailProperties
內容,設定在application.yml
中JavaMailSender
1.匯入依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
看它引入的依賴,可以看到 jakarta.mail
2、檢視自動設定類:MailSenderAutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ MimeMessage.class, MimeType.class, MailSender.class })
@ConditionalOnMissingBean(MailSender.class)
@Conditional(MailSenderCondition.class)
@EnableConfigurationProperties(MailProperties.class)
@Import({ MailSenderJndiConfiguration.class, MailSenderPropertiesConfiguration.class })
public class MailSenderAutoConfiguration {
/**
* Condition to trigger the creation of a {@link MailSender}. This kicks in if either
* the host or jndi name property is set.
*/
static class MailSenderCondition extends AnyNestedCondition {
MailSenderCondition() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}
@ConditionalOnProperty(prefix = "spring.mail", name = "host")
static class HostProperty {
}
@ConditionalOnProperty(prefix = "spring.mail", name = "jndi-name")
static class JndiNameProperty {
}
}
}
這個類沒有註冊bean,看它匯入的bean
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Session.class)
@ConditionalOnProperty(prefix = "spring.mail", name = "jndi-name")
@ConditionalOnJndi
class MailSenderJndiConfiguration {
private final MailProperties properties;
MailSenderJndiConfiguration(MailProperties properties) {
this.properties = properties;
}
@Bean
JavaMailSenderImpl mailSender(Session session) {
JavaMailSenderImpl sender = new JavaMailSenderImpl();
sender.setDefaultEncoding(this.properties.getDefaultEncoding().name());
sender.setSession(session);
return sender;
}
@Bean
@ConditionalOnMissingBean
Session session() {
String jndiName = this.properties.getJndiName();
try {
return JndiLocatorDelegate.createDefaultResourceRefLocator().lookup(jndiName, Session.class);
}
catch (NamingException ex) {
throw new IllegalStateException(String.format("Unable to find Session in JNDI location %s", jndiName), ex);
}
}
}
看下組態檔
spring.mail.username=xxx
spring.mail.password=xxxx
spring.mail.host=smtp.qq.com
# 開啟加密(qq獨有)
spring.mail.properties.smtp.ssl.enable=true
獲取授權碼:在QQ郵箱中的設定->賬戶->開啟pop3和smtp服務
4.測試
package cn.bloghut;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import javax.mail.internet.MimeMessage;
import java.io.File;
@SpringBootTest
class Demo6TestApplicationTests {
@Autowired
private JavaMailSenderImpl mailSender;
/**
* 簡單的郵件傳送
*/
@Test
void contextLoads() {
SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
simpleMailMessage.setSubject("閒言你好呀~");
simpleMailMessage.setText("今天天氣真好");
simpleMailMessage.setTo("1765736057@qq.com");
simpleMailMessage.setFrom("1765736057@qq.com");
mailSender.send(simpleMailMessage);
}
/**
* 複雜郵件傳送
*/
@Test
public void test2() throws Exception{
MimeMessage mimeMessage = mailSender.createMimeMessage();
//組裝
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,true);
//正文
helper.setSubject("閒言你好呀~plus");
helper.setText("<p style='color:red'>你要加油啊!</p>");
//附件
helper.addAttachment("1.png",new File("I:/1.png"));
helper.addAttachment("2.png",new File("I:/1.png"));
helper.setTo("1765736057@qq.com");
helper.setFrom("1765736057@qq.com");
mailSender.send(mimeMessage);
}
}
Spring為我們提供了非同步執行任務排程的方式,提供了兩個介面
TaskExecutor
介面 任務執行者TaskScheduler
介面 任務排程者兩個註解:
EnableScheduling
開啟定時功能的註解Scheduled
表示什麼時候執行cron
表示式
1、建立一個ScheduledService
@Service
public class ScheduledService {
//在一個特定的時間執行這個方法_Timer
//cron表示式
/**
* 30 15 10 * * ? 每天10點15分30秒 執行一次
*
*/
//秒 分 時 日 月 周
@Scheduled(cron = "0 * 22 * * ?")
public void hello(){
System.out.println("你被執行了"+new Date());
}
}
2、這裡寫完定時任務之後,我們需要在啟動類上增加@EnableScheduling 開啟定時任務功能
@EnableScheduling//開啟定時任務
@EnableAsync//開啟非同步註解功能
@SpringBootApplication
public class Demo6TestApplication {
public static void main(String[] args) {
SpringApplication.run(Demo6TestApplication.class, args);
}
}
常用表示式
(1)0/2 * * * * ? 表示每2秒 執行任務
(1)0 0/2 * * * ? 表示每2分鐘 執行任務
(1)0 0 2 1 * ? 表示在每月的1日的凌晨2點調整任務
(2)0 15 10 ? * MON-FRI 表示週一到週五每天上午10:15執行作業
(3)0 15 10 ? 6L 2002-2006 表示2002-2006年的每個月的最後一個星期五上午10:15執行作
(4)0 0 10,14,16 * * ? 每天上午10點,下午2點,4點
(5)0 0/30 9-17 * * ? 朝九晚五工作時間內每半小時
(6)0 0 12 ? * WED 表示每個星期三中午12點
(7)0 0 12 * * ? 每天中午12點觸發
(8)0 15 10 ? * * 每天上午10:15觸發
(9)0 15 10 * * ? 每天上午10:15觸發
(10)0 15 10 * * ? 每天上午10:15觸發
(11)0 15 10 * * ? 2005 2005年的每天上午10:15觸發
(12)0 * 14 * * ? 在每天下午2點到下午2:59期間的每1分鐘觸發
(13)0 0/5 14 * * ? 在每天下午2點到下午2:55期間的每5分鐘觸發
(14)0 0/5 14,18 * * ? 在每天下午2點到2:55期間和下午6點到6:55期間的每5分鐘觸發
(15)0 0-5 14 * * ? 在每天下午2點到下午2:05期間的每1分鐘觸發
(16)0 10,44 14 ? 3 WED 每年三月的星期三的下午2:10和2:44觸發
(17)0 15 10 ? * MON-FRI 週一至週五的上午10:15觸發
(18)0 15 10 15 * ? 每月15日上午10:15觸發
(19)0 15 10 L * ? 每月最後一日的上午10:15觸發
(20)0 15 10 ? * 6L 每月的最後一個星期五上午10:15觸發
(21)0 15 10 ? * 6L 2002-2005 2002年至2005年的每月的最後一個星期五上午10:15觸發
(22)0 15 10 ? * 6#3 每月的第三個星期五上午10:15觸發
詳細瞭解下cron
表示式;
https://www.bejson.com/othertools/cron/
在《分散式系統原理與範型》一書中有如下定義:「分散式系統是若干獨立計算機的集合
,這些計算機對於使用者來說就像單個相關係統
」;
分散式系統是由一組通過網路進行通訊
、為了完成共同的任務而協調工作的計算機節點組成的系統。分散式系統的出現是為了用廉價的、普通的機器完成單個計算機無法完成的計算、儲存任務。其目的是利用更多的機器,處理更多的資料
。
分散式系統(distributed system)是建立在網路之上
的軟體系統。
首先需要明確的是,只有當單個節點的處理能力無法滿足日益增長的計算、儲存任務的時候,且硬體的提升(加記憶體、加磁碟、使用更好的CPU)高昂到得不償失的時候,應用程式也不能進一步優化的時候,我們才需要考慮分散式系統。因為,分散式系統要解決的問題本身就是和單機系統一樣的,而由於分散式系統多節點、通過網路通訊的拓撲結構,會引入很多單機系統沒有的問題,為了解決這些問題又會引入更多的機制、協定,帶來更多的問題。。。
Dubbo檔案
隨著網際網路的發展,網站應用的規模不斷擴大,常規的垂直應用架構已無法應對,分散式服務架構以及流動計算架構勢在必行,急需一個治理系統
確保架構有條不紊的演進。
在Dubbo的官網檔案有這樣一張圖
單一應用架構
當網站流量很小時,只需一個應用,將所有功能都部署在一起,以減少部署節點和成本。此時,用於簡化增刪改查工作量的資料存取框架(ORM)是關鍵。
適用於小型網站,小型管理系統,將所有功能都部署到一個功能裡,簡單易用。
缺點:
1、效能擴充套件比較難
2、協同開發問題
3、不利於升級維護
垂直應用架構
當存取量逐漸增大,單一應用增加機器帶來的加速度越來越小,將應用拆成互不相干的幾個應用,以提升效率。此時,用於加速前端頁面開發的Web框架(MVC)是關鍵。
通過切分業務來實現各個模組獨立部署,降低了維護和部署的難度,團隊各司其職更易管理,效能擴充套件也更方便,更有針對性。
缺點:公用模組無法重複利用,開發性的浪費
分散式服務架構
當垂直應用越來越多,應用之間互動不可避免,將核心業務抽取出來,作為獨立的服務
,逐漸形成穩定的服務中心,使前端應用能更快速的響應多變的市場需求。此時,用於提高業務複用及整合的分散式服務架構(RPC)
是關鍵。
流動計算架構
當服務越來越多,容量的評估,小服務資源的浪費等問題逐漸顯現,此時需增加一個排程中心基於存取壓力實時管理叢集容量,提高叢集利用率。此時,用於提高機器利用率的資源排程和治理中心(SOA)[ Service Oriented Architecture]是關鍵。
http
: 通訊協定
rpc
:通訊協定
RPC【Remote Procedure Call】是指遠端過程呼叫,是一種程序間通訊方式,他是一種技術的思想
,而不是規範。它允許程式呼叫另一個地址空間(通常是共用網路的另一臺機器上)的過程或函數,而不用程式設計師顯式編碼這個遠端呼叫的細節。即程式設計師無論是呼叫原生的還是遠端的函數,本質上編寫的呼叫程式碼基本相同。
也就是說兩臺伺服器A,B,一個應用部署在A伺服器上,想要呼叫B伺服器上應用提供的函數/方法,由於不在一個記憶體空間,不能直接呼叫,需要通過網路來表達呼叫的語意和傳達呼叫的資料。為什麼要用RPC呢?就是無法在一個程序內,甚至一個計算機內通過本地呼叫的方式完成的需求,比如不同的系統間的通訊,甚至不同的組織間的通訊,由於計算能力需要橫向擴充套件,需要在多臺機器組成的叢集上部署應用。RPC就是要像呼叫原生的函數一樣去調遠端函數;
RPC基本原理
步驟解析:
RPC兩個核心模組:通訊,序列化。
是一個Jar包
Apache Dubbo 是一款高效能、輕量級的開源Java RPC框架,它提供了三大核心能力:面向介面的遠端方法呼叫,智慧容錯和負載均衡,以及服務自動註冊和發現。
dubbo官網 http://dubbo.apache.org/zh-cn/index.html
1.瞭解Dubbo的特性
2.檢視官方檔案
dubbo基本概念
專業的事,交給專業的人來做~不靠譜!
服務提供者(Provider)
:暴露服務的服務提供方,服務提供者在啟動時,向註冊中心註冊自己提供的服務。
服務消費者(Consumer)
:呼叫遠端服務的服務消費方,服務消費者在啟動時,向註冊中心訂閱自己所需的服務,服務消費者,從提供者地址列表中,基於軟負載均衡演演算法,選一臺提供者進行呼叫,如果呼叫失敗,再選另一臺呼叫。
註冊中心(Registry)
:註冊中心返回服務提供者地址列表給消費者,如果有變更,註冊中心將基於長連線推播變更資料給消費者
監控中心(Monitor)
:服務消費者和提供者,在記憶體中累計呼叫次數和呼叫時間,定時每分鐘傳送一次統計資料到監控中心
呼叫關係說明
啟動
,載入
,執行
服務提供者。提供者
在啟動時,向註冊中心註冊自己提供的服務
。消費者
在啟動時,向註冊中心訂閱自己所需的服務
。產生背景
當今是個分散式、叢集、雲端計算等名詞滿天飛的時代。造成這種局面的一個重要因素就是,單一機器的處理能力已經不能滿足我們的需求,不得不採用由多臺機器組成的服務叢集。服務叢集對外提供服務的過程中,可以分解處理壓力,在一定程度上打破效能瓶頸,並提高服務的可用性(不會因為一臺機器宕機而造成服務不可用)。
上圖中有三臺機器,每臺機器跑同樣的一個應用程式。然後我們將這三臺機器通過網路將其連線起來,構成一個系統來為使用者提供服務,對使用者來說這個系統的架構是透明的,他感覺不到這個系統是一個什麼樣的架構。那麼我們就可以把這種系統稱作一個分散式系統。
那麼,問題來了:
1.程式的執行往往依賴很多組態檔,比如資料庫地址、黑名單控制、服務地址列表等,而且有些設定資訊需要頻繁地進行動態變更,這時候怎麼保證所有機器共用的設定資訊保持一致?
2.如果有一臺機器掛掉了,其他機器如何感知到這一變化並接管任務?如果使用者激增,需要增加機器來緩解壓力,如何做到不重新啟動叢集而完成機器的新增?
3.使用者數量增加或者減少,會出現有的機器資源使用率繁忙,有的卻空閒,如何讓每臺機器感知到其他機器的負載狀態從而實現負載均衡?
4.在一臺機器上要多個程序或者多個執行緒操作同一資源比較簡單,因為可以有大量的狀態資訊或者紀錄檔資訊提供保證,比如兩個A和B程序同時寫一個檔案,加鎖就可以實現。但是分散式系統怎麼辦?需要一個三方的分配鎖的機制,幾百臺worker都對同一個網路中的檔案寫操作,怎麼協同?還有怎麼保證高效的執行?
除了上面列舉的幾種,還有很多細思極恐的問題,分散式系統到底有多然人抓狂,可以想想你第一次接觸多執行緒的感覺;
計劃中的多執行緒
現實中的多執行緒
分散式系統可以看作多執行緒的N級加強版……
ZooKeeper的前世今生
分散式系統的很多難題,都是由於缺少協調機制造成的。
目前,在分散式協調技術方面做得比較好的就是Google的Chubby還有Apache的ZooKeeper。有人會問既然有了Chubby為什麼還要弄一個ZooKeeper,難道Chubby做得不夠好嗎?主要是Chubby是非開源的,Google自家用。後來雅虎模仿Chubby開發出了ZooKeeper
,也實現了類似的分散式鎖的功能,並且將ZooKeeper作為一種開源的程式捐獻給了Apache,那麼這樣就可以使用ZooKeeper所提供鎖服務。而且在分散式領域久經考驗,它的可靠性,可用性都是經過理論和實踐的驗證的。
至於這個神器為什麼叫ZooKeeper,與外國人一貫的幽默精神有關。
眾所周知,外國人喜歡給用一個動物作為吉祥物,在IT界也不例外。比如,負責巨量資料工作的Hadoop是一個黃色的大象;負責資料倉儲的Hive是一個虛擬蜂巢;負責資料分析的Apache Pig是一頭聰明的豬;負責管理web容器的tomcat是一隻雄貓……那好,負責分散式協調工作的角色就叫ZooKeeper(動物園飼養員)吧。
ZooKeeper能幹什麼
官方說辭是:
ZooKeeper 分散式服務架構是Apache Hadoop 的一個子專案,它主要是用來解決分散式應用中經常遇到的一些資料管理問題
,如:統一命名服務、狀態同步服務、叢集管理、分散式應用設定項的管理等。簡化分散式應用協調及其管理的難度,提供高效能的分散式服務。ZooKeeper的目標就是封裝好複雜 易出錯的關鍵服務,將簡單易用的介面和效能高效、功能穩定的系統提供給使用者。
ZooKeeper在一致性、可用性、容錯性的保證,也是ZooKeeper的成功之處,它獲得的一切成功都與它採用的協定——Zab協定是密不可分的。
為了實現前面提到的各種服務,比如分散式鎖、設定維護、組服務等,ZooKeeper設計了一種新的資料結構——Znode,然後在該資料結構的基礎上定義了一些原語,也就是一些關於該資料結構的一些操作。有了這些資料結構和原語還不夠,因為ZooKeeper工作在分散式環境下,服務是通過訊息以網路的形式傳送給分散式應用程式,所以還需要一個通知機制——Watcher機制。總結一下,ZooKeeper所提供的服務主要是通過:資料結構 + 原語 + watcher機制,三個部分來實現的。
是一個監控管理後臺~檢視我們註冊了哪些服務,哪些服務被消費了
dubbo本身並不是一個服務軟體。它其實就是一個jar包
,能夠幫你的java程式連線到zookeeper,並利用zookeeper消費、提供服務。
但是為了讓使用者更好的管理監控眾多的dubbo服務,官方提供了一個視覺化的監控程式dubbo-admin,不過這個監控即使不裝也不影響使用。
這裡來安裝一下:
下載dubbo-admin
地址 :https://github.com/apache/dubbo-admin/tree/master
解壓進入目錄
修改 dubbo-admin\src\main\resources \application.properties 指定zookeeper地址
在專案目錄下打包dubbo-admin
清除並打包
mvn clean package -Dmaven.test.skip=true
打包完成
啟動jar 包
記得啟動zookeeper
存取
http://localhost:7001/
使用者名稱和密碼都是root