精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

零基礎(chǔ)都秒懂:手把手教你搭建一套微服務(wù)框架!

開(kāi)發(fā)
本文將以原理+實(shí)戰(zhàn)的方式,首先對(duì)“微服務(wù)”相關(guān)的概念進(jìn)行知識(shí)點(diǎn)掃盲,然后開(kāi)始手把手教你搭建一整套微服務(wù)系統(tǒng)。

這套微服務(wù)框架能干啥?

這套系統(tǒng)搭建完之后,可以實(shí)現(xiàn):

  • 微服務(wù)架構(gòu),你的整個(gè)應(yīng)用程序?qū)?huì)被拆分成一個(gè)個(gè)功能獨(dú)立的子系統(tǒng),獨(dú)立運(yùn)行,系統(tǒng)與系統(tǒng)之間通過(guò) RPC 接口通信。

這樣系統(tǒng)之間的耦合度大大降低,你的系統(tǒng)將非常容易擴(kuò)展,團(tuán)隊(duì)協(xié)作效率提升了 N 個(gè)檔次。

這種架構(gòu)通過(guò)眼下流行的 Spring Boot 和阿里巴巴吊 Dubbo 框架來(lái)實(shí)現(xiàn)。

  • 容器化部署,你的各個(gè)微服務(wù)將采用目前處于浪潮之巔的 Docker 來(lái)實(shí)現(xiàn)容器化部署,避免一切因環(huán)境引起的各種問(wèn)題,讓你們團(tuán)隊(duì)的全部精力集中在業(yè)務(wù)開(kāi)發(fā)上。
  • 自動(dòng)化構(gòu)建,項(xiàng)目被微服務(wù)化后,各個(gè)服務(wù)之間的關(guān)系錯(cuò)綜復(fù)雜,打包構(gòu)建的工作量相當(dāng)可怕。不過(guò)沒(méi)關(guān)系,本文將借助 Jenkins,幫助你一鍵自動(dòng)化部署,從此你便告別了加班。

知識(shí)點(diǎn)掃盲篇

咳咳,敲黑板啦!筆記趕緊記起來(lái),課后我要檢查的!檢查不合格的同學(xué)放學(xué)后留下來(lái)!

知識(shí)點(diǎn) 1:微服務(wù)

微服務(wù)一詞近幾年相當(dāng)火,成為程序猿裝逼熱門(mén)詞匯,你不對(duì)它有所了解如何在程序猿裝逼圈子里混?下面我用最為通俗易懂的語(yǔ)言介紹它。要講清楚微服務(wù),我先要從一個(gè)系統(tǒng)架構(gòu)的演進(jìn)過(guò)程講起。

單機(jī)結(jié)構(gòu)

我想大家最最最熟悉的就是單機(jī)結(jié)構(gòu),一個(gè)系統(tǒng)業(yè)務(wù)量很小的時(shí)候所有的代碼都放在一個(gè)項(xiàng)目中,然后這個(gè)項(xiàng)目部署在一臺(tái)服務(wù)器上就好了。整個(gè)項(xiàng)目所有的服務(wù)都由這臺(tái)服務(wù)器提供。這就是單機(jī)結(jié)構(gòu)。

那么,單機(jī)結(jié)構(gòu)有啥缺點(diǎn)呢?單機(jī)的處理能力畢竟有限,當(dāng)你的業(yè)務(wù)增長(zhǎng)到一定程度的時(shí)候,單機(jī)的硬件資源將無(wú)法滿足你的業(yè)務(wù)需求。此時(shí)便出現(xiàn)了集群模式。

集群結(jié)構(gòu)

集群模式在程序猿界由各種裝逼解釋?zhuān)械淖屇愀緹o(wú)法理解,其實(shí)就是一個(gè)很簡(jiǎn)單的玩意兒,且聽(tīng)我一一道來(lái)。

單機(jī)處理到達(dá)瓶頸的時(shí)候,你就把單機(jī)復(fù)制幾份,這樣就構(gòu)成了一個(gè)“集群”。集群中每臺(tái)服務(wù)器就叫做這個(gè)集群的一個(gè)“節(jié)點(diǎn)”,所有節(jié)點(diǎn)構(gòu)成了一個(gè)集群。

每個(gè)節(jié)點(diǎn)都提供相同的服務(wù),那么這樣系統(tǒng)的處理能力就相當(dāng)于提升了好幾倍(有幾個(gè)節(jié)點(diǎn)就相當(dāng)于提升了這么多倍)。

但問(wèn)題是用戶(hù)的請(qǐng)求究竟由哪個(gè)節(jié)點(diǎn)來(lái)處理呢?最好能夠讓此時(shí)此刻負(fù)載較小的節(jié)點(diǎn)來(lái)處理,這樣使得每個(gè)節(jié)點(diǎn)的壓力都比較平均。

要實(shí)現(xiàn)這個(gè)功能,就需要在所有節(jié)點(diǎn)之前增加一個(gè)“調(diào)度者”的角色,用戶(hù)的所有請(qǐng)求都先交給它,然后它根據(jù)當(dāng)前所有節(jié)點(diǎn)的負(fù)載情況,決定將這個(gè)請(qǐng)求交給哪個(gè)節(jié)點(diǎn)處理。這個(gè)“調(diào)度者”有個(gè)牛逼的名字——負(fù)載均衡服務(wù)器。

集群結(jié)構(gòu)的好處就是系統(tǒng)擴(kuò)展非常容易。隨著你們系統(tǒng)業(yè)務(wù)的發(fā)展,當(dāng)前的系統(tǒng)又支撐不住了,那么給這個(gè)集群再增加節(jié)點(diǎn)就行了。

但是,當(dāng)你的業(yè)務(wù)發(fā)展到一定程度的時(shí)候,你會(huì)發(fā)現(xiàn)一個(gè)問(wèn)題——無(wú)論怎么增加節(jié)點(diǎn),貌似整個(gè)集群性能的提升效果并不明顯了。這時(shí)候,你就需要使用微服務(wù)結(jié)構(gòu)了。

微服務(wù)結(jié)構(gòu)

先來(lái)對(duì)前面的知識(shí)點(diǎn)做個(gè)總結(jié)。從單機(jī)結(jié)構(gòu)到集群結(jié)構(gòu),你的代碼基本無(wú)需要作任何修改,你要做的僅僅是多部署幾臺(tái)服務(wù)器,沒(méi)臺(tái)服務(wù)器上運(yùn)行相同的代碼就行了。

但是,當(dāng)你要從集群結(jié)構(gòu)演進(jìn)到微服務(wù)結(jié)構(gòu)的時(shí)候,之前的那套代碼就需要發(fā)生較大的改動(dòng)了。

所以對(duì)于新系統(tǒng)我們建議,系統(tǒng)設(shè)計(jì)之初就采用微服務(wù)架構(gòu),這樣后期運(yùn)維的成本更低。

但如果一套老系統(tǒng)需要升級(jí)成微服務(wù)結(jié)構(gòu)的話,那就得對(duì)代碼大動(dòng)干戈了。所以,對(duì)于老系統(tǒng)而言,究竟是繼續(xù)保持集群模式,還是升級(jí)成微服務(wù)架構(gòu),這需要你們的架構(gòu)師深思熟慮、權(quán)衡投入產(chǎn)出比。

下面開(kāi)始介紹所謂的微服務(wù)。 

微服務(wù)就是將一個(gè)完整的系統(tǒng),按照業(yè)務(wù)功能,拆分成一個(gè)個(gè)獨(dú)立的子系統(tǒng),在微服務(wù)結(jié)構(gòu)中,每個(gè)子系統(tǒng)就被稱(chēng)為“服務(wù)”。這些子系統(tǒng)能夠獨(dú)立運(yùn)行在 Web 容器中,它們之間通過(guò) RPC 方式通信。

舉個(gè)例子,假設(shè)需要開(kāi)發(fā)一個(gè)在線商城。按照微服務(wù)的思想,我們需要按照功能模塊拆分成多個(gè)獨(dú)立的服務(wù),如:用戶(hù)服務(wù)、產(chǎn)品服務(wù)、訂單服務(wù)、后臺(tái)管理服務(wù)、數(shù)據(jù)分析服務(wù)等等。

這一個(gè)個(gè)服務(wù)都是一個(gè)個(gè)獨(dú)立的項(xiàng)目,可以獨(dú)立運(yùn)行。如果服務(wù)之間有依賴(lài)關(guān)系,那么通過(guò) RPC 方式調(diào)用。

這樣的好處有很多:

  • 系統(tǒng)之間的耦合度大大降低,可以獨(dú)立開(kāi)發(fā)、獨(dú)立部署、獨(dú)立測(cè)試,系統(tǒng)與系統(tǒng)之間的邊界非常明確,排錯(cuò)也變得相當(dāng)容易,開(kāi)發(fā)效率大大提升。
  • 系統(tǒng)之間的耦合度降低,從而系統(tǒng)更易于擴(kuò)展。我們可以針對(duì)性地?cái)U(kuò)展某些服務(wù)。假設(shè)這個(gè)商城要搞一次大促,下單量可能會(huì)大大提升,因此我們可以針對(duì)性地提升訂單系統(tǒng)、產(chǎn)品系統(tǒng)的節(jié)點(diǎn)數(shù)量,而對(duì)于后臺(tái)管理系統(tǒng)、數(shù)據(jù)分析系統(tǒng)而言,節(jié)點(diǎn)數(shù)量維持原有水平即可。
  • 服務(wù)的復(fù)用性更高。比如,當(dāng)我們將用戶(hù)系統(tǒng)作為單獨(dú)的服務(wù)后,該公司所有的產(chǎn)品都可以使用該系統(tǒng)作為用戶(hù)系統(tǒng),無(wú)需重復(fù)開(kāi)發(fā)。

那么問(wèn)題來(lái)了,當(dāng)采用微服務(wù)結(jié)構(gòu)后,一個(gè)完整的系統(tǒng)可能由很多獨(dú)立的子系統(tǒng)組成,當(dāng)業(yè)務(wù)量漸漸發(fā)展起來(lái)之后,這些子系統(tǒng)之間的關(guān)系將錯(cuò)綜復(fù)雜。

而且為了能夠針對(duì)性地增加某些服務(wù)的處理能力,某些服務(wù)的背后可能是一個(gè)集群模式,由多個(gè)節(jié)點(diǎn)構(gòu)成,這無(wú)疑大大增加了運(yùn)維的難度。

微服務(wù)的想法好是好,但開(kāi)發(fā)、運(yùn)維的復(fù)雜度實(shí)在是太高。為了解決這些問(wèn)題,阿里巴巴的 Dubbo 就橫空出世了。

知識(shí)點(diǎn) 2:Dubbo

Dubbo 是一套微服務(wù)系統(tǒng)的協(xié)調(diào)者,在它這套體系中,一共有三種角色:

  • 服務(wù)提供者(下面簡(jiǎn)稱(chēng)提供者)
  • 服務(wù)消費(fèi)者(下面簡(jiǎn)稱(chēng)消費(fèi)者)
  • 注冊(cè)中心

你在使用的時(shí)候需要將 Dubbo 的 jar 包引入到你的項(xiàng)目中,也就是每個(gè)服務(wù)都要引入 Dubbo 的 jar 包。

然后當(dāng)這些服務(wù)初始化的時(shí)候,Dubbo 就會(huì)將當(dāng)前系統(tǒng)需要發(fā)布的服務(wù)、以及當(dāng)前系統(tǒng)的 IP 和端口號(hào)發(fā)送給注冊(cè)中心,注冊(cè)中心將其記錄下來(lái)。這就是服務(wù)發(fā)布的過(guò)程。

與此同時(shí),也是在系統(tǒng)初始化的時(shí)候,Dubbo 還會(huì)掃描一下當(dāng)前系統(tǒng)所需要引用的服務(wù),然后向注冊(cè)中心請(qǐng)求這些服務(wù)所在的 IP 和端口號(hào)。

接下來(lái)系統(tǒng)就可以正常運(yùn)行了。當(dāng)系統(tǒng) A 需要調(diào)用系統(tǒng) B 的服務(wù)的時(shí)候,A 就會(huì)與 B 建立起一條 RPC 信道,然后再調(diào)用 B 系統(tǒng)上相應(yīng)的服務(wù)。

這,就是 Dubbo 的作用。

知識(shí)點(diǎn) 3:容器化部署

當(dāng)我們使用了微服務(wù)架構(gòu)后,我們將一個(gè)原本完整的系統(tǒng),按照業(yè)務(wù)邏輯拆分成一個(gè)個(gè)可獨(dú)立運(yùn)行的子系統(tǒng)。

為了降低系統(tǒng)間的耦合度,我們希望這些子系統(tǒng)能夠運(yùn)行在獨(dú)立的環(huán)境中,這些環(huán)境之間能夠相互隔離。

在 Docker 出現(xiàn)之前,若使用虛擬機(jī)來(lái)實(shí)現(xiàn)運(yùn)行環(huán)境的相互隔離的話成本較高,虛擬機(jī)會(huì)消耗較多的計(jì)算機(jī)硬件/軟件資源。

Docker 不僅能夠?qū)崿F(xiàn)運(yùn)行環(huán)境的隔離,而且能極大程度的節(jié)約計(jì)算機(jī)資源,它成為一種輕量級(jí)的“虛擬機(jī)”。

知識(shí)點(diǎn) 4:自動(dòng)化構(gòu)建

當(dāng)我們使用微服務(wù)架構(gòu)后,隨著業(yè)務(wù)的逐漸發(fā)展,系統(tǒng)之間的依賴(lài)關(guān)系會(huì)日益復(fù)雜,而且各個(gè)模塊的構(gòu)建順序都有所講究。

對(duì)于一個(gè)小型系統(tǒng)來(lái)說(shuō),也許只有幾個(gè)模塊,那么你每次采用人肉構(gòu)建的方式也許并不感覺(jué)麻煩。

但隨著系統(tǒng)業(yè)務(wù)的發(fā)展,你的系統(tǒng)之間的依賴(lài)關(guān)系日益復(fù)雜,子系統(tǒng)也逐漸增多,每次構(gòu)建一下你都要非常小心謹(jǐn)慎,稍有不慎整個(gè)服務(wù)都無(wú)法正常啟動(dòng)。

而且這些構(gòu)建的工作很 Low,卻需要消耗大量的精力,這無(wú)疑降低了開(kāi)發(fā)的效率。不過(guò)沒(méi)關(guān)系,Jenkins 就是來(lái)幫助你解決這個(gè)問(wèn)題的。

我們只需在 Jenkins 中配置好代碼倉(cāng)庫(kù)、各個(gè)模塊的構(gòu)建順序和構(gòu)建命令,在以后的構(gòu)建中,只需要點(diǎn)擊“立即構(gòu)建”按鈕,Jenkins 就會(huì)自動(dòng)到你的代碼倉(cāng)庫(kù)中拉取最新的代碼,然后根據(jù)你事先配置的構(gòu)建命令進(jìn)行構(gòu)建,最后發(fā)布到指定的容器中運(yùn)行。

你也可以讓 Jenkins 定時(shí)檢查代碼倉(cāng)庫(kù)版本的變化,一旦發(fā)現(xiàn)變動(dòng)就自動(dòng)地開(kāi)始構(gòu)建過(guò)程,并且讓 Jenkins 在構(gòu)建成功后給你發(fā)一封郵件。這樣你連“立即構(gòu)建”的按鈕也不需要按,就能全自動(dòng)地完成這一切構(gòu)建過(guò)程。

實(shí)戰(zhàn)動(dòng)手篇

學(xué)習(xí)目標(biāo)

接下來(lái)我會(huì)帶著大家,以一個(gè)在線商城為例,搭建一套能夠自動(dòng)化部署的微服務(wù)框架。

這個(gè)框架能做如下幾件事情:

  • 基于 Spring Boot 快速開(kāi)發(fā),我們將選擇目前熱度很高的 Spring Boot,最大限度地降低配置復(fù)雜度,把大量的精力投入到我們的業(yè)務(wù)開(kāi)發(fā)中來(lái)。
  • 基于 Dubbo 的微服務(wù)化,我們會(huì)使用阿里巴巴的開(kāi)源框架 Dubbo,將我們的系統(tǒng)拆分成多個(gè)獨(dú)立的微服務(wù),然后用 Dubbo 來(lái)管理所有服務(wù)的發(fā)布和引用。
  • 有了 Dubbo 之后,調(diào)用遠(yuǎn)程服務(wù)就像調(diào)用一個(gè)本地函數(shù)一樣簡(jiǎn)單,Dubbo 會(huì)幫我們完成遠(yuǎn)程調(diào)用背后所需要的一切。
  • 基于 Docker 的容器化部署,由于使用了微服務(wù)架構(gòu)后,我們的系統(tǒng)將會(huì)由很多子系統(tǒng)構(gòu)成。

為了達(dá)到多個(gè)系統(tǒng)之間環(huán)境隔離的目的,我們可以將它們部署在多臺(tái)服務(wù)器上,可這樣的成本會(huì)比較高,而且每臺(tái)服務(wù)器的性能可能都沒(méi)有充分利用起來(lái)。

所以我們很自然地想到了虛擬機(jī),在同一臺(tái)服務(wù)器上運(yùn)行多個(gè)虛擬機(jī),從而實(shí)現(xiàn)環(huán)境的隔離,每個(gè)虛擬機(jī)上運(yùn)行獨(dú)立的服務(wù)。然而虛擬機(jī)的隔離成本依舊很高,因?yàn)樗枰加梅?wù)器較多的硬件資源和軟件資源。

所以,在微服務(wù)結(jié)構(gòu)下,要實(shí)現(xiàn)服務(wù)環(huán)境的隔離,Docker 是最佳選擇。它比虛擬機(jī)更加輕量級(jí),占用資源較少,而且能夠?qū)崿F(xiàn)快速部署。

基于 Jenkins 的自動(dòng)化構(gòu)建,當(dāng)我們采用了微服務(wù)架構(gòu)后,我們會(huì)發(fā)現(xiàn)這樣一個(gè)問(wèn)題。整個(gè)系統(tǒng)由許許多多的服務(wù)構(gòu)成,這些服務(wù)都需要運(yùn)行在單獨(dú)的容器中,那么每次發(fā)布的復(fù)雜度將非常高。

首先你要搞清楚這些服務(wù)之間的依賴(lài)關(guān)系、啟動(dòng)的先后順序,然后再將多個(gè)子系統(tǒng)挨個(gè)編譯、打包、發(fā)布。這些操作技術(shù)難度低,卻又容易出錯(cuò)。

那么有什么工具能夠幫助我們解決這些問(wèn)題呢?答案就是——Jenkins。 它是一款自動(dòng)化構(gòu)建的工具,簡(jiǎn)單的來(lái)說(shuō),就是我們只需要在它的界面上按一個(gè)按鈕,就可以實(shí)現(xiàn)上述一系列復(fù)雜的過(guò)程。

項(xiàng)目背景介紹

本文我以一個(gè)在線商城作為例子,一步步教大家如何搭建微服務(wù)框架,它有如下功能:

  • 產(chǎn)品管理,產(chǎn)品的增刪改查。
  • 訂單管理,訂單的增刪改查、購(gòu)物車(chē)功能。
  • 用戶(hù)管理,用戶(hù)的登錄、注冊(cè)、權(quán)限管理、收貨地址等等。
  • 數(shù)據(jù)分析,提供對(duì)本系統(tǒng)數(shù)據(jù)分析的功能。

注意:本文的 IDE 使用的是 intelliJ IDEA,推薦大家也用這個(gè),用了都說(shuō)好,用了你就會(huì)愛(ài)上它。

創(chuàng)建項(xiàng)目的組織結(jié)構(gòu)

在動(dòng)手之前,我先來(lái)說(shuō)一說(shuō)這一步的目標(biāo):

  • 創(chuàng)建一個(gè) Maven Project,命名為“Gaoxi”。這個(gè) Project 由多個(gè) Module 構(gòu)成,每個(gè) Module 對(duì)應(yīng)著“微服務(wù)”的一個(gè)子系統(tǒng),可獨(dú)立運(yùn)行,是一個(gè)獨(dú)立的項(xiàng)目。 這也是目前主流的項(xiàng)目組織形式,即多模塊項(xiàng)目。
  • 在 Gaoxi 這個(gè)項(xiàng)目下創(chuàng)建各個(gè)子模塊。

每個(gè)自模塊都是一個(gè)獨(dú)立的 Spring Boot 項(xiàng)目:

  • Gaoxi-User,用戶(hù)服務(wù)。
  • Gaoxi-Order,訂單服務(wù)。
  • Gaoxi-Product,產(chǎn)品服務(wù)。
  • Gaoxi-Analysis,數(shù)據(jù)分析服務(wù)。
  • Gaoxi-Controller,本系統(tǒng)的控制層,和以往三層結(jié)構(gòu)中的 Controller 層的作用一樣,都是用作請(qǐng)求調(diào)度,只不過(guò)在微服務(wù)架構(gòu)中,我們將它抽象成一個(gè)單獨(dú)的系統(tǒng),可以獨(dú)立運(yùn)行。
  • Gaoxi-Common-Service-Facade,它處于本系統(tǒng)的最底層,被所有模塊依賴(lài),一些公用的類(lèi)庫(kù)都放在這里。
  • Gaoxi-Redis,我們將 Redis 封裝成一個(gè)單獨(dú)的服務(wù),運(yùn)行在獨(dú)立的容器中,當(dāng)哪一個(gè)模塊需要使用 Redis 的時(shí)候,僅需要引入該服務(wù)即可,就免去了各種繁瑣的、重復(fù)的配置。而這些配置均在 Gaoxi-Redis 系統(tǒng)中完成了。

下面開(kāi)始動(dòng)手。

創(chuàng)建 Project

首先 New 一個(gè) Project,如下圖:

然后選擇 Spring Initializr,如下圖所示:

設(shè)置 groupId、artifactId、version,代碼如下: 

  1. <groupId>com.gaoxi</groupId>  
  2. <artifactId>gaoxi</artifactId>  
  3. <version>0.0.1-SNAPSHOT</version> 

Project 創(chuàng)建完畢!接下來(lái)在 Project 下面創(chuàng)建 Module。

創(chuàng)建 Module

在 Project 上 New Module,如下圖:

和剛才一樣,選擇 Spring Initializr,設(shè)置 groupId、artifactId、version。

依次創(chuàng)建好所有的 Module,如下圖所示:

構(gòu)建模塊的依賴(lài)關(guān)系

目前為止,模塊之間沒(méi)有任何聯(lián)系,下面我們要通過(guò) pom 文件來(lái)指定它們之間的依賴(lài)關(guān)系,依賴(lài)關(guān)系如下圖所示:

Gaoxi-User、Gaoxi-Analysis、Gaoxi-Product、Gaoxi-Order 這四個(gè)系統(tǒng)相當(dāng)于以往三層結(jié)構(gòu)的 Service 層,提供系統(tǒng)的業(yè)務(wù)邏輯。

只不過(guò)在微服務(wù)結(jié)構(gòu)中,Service 層的各個(gè)模塊都被抽象成一個(gè)個(gè)單獨(dú)的子系統(tǒng),它們提供 RPC 接口供上面的 Gaoxi-Controller 調(diào)用。它們之間的調(diào)用由 Dubbo 來(lái)完成,所以它們的 pom 文件中并不需要作任何配置。

而這些模塊和 Gaoxi-Common-Service-Facade 之間是本地調(diào)用,因此需要將 Gaoxi-Common-Service-Facade 打成 jar 包,并讓這些模塊依賴(lài)這個(gè) jar,因此就需要在所有模塊的 pom 中配置和 Gaoxi-Common-Service-Facade 的依賴(lài)關(guān)系。

此外,為了簡(jiǎn)化各個(gè)模塊的配置,我們將所有模塊的通用依賴(lài)放在 Project 的 pom 文件中,然后讓所有模塊作為 Project 的子模塊。這樣子模塊就可以從父模塊中繼承所有的依賴(lài),而不需要自己再配置了。

下面開(kāi)始動(dòng)手:

首先將 Common-Service-Facade 的打包方式設(shè)成 jar。

當(dāng)打包這個(gè)模塊的時(shí)候,Maven 會(huì)將它打包成 jar,并安裝在本地倉(cāng)庫(kù)中。這樣其他模塊打包的時(shí)候就可以引用這個(gè) jar。 

  1. <groupId>com.gaoxi</groupId>  
  2. <artifactId>gaoxi-common-service-facade</artifactId>  
  3. <version>0.0.1</version>  
  4. <packaging>jar</packaging> 

將其他模塊的打包方式設(shè)為 war。除了 Gaoxi-Common-Service-Facade 外,其他模塊都是一個(gè)個(gè)可獨(dú)立運(yùn)行的子系統(tǒng),需要在 Web 容器中運(yùn)行,所以我們需要將這些模塊的打包方式設(shè)成 war。 

  1. <groupId>com.gaoxi</groupId>  
  2. <artifactId>gaoxi-user</artifactId>  
  3. <version>0.0.1-SNAPSHOT</version>  
  4. <packaging>war</packaging> 

在總 pom 中指定子模塊。modules 標(biāo)簽指定了當(dāng)前模塊的子模塊是誰(shuí),但是僅在父模塊的 pom 文件中指定子模塊還不夠,還需要在子模塊的 pom 文件中指定父模塊是誰(shuí)。 

  1. <modules>  
  2.     <module>Gaoxi-Analysis</module>  
  3.     <module>Gaoxi-Order</module>  
  4.     <module>Gaoxi-Product</module>  
  5.     <module>Gaoxi-User</module>  
  6.     <module>Gaoxi-Redis</module>  
  7.     <module>Gaoxi-Controller</module>  
  8.     <module>Gaoxi-Common-Service-Facade</module>  
  9. </modules> 

在子模塊中指定父模塊。 

  1. <parent>  
  2.     <groupId>com.gaoxi</groupId>  
  3.     <artifactId>gaoxi</artifactId>  
  4.     <version>0.0.1-SNAPSHOT</version>  
  5.     <relativePath>../pom.xml</relativePath>  
  6. </parent> 

到此為止,模塊的依賴(lài)關(guān)系配置完畢!但要注意模塊打包的順序。

由于所有模塊都依賴(lài)于 Gaoxi-Common-Servie-Facade 模塊,因此在構(gòu)建模塊時(shí),首先需要編譯、打包、安裝 Gaoxi-Common-Servie-Facade,將它打包進(jìn)本地倉(cāng)庫(kù)中,這樣上層模塊才能引用到。當(dāng)該模塊安裝完畢后,再構(gòu)建上層模塊。

否則在構(gòu)建上層模塊的時(shí)候會(huì)出現(xiàn)找不到 Gaoxi-Common-Servie-Facade 中類(lèi)庫(kù)的問(wèn)題。

在父模塊的 pom 中添加所有子模塊公用的依賴(lài) 

  1. <dependencies>  
  2.     <!-- Spring Boot -->  
  3.     <dependency>  
  4.         <groupId>org.springframework.boot</groupId>  
  5.         <artifactId>spring-boot-starter</artifactId>  
  6.     </dependency>  
  7.     <!-- Spring MVC -->  
  8.     <dependency>  
  9.         <groupId>org.springframework.boot</groupId>  
  10.         <artifactId>spring-boot-starter-web</artifactId>  
  11.     </dependency>  
  12.     <!-- Spring Boot Test -->  
  13.     <dependency>  
  14.         <groupId>org.springframework.boot</groupId>  
  15.         <artifactId>spring-boot-starter-test</artifactId>  
  16.         <scope>test</scope>  
  17.     </dependency>  
  18.     <!-- MyBatis -->  
  19.     <dependency>  
  20.         <groupId>org.mybatis.spring.boot</groupId>  
  21.         <artifactId>mybatis-spring-boot-starter</artifactId>  
  22.         <version>1.3.1</version>  
  23.     </dependency>  
  24.     <!-- Mysql -->  
  25.     <dependency>  
  26.         <groupId>mysql</groupId>  
  27.         <artifactId>mysql-connector-java</artifactId>  
  28.         <scope>runtime</scope>  
  29.     </dependency>  
  30.     <!-- Dubbo -->  
  31.     <dependency> 
  32.          <groupId>io.dubbo.springboot</groupId>  
  33.         <artifactId>spring-boot-starter-dubbo</artifactId>  
  34.         <version>1.0.0</version>  
  35.     </dependency>  
  36.     <!-- gaoxi-common-service-facade -->  
  37.     <dependency>  
  38.         <groupId>com.gaoxi</groupId>  
  39.         <artifactId>gaoxi-common-service-facade</artifactId>  
  40.         <version>0.0.1</version>  
  41.     </dependency>  
  42.     <!-- AOP -->  
  43.     <dependency>  
  44.         <groupId>org.springframework.boot</groupId>  
  45.         <artifactId>spring-boot-starter-aop</artifactId>  
  46.     </dependency>  
  47.     <!-- guava -->  
  48.     <dependency>  
  49.         <groupId>com.google.guava</groupId>  
  50.         <artifactId>guava</artifactId>  
  51.         <version>23.3-jre</version>  
  52.     </dependency>  
  53. </dependencies> 

當(dāng)父模塊的 pom 中配置了公用依賴(lài)后,子模塊的 pom 文件將非常簡(jiǎn)潔,如下所示: 

  1. <groupId>com.gaoxi</groupId>  
  2. <artifactId>gaoxi-user</artifactId>  
  3. <version>0.0.1-SNAPSHOT</version>  
  4. <packaging>war</packaging>  
  5. <name>gaoxi-user</name 
  6. <parent>  
  7.     <groupId>com.gaoxi</groupId>  
  8.     <artifactId>gaoxi</artifactId>  
  9.     <version>0.0.1-SNAPSHOT</version>  
  10.     <relativePath>../pom.xml</relativePath>  
  11. </parent> 

當(dāng)項(xiàng)目的結(jié)構(gòu)搭建完成之后,接下來(lái)你需要配置 Docker 環(huán)境,并將這些項(xiàng)目打包進(jìn)容器中,驗(yàn)證下是否能正常啟動(dòng)。

創(chuàng)建 Docker 容器

安裝 Docker

在使用 Docker 之前,你當(dāng)然先要安裝 Docker,安裝過(guò)程較為簡(jiǎn)單,基本上就是傻瓜式操作,這里就不作過(guò)多介紹了,你可以在 Docker 的官網(wǎng)下載相應(yīng)系統(tǒng)的安裝包。

獲取 Tomcat 鏡像

在微服務(wù)架構(gòu)中,一個(gè)完整的系統(tǒng)被拆分成了多個(gè)被稱(chēng)為“微服務(wù)”的子系統(tǒng),這些子系統(tǒng)可以獨(dú)立運(yùn)行在 Web 容器中。所以我們需要為這些系統(tǒng)提供運(yùn)行的 Web 容器,這里我們選擇大家較為熟悉的 Tomcat。

我們知道,Tomcat 依賴(lài)于 Java 環(huán)境,安裝 Tomcat 之前要進(jìn)行一系列環(huán)境的配置:安裝 Java、配置環(huán)境變量、安裝 Tomcat 等等。

這些操作還是有些繁瑣的。不過(guò)沒(méi)關(guān)系,當(dāng)使用了 Docker 之后,這些過(guò)程都可以輕而易舉地完成。

我們只需從 Docker Hub 上找到 Tomcat 的鏡像資源,然后從上面拉取下來(lái)就可以使用。你可以使用 Tomcat 官方的鏡像,也可以使用我發(fā)布在 Docker Hub 上的 Tomcat 鏡像。

注意點(diǎn):推薦使用我的 Tomcat 鏡像資源 chaimm/tomcat,因?yàn)檫@個(gè)鏡像中除了配置 Tomcat 的安裝環(huán)境以外,還有一些本項(xiàng)目中要用到的 Jenkins 相關(guān)的配置。

采用如下命令從 Docker Hub 上拉取鏡像: 

  1. docker pull chaimm/tomcat:1.1 

簡(jiǎn)單解釋下,Docker pull 是從 Docker Hub 上拉取鏡像的命令,后面的 chaimm/tomcat 是鏡像的名稱(chēng);:1.1 是鏡像的版本號(hào)。目前這個(gè)鏡像的最新版本號(hào)是 1.1,推薦大家拉取這個(gè)。

創(chuàng)建 Tomcat 容器

這里再簡(jiǎn)單介紹下“鏡像”和“容器”的關(guān)系。 “鏡像”就好比是面向?qū)ο笾械?ldquo;類(lèi)”,“容器”就好比“類(lèi)”創(chuàng)建的“對(duì)象”。

在面向?qū)ο笾校?ldquo;類(lèi)”定義了各種屬性,“類(lèi)”可以實(shí)例化出多個(gè)“對(duì)象”;而在 Docker 中,“鏡像”定義了各種配置信息,它可以實(shí)例化出多個(gè)“容器”。“容器”就是一臺(tái)可以運(yùn)行的“虛擬機(jī)”。

接下來(lái)我們需要為所有的微服務(wù)創(chuàng)建各自的容器:

  • gaoxi-user
  • gaoxi-product
  • gaoxi-order
  • gaoxi-analysis
  • gaoxi-controller
  • gaoxi-redis

以創(chuàng)建 gaoxi-user 容器為例,采用如下命令創(chuàng)建容器: 

  1. docker run --name gaoxi-user-1 -p 8082:8080 -v /usr/web/gaoxi-log:/opt/tomcat/gaoxi-log chaimm/tomcat:1.1 
  • --name:指定容器的名字。
  • -p:指定容器的端口映射,-p 8082:8080 表示將容器的 8080 端口映射到宿主機(jī)的 8082 端口上。
  • -v:指定容器數(shù)據(jù)卷的映射,xxx:yyy 表示將容器 yyy 目錄映射到宿主機(jī)的 xxx 目錄上,從而訪問(wèn)宿主機(jī)的 xxx 目錄就相當(dāng)于訪問(wèn)容器的 yyy 目錄。
  • chaimm/tomcat:1.1:表示容器所對(duì)應(yīng)的鏡像。

這條命令執(zhí)行成功后,你就可以通過(guò)你的 IP:8082 訪問(wèn)到 gaoxi-user-1 容器的 Tomcat 了。如果你看到了那只眼熟的貓,那就說(shuō)明容器啟動(dòng)成功了!

接下來(lái),你需要按照上面的方法,給剩下幾個(gè)系統(tǒng)創(chuàng)建好 Tomcat 容器。

注意點(diǎn):你需要給這些 Tomcat 容器指定不同的端口號(hào),防止端口號(hào)沖突。當(dāng)然,在實(shí)際開(kāi)發(fā)中,你并不需要將容器的 8080 端口映射到宿主機(jī)上,這里僅僅是為了驗(yàn)證容器是否啟動(dòng)成功才這么做的。

整合 Dubbo

創(chuàng)建 ZooKeeper 容器

Dubbo 一共定義了三種角色,分別是:服務(wù)提供者、服務(wù)消費(fèi)者、注冊(cè)中心。

注冊(cè)中心是服務(wù)提供者和服務(wù)消費(fèi)者的橋梁,服務(wù)消費(fèi)者會(huì)在初始化的時(shí)候?qū)⒆约旱?IP 和端口號(hào)發(fā)送給注冊(cè)中心,而服務(wù)消費(fèi)者通過(guò)注冊(cè)中心指導(dǎo)服務(wù)提供者的 IP 和端口號(hào)。

在 Dubbo 中,注冊(cè)中心有多種選擇,Dubbo 最為推薦的即為 ZooKeeper,本文采用 ZooKeepeer 作為 Dubbo 的注冊(cè)中心。

創(chuàng)建 ZooKeeper 容器也較為簡(jiǎn)單,大家可以直接使用我創(chuàng)建的 ZooKeeper 鏡像,通過(guò)如下命令即可下載鏡像: 

  1. docker pull chaimm/zookeeper-dubbo:1.0 

該鏡像中不僅運(yùn)行了一個(gè) ZooKeeper,還運(yùn)行了一個(gè)擁有 Dubbo-Admin 項(xiàng)目的 Tomcat。dubbo-admin 是 Dubbo 的一個(gè)可視化管理工具,可以查看服務(wù)的發(fā)布和引用的情況。

使用如下命令啟動(dòng)容器: 

  1. docker run --name zookeeper-debug -p 2182:2181 -p 10000:8080 chaimm/zookeeper-dubbo:1.0 
  • -p 2182:2181,將容器的 2181 端口映射到宿主機(jī)的 2182 端口上,該端口是 ZooKeeper 的端口號(hào)。
  • -p 10000:8080,將容器的 8080 端口映射到宿主機(jī)的 10000 端口上,該端口是 Dubbo-Admin 所在 Tomcat 的端口號(hào)。

啟動(dòng)成功后,你就可以通過(guò)你的 IP:10000/dubbo-admin-2.8.4/ 訪問(wèn)到 Dubbo-Admin,如下圖所示:

父 POM 文件中引入 Dubbo 依賴(lài) 

  1. <!-- Spring Boot Dubbo 依賴(lài) -->  
  2. <dependency>  
  3.     <groupId>io.dubbo.springboot</groupId>  
  4.     <artifactId>spring-boot-starter-dubbo</artifactId>  
  5.     <version>1.0.0</version>  
  6. </dependency> 

發(fā)布服務(wù)

假設(shè),我們需要將 Gaoxi-User 項(xiàng)目中的 UserService 發(fā)布成一項(xiàng) RPC 服務(wù),供其他系統(tǒng)遠(yuǎn)程調(diào)用,那么我們究竟該如何借助 Dubbo 來(lái)實(shí)現(xiàn)這一功能呢?

在 Gaoxi-Common-Service-Facade 中定義 UserService 的接口。

由于服務(wù)的發(fā)布和引用都依賴(lài)于接口,但服務(wù)的發(fā)布方和引用方在微服務(wù)架構(gòu)中往往不在同一個(gè)系統(tǒng)中,所以需要將發(fā)布和引用的接口放在公共類(lèi)庫(kù)中,從而雙方都能夠引用。

接口如下所示: 

  1. public interface UserService {  
  2.     public UserEntity login(LoginReq loginReq);  

在 Gaoxi-User 中定義接口的實(shí)現(xiàn)。在實(shí)現(xiàn)類(lèi)上需要加上 Dubbo 的 @Service 注解,從而 Dubbo 會(huì)在項(xiàng)目啟動(dòng)的時(shí)候掃描到該注解,將它發(fā)布成一項(xiàng) RPC 服務(wù)。 

  1. @Service(version = "1.0.0" 
  2. public class UserServiceImpl implements UserService {  
  3.     @Override  
  4.     public UserEntity login(LoginReq loginReq) {  
  5.         // 具體的實(shí)現(xiàn)代碼  
  6.     }  

在 Gaoxi-User 的 application.properties 中配置服務(wù)提供者的信息。 

  1. spring.dubbo.application.name=user-provider # 本服務(wù)的名稱(chēng)  
  2. spring.dubbo.registry.address=zookeeper://IP:2182 # ZooKeeper所在服務(wù)器的IP和端口號(hào)  
  3. spring.dubbo.protocol.name=dubbo # RPC通信所采用的協(xié)議  
  4. spring.dubbo.protocol.port=20883 # 本服務(wù)對(duì)外暴露的端口號(hào)  
  5. spring.dubbo.scan=com.gaoxi.user.service # 服務(wù)實(shí)現(xiàn)類(lèi)所在的路徑 

按照上面配置完成后,當(dāng) Gaoxi-User 系統(tǒng)初始化的時(shí)候,就會(huì)掃描 spring.dubbo.scan 所指定的路徑下的 @Service 注解,該注解標(biāo)識(shí)了需要發(fā)布成 RPC 服務(wù)的類(lèi)。

Dubbo 會(huì)將這些類(lèi)的接口信息+本服務(wù)器的 IP+spring.dubbo.protocol.port 所指定的端口號(hào)發(fā)送給 ZooKeeper,Zookeeper 會(huì)將這些信息存儲(chǔ)起來(lái)。 這就是服務(wù)發(fā)布的過(guò)程,下面來(lái)看如何引用一項(xiàng) RPC 服務(wù)。

引用服務(wù)

假設(shè),Gaoxi-Controller 需要調(diào)用 Gaoxi-User 提供的登錄功能,此時(shí)它就需要引用 UserService 這項(xiàng)遠(yuǎn)程服務(wù)。下面來(lái)介紹服務(wù)引用的方法。

聲明需要引用的服務(wù)。引用服務(wù)非常簡(jiǎn)單,你只需要在引用的類(lèi)中聲明一項(xiàng)服務(wù),然后用 @Reference 標(biāo)識(shí),如下所示: 

  1. @RestController  
  2. public class UserControllerImpl implements UserController {  
  3.     @Reference(version = "1.0.0" 
  4.     private UserService userService;  
  5.     @Override  
  6.     public Result login(LoginReq loginReq, HttpServletResponse httpRsp) {  
  7.         // 登錄鑒權(quán)  
  8.         UserEntity userEntity = userService.login(loginReq);  
  9.     }  

在 Gaoxi-Controller 的 application.properties 中配置服務(wù)消費(fèi)者的信息。 

  1. spring.dubbo.application.name=controller-consumer # 本服務(wù)的名稱(chēng)  
  2. spring.dubbo.registry.address=zookeeper://IP:2182 # zookeeper所在服務(wù)器的IP和端口號(hào)  
  3. spring.dubbo.scan=com.gaoxi # 引用服務(wù)的路徑 

上述操作完成后,當(dāng) Gaoxi-Controller 初始化的時(shí)候,Dubbo 就會(huì)掃描 spring.dubbo.scan 所指定的路徑,并找到所有被 @Reference 修飾的成員變量;然后向 Zookeeper 請(qǐng)求該服務(wù)所在的 IP 和端口號(hào)。

當(dāng)調(diào)用 userService.login() 的時(shí)候,Dubbo 就會(huì)向 Gaoxi-User 發(fā)起請(qǐng)求,完成調(diào)用的過(guò)程。

這個(gè)調(diào)用過(guò)程是一次 RPC 調(diào)用,但作為程序猿來(lái)說(shuō),這和調(diào)用一個(gè)本地函數(shù)沒(méi)有任何區(qū)別,遠(yuǎn)程調(diào)用的一切都由 Dubbo 來(lái)幫你完成。這就是 Dubbo 的作用。

自動(dòng)化構(gòu)建

Jenkins 是一個(gè)自動(dòng)化構(gòu)建工具,它可以幫助我們擺脫繁瑣的部署過(guò)程,我們只需要在一開(kāi)始配置好構(gòu)建策略,以后部署只需要一鍵完成。

創(chuàng)建 Jenkins 容器

Jenkins 采用 Java 開(kāi)發(fā),也需要 Java 環(huán)境,但我們使用 Docker 后,一切都采用容器化部署,Jenkins 也不例外。

拉取鏡像,這里我們使用 Jenkins 官方提供的鏡像,大家只需執(zhí)行如下命令拉取即可: 

  1. docker pull docker.io/jenkins/jenkins 

啟動(dòng)容器,由于 Jenkins 運(yùn)行在 Tomcat 容器中,因此我們將容器的 8080 端口映射到宿主機(jī)的 10080 端口上: 

  1. docker run --name jenkins -p 10080:8080 docker.io/jenkins/jenkins 

初始化 Jenkins,然后你需要訪問(wèn) IP:10080,Jenkins 會(huì)帶著你進(jìn)行一系列的初始化設(shè)置,你只要跟著它一步步走就行了,比較傻瓜式。

在 Jenkins 中創(chuàng)建項(xiàng)目

接下來(lái)我們要做的是,在 Jenkins 中為每一個(gè)服務(wù)創(chuàng)建一個(gè)項(xiàng)目,每個(gè)項(xiàng)目中定義了構(gòu)建的具體流程。由于我們將整個(gè)項(xiàng)目分成了 6 個(gè)微服務(wù),所以我們需要在 Jenkins 中分別為這 6 個(gè)服務(wù)創(chuàng)建項(xiàng)目。那就開(kāi)始吧~

點(diǎn)擊頁(yè)面左側(cè)的“新建”按鈕:

輸入項(xiàng)目名稱(chēng) gaoxi-user,選擇“構(gòu)建一個(gè) Maven 項(xiàng)目”,然后點(diǎn)擊“OK”:

配置 Git 倉(cāng)庫(kù),選擇 Git,然后輸入本項(xiàng)目 Git 倉(cāng)庫(kù)的 URL,并在 Credentials 中輸入 Git 的用戶(hù)名和密碼,如下圖所示:

構(gòu)建觸發(fā)器,選擇第一項(xiàng),如下圖所示:

Pre Step,Pre Step 會(huì)在正式構(gòu)建前執(zhí)行,由于所有項(xiàng)目都依賴(lài)于 Gaoxi-Common-Service—Facade,因此在項(xiàng)目構(gòu)建前,需要將它安裝到本地倉(cāng)庫(kù),然后才能被當(dāng)前項(xiàng)目正確依賴(lài)。 

因此,在 Pre Step 中填寫(xiě)如下信息:

Build,然后就是正式構(gòu)建的過(guò)程,填寫(xiě)如下信息即可:

OK,Gaoxi-User 的構(gòu)建過(guò)程就配置完成了。當(dāng)我們點(diǎn)擊“立即構(gòu)建”按鈕時(shí),Jenkins 首先會(huì)從我們指定的 Git 倉(cāng)庫(kù)中拉取代碼,然后執(zhí)行 Pre Step 中的 Maven 命令,將 Gaoxi-Common-Serivce-Facade 打包安裝到本地倉(cāng)庫(kù)。然后執(zhí)行 Build 過(guò)程,將 Gaoxi-User 進(jìn)行編譯打包。

但此時(shí) Gaoxi-User 仍然只是一個(gè)本地 war 包,并沒(méi)有部署到 Tomcat 容器中,而我們采用了容器化部署后,Jenkins 服務(wù)和 Gaoxi-User 服務(wù)并不在同一個(gè) Docker 容器中。

那么究竟該如何才能將 Jenkins 本地編譯好的 war 包發(fā)送到 Gaoxi-User 容器中呢?這就需要使用 Jenkins 的一個(gè)插件——Deploy Plugin。

遠(yuǎn)程部署

首先你需要下載 Deploy Plugin 插件。

下載地址:https://wiki.jenkins.io/display/JENKINS/Deploy+Plugin

安裝插件,在系統(tǒng)管理–>插件管理–>高級(jí)上傳 deploy.hpi 進(jìn)行安裝。

在父項(xiàng)目的 pom 文件中增加遠(yuǎn)程部署插件: 

  1. <plugin>  
  2.     <groupId>org.codehaus.cargo</groupId>  
  3.     <artifactId>cargo-maven2-plugin</artifactId>  
  4.     <version>1.6.5</version>  
  5.     <configuration>  
  6.         <container>  
  7.             <!-- 指明使用的tomcat服務(wù)器版本 -->  
  8.             <containerId>tomcat8x</containerId>  
  9.             <type>remote</type>  
  10.         </container>  
  11.         <configuration>  
  12.             <type>runtime</type>  
  13.             <cargo.remote.username>Tomcat的用戶(hù)名</cargo.remote.username>  
  14.             <cargo.remote.password>Tomcat的密碼</cargo.remote.password 
  15.         </configuration>  
  16.     </configuration>  
  17.     <executions>  
  18.         <execution>  
  19.             <phase>deploy</phase>  
  20.             <goals>  
  21.                 <goal>redeploy</goal>  
  22.             </goals>  
  23.         </execution>  
  24.     </executions>  
  25. </plugin> 

為 Tomcat 設(shè)置用戶(hù)名和密碼,修改 gaoxi-user 容器中 Tomcat 的 tomcat-users.xml 文件,增加 Tomcat 的 manager 用戶(hù)。

注意:如果你使用了 chaimm/tomcat 鏡像,那么其中 Tomcat 配置都已經(jīng)完成,默認(rèn)用戶(hù)名:admin、默認(rèn)密碼:jishimen2019。強(qiáng)烈建議修改用戶(hù)名和密碼。

修改 Jenkins 中 gaoxi-user 的配置,在“構(gòu)建后操作”中增加如下配置:

  • WAR/EAR files:表示你需要發(fā)布的 war 包。
  • Containers:配置目標(biāo) Tomcat 的用戶(hù)名和密碼。

Maven 的 profile 功能

在實(shí)際開(kāi)發(fā)中,我們的系統(tǒng)往往有多套環(huán)境構(gòu)成,如:開(kāi)發(fā)環(huán)境、測(cè)試環(huán)境、預(yù)發(fā)環(huán)境、生產(chǎn)環(huán)境。而不同環(huán)境的配置各不相同。

如果我們只有一套配置,那么當(dāng)系統(tǒng)從一個(gè)環(huán)境遷移到另一個(gè)環(huán)境的時(shí)候,就需要通過(guò)修改代碼來(lái)更換配置,這樣無(wú)疑增加了工作的復(fù)雜度,而且易于出錯(cuò)。但好在 Maven 提供了 profile 功能,能幫助我們解決這一個(gè)問(wèn)題。

父項(xiàng)目的 pom 中添加 profile 元素,首先,我們需要在總 pom 的中添加多套環(huán)境的信息,如下所示: 

  1. <profiles>  
  2.     <profile>  
  3.         <id>dev</id>  
  4.         <properties>  
  5.             <profileActive>dev</profileActive>  
  6.         </properties>  
  7.         <activation>  
  8.             <activeByDefault>true</activeByDefault>  
  9.         </activation>  
  10.     </profile>  
  11.     <profile>  
  12.         <id>test</id>  
  13.         <properties>  
  14.             <profileActive>test</profileActive>  
  15.         </properties>  
  16.     </profile>  
  17.     <profile>  
  18.         <id>prod</id>  
  19.         <properties>  
  20.             <profileActive>prod</profileActive>  
  21.         </properties>  
  22.     </profile>  
  23. </profiles> 

父項(xiàng)目的 pom 中添加 resource 元素,resource 標(biāo)識(shí)了不同環(huán)境下需要打包哪些配置文件。 

  1. <resources>  
  2.     <resource>  
  3.         <!-- 標(biāo)識(shí)配置文件所在的目錄 -->  
  4.         <directory>src/main/resources</directory>  
  5.         <filtering>true</filtering>  
  6.         <!-- 構(gòu)建時(shí)將這些配置文件全都排除掉 -->  
  7.         <excludes>  
  8.             <exclude>application.properties</exclude>  
  9.             <exclude>application-dev.properties</exclude>  
  10.             <exclude>application-test.properties</exclude>  
  11.             <exclude>application-prod.properties</exclude>  
  12.         </excludes>  
  13.     </resource>  
  14.     <resource>  
  15.         <directory>src/main/resources</directory>  
  16.         <filtering>true</filtering>  
  17.         <!-- 標(biāo)識(shí)構(gòu)建時(shí)所需要的配置文件 -->  
  18.         <includes>  
  19.             <include>application.properties</include>  
  20.             <!-- ${profileActive}這個(gè)值會(huì)在maven構(gòu)建時(shí)傳入 -->  
  21.             <include>application-${profileActive}.properties</include>  
  22.         </includes>  
  23.     </resource>  
  24. </resources> 

父項(xiàng)目的 pom 中添加插件 maven-resources-plugin,該插件用來(lái)在 Maven 構(gòu)建時(shí)參數(shù)替換。 

  1. <plugin>  
  2.     <artifactId>maven-resources-plugin</artifactId>  
  3.     <version>3.0.2</version>  
  4.     <configuration>  
  5.         <delimiters>  
  6.             <delimiter>@</delimiter>  
  7.         </delimiters>  
  8.         <useDefaultDelimiters>false</useDefaultDelimiters>  
  9.     </configuration>  
  10. </plugin> 

在子項(xiàng)目中創(chuàng)建配置,分別為 dev 環(huán)境、test 環(huán)境、prod 環(huán)境創(chuàng)建三套配置,application.proerpties 中存放公用的配置。

在 application.properties 中添加spring.profiles.active=@profileActive@: 

  1. spring.profiles.active=@profileActive@  

修改 Jenkins 的配置,在所有 Jenkins 中所有 Maven 命令的末尾添加-P test,在打包的時(shí)候 -P 后面的參數(shù)將會(huì)作為 @profileActive@ 的值傳入系統(tǒng)中,從而根據(jù)該值打包相應(yīng)的 application-{profileActive}.properties 文件。

開(kāi)發(fā)流程

到此為止,所有準(zhǔn)備工作都已經(jīng)完成,接下來(lái)就可以進(jìn)入代碼開(kāi)發(fā)階段。下面我以一個(gè)例子,帶著大家感受下有了這套微服務(wù)框架后,我們的開(kāi)發(fā)流程究竟有了哪些改變?

下面以開(kāi)發(fā)一個(gè)用戶(hù)登錄功能為例,介紹下使用本框架之后開(kāi)發(fā)的流程。

開(kāi)發(fā)目標(biāo)

有如下兩個(gè)開(kāi)發(fā)目標(biāo):

  • 在 Gaoxi-User 系統(tǒng)中實(shí)現(xiàn)登錄的業(yè)務(wù)邏輯,并發(fā)布成 RPC 服務(wù)。
  • 在 Gaoxi-Controller 中遠(yuǎn)程調(diào)用登錄服務(wù),并向前端提供登錄的 REST 接口。

開(kāi)發(fā)登錄服務(wù)

首先需要在 Gaoxi-Common-Service-Facade 中創(chuàng)建 UserService 接口,并在其中聲明登錄的抽象函數(shù)。 

  1. public interface UserService {  
  2.     public UserEntity login(LoginReq loginReq);  

PS:為什么要將 UserService 放在 Gaoxi-Common-Service-Facade 中? 

在這個(gè)項(xiàng)目中,Gaoxi-User 是 UserService 服務(wù)的提供方,Gaoxi-Controller 是 UserService 服務(wù)的引用方。由于二者并不在同一個(gè)系統(tǒng)中,所以必須要借助于 Dubbo 來(lái)實(shí)現(xiàn)遠(yuǎn)程方法調(diào)用。

而 Dubbo 發(fā)布服務(wù)和引用服務(wù)的時(shí)候,都是根據(jù)服務(wù)的接口標(biāo)識(shí)服務(wù)的,即服務(wù)引用方和發(fā)布方都需要使用服務(wù)的接口,因此需要將服務(wù)的接口放在所有項(xiàng)目共同依賴(lài)的基礎(chǔ)模塊——Gaoxi-Common-Service-Facade 中。

然后在 Gaoxi-User 中開(kāi)發(fā) UserService 的實(shí)現(xiàn)——UserServiceImpl。 UserServiceImpl 上必須要加上 Dubbo 的 @Service 注解,從而告訴 Dubbo,在本項(xiàng)目初始化的時(shí)候需要將這個(gè)類(lèi)發(fā)布成一項(xiàng)服務(wù),供其他系統(tǒng)調(diào)用。 

  1. @Service(version = "1.0.0" 
  2. @org.springframework.stereotype.Service  
  3. public class UserServiceImpl implements UserService {  
  4.     @Autowired  
  5.     private UserDAO userDAO;  
  6.     @Override  
  7.     public UserEntity login(LoginReq loginReq) {  
  8.         // 校驗(yàn)參數(shù)  
  9.         checkParam(loginReq);  
  10.         // 創(chuàng)建用戶(hù)查詢(xún)請(qǐng)求  
  11.         UserQueryReq userQueryReq = buildUserQueryReq(loginReq);  
  12.         // 查詢(xún)用戶(hù)  
  13.         List<UserEntity> userEntityList = userDAO.findUsers(userQueryReq);  
  14.         // 查詢(xún)失敗  
  15.         if (CollectionUtils.isEmpty(userEntityList)) {  
  16.             throw new CommonBizException(ExpCodeEnum.LOGIN_FAIL);  
  17.         }  
  18.         // 查詢(xún)成功  
  19.         return userEntityList.get(0);  
  20.     }  

引用登錄服務(wù)

當(dāng) UserService 開(kāi)發(fā)完畢后,接下來(lái) Gaoxi-Controller 需要引用該服務(wù),并向前端提供一個(gè)登錄的 REST 接口。

若要使用 userService 中的函數(shù),僅需要在 userService 上添加 @Reference 注解,然后就像調(diào)用本地函數(shù)一樣使用 userService 即可。

Dubbo 會(huì)幫你找到 UserService 服務(wù)所在的 IP 和端口號(hào),并發(fā)送調(diào)用請(qǐng)求。但這一切對(duì)于程序猿來(lái)說(shuō)是完全透明的。 

  1. @RestController  
  2. public class UserControllerImpl implements UserController {  
  3.     @Reference(version = "1.0.0" 
  4.     private UserService userService;  
  5.     @Override  
  6.     public Result login(LoginReq loginReq, HttpServletResponse httpRsp) {  
  7.         // 登錄鑒權(quán)  
  8.         UserEntity userEntity = userService.login(loginReq);  
  9.         // 登錄成功  
  10.         doLoginSuccess(userEntity, httpRsp);  
  11.         return Result.newSuccessResult();  
  12.     }  

自動(dòng)構(gòu)建服務(wù)

上面的代碼完成后,你需要將代碼提交至你的 Git 倉(cāng)庫(kù),接下來(lái)就是自動(dòng)化部署的過(guò)程了。

你需要進(jìn)入 Jenkins,由于剛才修改了 Gaoxi-User 和 Gaoxi-Controller 的代碼,因此你需要分別構(gòu)建這兩個(gè)項(xiàng)目。 

接下來(lái) Jenkins 會(huì)自動(dòng)從你的 Git 倉(cāng)庫(kù)中拉取最新的代碼,然后依次執(zhí)行 Pre Step、Build、構(gòu)建后操作的過(guò)程。

由于我們?cè)?Pre Step 中設(shè)置了編譯 Gaoxi-Common-Service-Facade,因此 Jenkins 首先會(huì)將其安裝到本地倉(cāng)庫(kù);然后再執(zhí)行 Build 過(guò)程,構(gòu)建 Gaoxi-User,并將其打包成 war 包。

最后將執(zhí)行“構(gòu)建后操作”,將 war 包發(fā)布到相應(yīng)的 Tomcat 容器中。 至此,整個(gè)發(fā)布流程完畢!

查看服務(wù)的狀態(tài)

當(dāng) Jenkins 構(gòu)建完成后,我們可以登錄 Dubbo-Admin 查看服務(wù)發(fā)布和引用的狀態(tài)。

當(dāng)我們搜索 UserService 服務(wù)后,可以看到,該服務(wù)的提供者已經(jīng)成功發(fā)布了服務(wù):

點(diǎn)擊“消費(fèi)者”我們可以看到,該服務(wù)已經(jīng)被 controller-consumer 成功訂閱:

總結(jié)

總結(jié)一下,這套框架有如下優(yōu)勢(shì):

  • 微服務(wù)架構(gòu),我們借助于 Spring Boot 和 Dubbo 實(shí)現(xiàn)了微服務(wù)架構(gòu)。微服務(wù)架構(gòu)的理念就是將一個(gè)原本龐大、復(fù)雜的系統(tǒng),按照業(yè)務(wù)功能拆分成一個(gè)個(gè)具有獨(dú)立功能、可以獨(dú)立運(yùn)行的子系統(tǒng)。

系統(tǒng)之間若有依賴(lài),則通過(guò) RPC 接口通信。從而最大限度地降低了系統(tǒng)之間的耦合度,從而更加易于擴(kuò)展、更加易于維護(hù)。

  • 容器化部署,我們借助于 Docker 實(shí)現(xiàn)了容器化部署。容器能夠幫助我們屏蔽不同環(huán)境下的配置問(wèn)題,使得我們只需要有一個(gè) Dockerfile 文件,就可以處處運(yùn)行。

和虛擬機(jī)一樣,Docker 也擁有環(huán)境隔離的能力,但比虛擬機(jī)更加輕量級(jí),由于每個(gè)容器僅僅是一條進(jìn)程,因此它可以達(dá)到秒級(jí)的啟動(dòng)速度。

  • 自動(dòng)化構(gòu)建,我們借助于 Jenkins 實(shí)現(xiàn)了所有項(xiàng)目的自動(dòng)化構(gòu)建與部署。我們只需要點(diǎn)擊“立即構(gòu)建”這個(gè)按鈕,Jenkins 就可以幫助我們梳理好錯(cuò)綜復(fù)雜的項(xiàng)目依賴(lài)關(guān)系,準(zhǔn)確無(wú)誤地完成構(gòu)建,并將 war 包發(fā)送到相應(yīng)的 Web 容器中。

在啟動(dòng)的過(guò)程中,Dubbo 會(huì)掃描當(dāng)前項(xiàng)目所需要發(fā)布和引用的服務(wù),將所需要發(fā)布的服務(wù)發(fā)布到 ZooKeeper 上,并向 ZooKeeper 訂閱所需的服務(wù)。

有了 Jenkins 之后,這一切都是自動(dòng)化完成。也許你并沒(méi)有太強(qiáng)烈地感受到 Jenkins 所帶來(lái)的便利。但是你想一想,對(duì)于一個(gè)具有錯(cuò)綜復(fù)雜的依賴(lài)關(guān)系的微服務(wù)系統(tǒng)而言,如果每個(gè)服務(wù)的構(gòu)建都需要你手動(dòng)完成的話,你很快就會(huì)崩潰,你大把的時(shí)間將會(huì)投入在無(wú)聊但又容易出錯(cuò)的服務(wù)構(gòu)建上,而 Jenkins 的出現(xiàn)能讓這一切自動(dòng)化完成。

作者:大閑人柴毛毛

編輯:陶家龍、孫淑娟

來(lái)源:轉(zhuǎn)載自公眾號(hào):大閑人柴毛毛,ID:dxrcmm

源碼:https://github.com/bz51/SpringBoot-Dubbo-Docker-Jenkins


 

責(zé)任編輯:龐桂玉 來(lái)源: 51CTO技術(shù)棧
相關(guān)推薦

2022-08-25 14:41:51

集群搭建

2024-10-21 08:01:49

私服倉(cāng)庫(kù)Maven

2019-08-26 09:25:23

RedisJavaLinux

2020-06-17 07:35:57

虛擬機(jī)部署微服務(wù)

2022-07-14 07:34:26

windowsmysqlcentos

2017-01-18 09:04:13

TensorFlow圖像識(shí)別

2022-03-14 14:47:21

HarmonyOS操作系統(tǒng)鴻蒙

2010-01-20 10:44:01

linux DHCP服務(wù)器

2017-01-18 09:20:23

TensorFlow圖像識(shí)別

2011-03-25 12:45:49

Oracle SOA

2020-11-03 14:10:29

Vue服務(wù)端渲染前端

2023-03-14 07:34:47

代碼生成器開(kāi)發(fā)

2010-07-06 09:38:51

搭建私有云

2022-01-04 08:52:14

博客網(wǎng)站Linux 系統(tǒng)開(kāi)源

2010-07-06 09:43:57

搭建私有云

2021-07-14 09:00:00

JavaFX開(kāi)發(fā)應(yīng)用

2011-05-03 15:59:00

黑盒打印機(jī)

2011-01-10 14:41:26

2025-05-07 00:31:30

2010-10-29 14:04:49

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

国产一区二区三区综合| 日韩理论电影中文字幕| 国产精品美女久久久久久2018| 国产成人精品久久亚洲高清不卡| 精品人妻无码一区二区三区 | 欧美成人午夜精品免费| 四虎亚洲精品| 99精品久久只有精品| 国产精品成人va在线观看| 成人无码精品1区2区3区免费看 | 亚洲精品国产精品乱码不99 | 亚洲欧洲精品一区| 国产区精品在线| 亚洲久久一区| 亚洲亚裔videos黑人hd| 午夜一级免费视频| 国产经典三级在线| www国产亚洲精品久久麻豆| 国产精品久久久久久影视| 日本黄色小说视频| 天海翼精品一区二区三区| 欧美日韩五月天| 日韩激情视频一区二区| av男人的天堂在线| 成人美女在线视频| 国产精品入口免费视| 欧美日韩一级大片| 欧洲杯什么时候开赛| 日韩三级视频在线观看| 欧美激情国产精品日韩| 在线电影福利片| 国产蜜臀97一区二区三区 | 美国十次综合久久| 黑人精品xxx一区一二区| 亚洲人成网站在线播放2019| 日本xxxx人| 国产一区二区影院| 国产激情视频一区| 日本熟妇毛耸耸xxxxxx| 97精品97| 国产一区二区三区四区福利| 制服丝袜在线第一页| 久久夜夜久久| 欧美日韩国产一区二区三区| 国产四区在线观看| 东凛在线观看| 97se亚洲国产综合自在线观| 不卡视频一区二区| 91中文字幕在线视频| 久久蜜桃精品| 18久久久久久| 黄色一级视频在线观看| 国产99久久| 亚洲精品自拍偷拍| 亚洲av成人片无码| 中文字幕久久精品一区二区| 日韩欧美国产三级电影视频| 中文字幕乱妇无码av在线| 激情视频亚洲| 精品伦理精品一区| 一级欧美一级日韩片| 精品国内亚洲2022精品成人| 精品国产免费一区二区三区香蕉| 不卡的一区二区| 激情亚洲另类图片区小说区| 日韩电影中文 亚洲精品乱码| 久久无码人妻精品一区二区三区| 天堂在线精品| 在线日韩第一页| 糖心vlog免费在线观看| 一区二区中文字| 久久久久五月天| 久久久久久久久久免费视频| 久久亚洲二区| 91精品美女在线| 午夜精品久久久久久久99老熟妇| 成人免费av在线| 免费一区二区三区在在线视频| 精品99又大又爽又硬少妇毛片| 中文字幕乱码久久午夜不卡| 国产对白在线播放| yellow在线观看网址| 91久久线看在观草草青青| 国产福利精品一区二区三区| 91精品尤物| 亚洲美女性视频| 日本一区二区在线播放| 51妺嘿嘿午夜福利| 天天综合国产| 久久久亚洲精选| 无码人妻熟妇av又粗又大| 麻豆极品一区二区三区| 亚洲最大成人免费视频| 熟妇高潮一区二区三区| 欧美激情一区在线观看| 中文字幕在线乱| 丝袜诱惑一区二区| 欧美久久久一区| 欧美精品黑人猛交高潮| 香蕉久久网站| 欧美在线www| 99在线小视频| 久久久国际精品| 欧美大黑帍在线播放| 欧美成人影院| 精品免费日韩av| 久久久久99精品成人| 亚洲美女黄网| 91精品久久久久久久久久久| 天天射天天色天天干| 中文字幕在线一区二区三区| 国产在线视频综合| 成人国产精品入口免费视频| 亚洲精品久久久久久下一站 | 国模大尺度视频| 欧美精美视频| 午夜精品久久久久久久久久久久久 | 亚洲五月激情网| 一区二区三区自拍视频| 亚洲区在线播放| 国产在线视频在线观看| 精品一区二区成人精品| 日本一区二区在线视频| 欧美freesex黑人又粗又大| 日韩亚洲欧美一区| 2017亚洲天堂| 日韩中文欧美在线| 蜜桃日韩视频| 国产社区精品视频| 欧美成人一级视频| 日韩a级片在线观看| 捆绑调教美女网站视频一区| 免费在线成人av| 不卡av影片| 日韩国产高清视频在线| 欧美日韩中文视频| 成人天堂资源www在线| 日本黄网站色大片免费观看| 91麻豆精品| 精品国产一区二区三区在线观看 | 日本成人黄色| 美女18一级毛片一品久道久久综合| 精品动漫一区二区三区在线观看| 九九精品在线观看视频| 国产精品小仙女| 国产一二三四区在线观看| 精品视频成人| 久久天天躁狠狠躁夜夜躁| 91精品国产乱码久久久| 一区精品在线播放| 看看黄色一级片| 91精品蜜臀一区二区三区在线| 成人xxxx视频| 成人ww免费完整版在线观看| 欧美一区二区视频在线观看2022| 亚洲综合视频网站| 国产精品一品二品| 欧美激情亚洲天堂| 国产精品宾馆| 欧美a级在线| 一区二区三区动漫| 中文区中文字幕免费看| 亚洲国产精品av| 性生生活大片免费看视频| 97精品国产| 亚洲aaa激情| 久久五月精品中文字幕| 亚洲成人激情在线观看| 在线观看黄网站| 久久精品亚洲精品国产欧美| 天天色综合社区| 国产精品久久久久9999赢消| 91国产在线播放| 高清精品在线| 一区三区二区视频| 91麻豆成人精品国产| 一区二区三区在线观看网站| 亚洲精品第二页| 日韩专区中文字幕一区二区| 美女黄色片网站| 成人av婷婷| 日本欧美在线视频| 美女羞羞视频在线观看| 337p日本欧洲亚洲大胆精品| 一级一片免费看| 亚洲男人的天堂在线观看| 午夜男人的天堂| 久久国产尿小便嘘嘘| 成人免费看片'免费看| 久久综合欧美| 成人在线视频电影| 久久天堂av| 欧美高跟鞋交xxxxhd| 麻豆影视在线| 欧美本精品男人aⅴ天堂| 亚洲中文一区二区| 亚洲综合激情网| 国产综合精品在线| 成人午夜在线播放| 污污动漫在线观看| 一本一本久久| 路边理发店露脸熟妇泻火| 欧美人与拘性视交免费看| 91成人理论电影| 国产私拍福利精品视频二区| 欧美激情免费观看| 久久综合之合合综合久久| 精品无人国产偷自产在线| 国产女无套免费视频| 色狠狠色狠狠综合| 国产精品成人久久| 亚洲免费观看在线视频| 最近中文字幕在线mv视频在线 | 韩国一区二区三区四区| 国产真人无遮挡作爱免费视频| 国产精品一区免费在线观看| 国内外免费激情视频| 国模吧视频一区| 在线视频一区观看| 亚洲动漫在线观看| 国产精品久久久久久久久久久久午夜片 | 欧美在线免费观看| 国产一线二线在线观看| www.欧美免费| 91网页在线观看| 亚洲最新中文字幕| 国产特黄在线| 亚洲精品日韩在线| 午夜av免费观看| 亚洲第一区在线观看| 精品久久人妻av中文字幕| 欧美老人xxxx18| 综合久久中文字幕| 欧洲一区二区三区在线| 免费av网站在线| 欧美日韩免费在线| 国产区在线观看视频| 香蕉影视欧美成人| 日本系列第一页| 午夜精品一区在线观看| 久久久久久久久艹| 亚洲一卡二卡三卡四卡| 久久久久亚洲天堂| 亚洲在线免费播放| 久久久久久久久97| 亚洲伊人色欲综合网| 国产一级二级毛片| 性做久久久久久久免费看| 日本熟女一区二区| 精品久久久久久亚洲国产300| 日韩欧美三级在线观看| 激情久久av一区av二区av三区| 亚洲黄色三级视频| 日韩欧美a级成人黄色| 秋霞av一区二区三区| 欧美日韩综合在线| 国产精品特级毛片一区二区三区| 6080午夜不卡| 国产1区在线观看| 亚洲精品一区二区三区99| 五月婷婷狠狠干| 亚洲欧美在线免费观看| 成年人视频网站在线| 色青青草原桃花久久综合 | 日本一区高清| 欧美一卡在线观看| 黄色三级网站在线观看| 日韩精品在线私人| 触手亚洲一区二区三区| 日韩在线资源网| 欧美xxxx免费虐| 57pao国产成人免费| 成人黄色图片网站| 91嫩草在线视频| 露出调教综合另类| 色一情一区二区三区四区| 91精品福利| 日本精品一区在线观看| 男女男精品视频| 日本少妇一级片| 久久一区二区三区四区| 青青操在线视频观看| 亚洲成人免费视| 亚洲精品无码久久久久| 日韩一级片在线观看| 深夜福利视频一区| 久久久91精品国产一区不卡| 97人人在线视频| 国产精品久久久久久亚洲调教| 久久在线观看| 欧美精品欧美精品系列c| 91精品国产91久久久久久密臀| 国产69精品久久久久999小说| 免费看日韩精品| 亚洲一区二区三区四区av| 国产精品久久久久四虎| 国产无套粉嫩白浆内谢| 欧美日韩一区不卡| 色婷婷激情五月| 久久精品亚洲热| 国产精品粉嫩| 超碰97国产在线| 日韩精品中文字幕第1页| 青青青青草视频| 波多野结衣人妻| 欧美日韩日日夜夜| 污污网站在线免费观看| 日韩在线www| 在线观看欧美日韩电影| 99久久精品久久久久久ai换脸| 国产麻豆一区二区三区精品视频| www污在线观看| 精品在线一区二区| 国产特黄级aaaaa片免| 亚洲一区二区综合| 91在线观看喷潮| 在线视频欧美性高潮| 日本在线高清| 国产欧美丝袜| 欧美精品综合| www.久久久精品| 国产亚洲欧美中文| 国产污污视频在线观看| 精品精品欲导航| 亚洲卡一卡二| 91在线精品视频| 日本不卡电影| 日本www.色| 久久午夜电影网| 亚洲男人第一av| 亚洲精品国产精品国产自| eeuss鲁一区二区三区| 3d蒂法精品啪啪一区二区免费| 日本道不卡免费一区| 精品免费国产一区二区| 久久先锋资源网| 国产精品免费精品一区| 精品一区二区电影| а√在线天堂官网| 极品日韩久久| 日韩图片一区| 六十路息与子猛烈交尾| 麻豆精品久久精品色综合| 欧美最顶级丰满的aⅴ艳星| 久久精品九色| 久操手机在线视频| 成人免费的视频| 国产真人真事毛片| 亚洲精品一区二区三区福利| h片视频在线观看| 精品免费视频123区| 香蕉久久夜色精品| 91成人在线免费视频| 精品视频999| 国产精品实拍| 国产66精品久久久久999小说| 好看的日韩av电影| 制服丝袜在线第一页| 欧美日韩在线看| 麻豆影视在线| 91精品久久久久久久| 欧美另类视频| 秘密基地免费观看完整版中文| 午夜一区二区三区视频| 日韩一二三四| 国产精品igao视频| 久久视频在线| 女教师高潮黄又色视频| 图片区小说区区亚洲影院| 你懂的好爽在线观看| 国产精品永久免费观看| 综合视频在线| 黄色av网址在线观看| 色噜噜狠狠成人中文综合| 婷婷在线视频观看| 91青青草免费观看| 国产情侣一区| 国产不卡在线观看视频| 日韩午夜av一区| 松下纱荣子在线观看| 一区二区三区四区视频在线| 国产电影精品久久禁18| av网站中文字幕| 久久人人爽亚洲精品天堂| 超碰在线亚洲| 制服丝袜综合网| 亚洲国产成人精品视频| 成在在线免费视频| 国产伦精品一区二区三区照片| 蜜乳av另类精品一区二区| av激情在线观看| 亚洲美女又黄又爽在线观看| 亚洲午夜剧场| 免费看一级大黄情大片| 亚洲欧美在线另类| 天堂av网在线| 成人午夜在线观看| 久久精品123| 久久精品人妻一区二区三区| 国产一区二区三区在线免费观看 |