-
微服務架構
你的整個應用程式將會被拆分成一個個功能獨立的子系統,獨立執行,系統與系統之間透過RPC介面通訊。這樣這些系統之間的耦合度大大降低,你的系統將非常容易擴充套件,團隊協作效率提升了N個檔次。這種架構透過眼下流行的SpringBoot和阿裡巴巴弔炸天的Dubbo框架來實現。
-
容器化部署
你的各個微服務將採用目前處於浪潮之巔的Docker來實現容器化部署,避免一切因環境引起的各種問題,讓你們團隊的全部精力集中在業務開發上。
-
自動化構建
專案被微服務化後,各個服務之間的關係錯中複雜,打包構建的工作量相當可怕。不過沒關係,本文將藉助Jenkins,幫助你一鍵自動化部署,從此你便告別了加班。
-
系統之間的耦合度大大降低,可以獨立開發、獨立部署、獨立測試,系統與系統之間的邊界非常明確,排錯也變得相當容易,開發效率大大提升。
-
系統之間的耦合度降低,從而系統更易於擴充套件。我們可以針對性地擴充套件某些服務。假設這個商城要搞一次大促,下單量可能會大大提升,因此我們可以針對性地提升訂單系統、產品系統的節點數量,而對於後臺管理系統、資料分析系統而言,節點數量維持原有水平即可。
-
服務的復用性更高。比如,當我們將使用者系統作為單獨的服務後,該公司所有的產品都可以使用該系統作為使用者系統,無需重覆開發。
-
基於SpringBoot快速開發
我們將選擇目前熱度很高的SpringBoot,最大限度地降低配置複雜度,把大量的精力投入到我們的業務開發中來。
-
基於Dubbo的微服務化
我們會使用阿裡巴巴的開源框架Dubbo,將我們的系統拆分成多個獨立的微服務,然後用Dubbo來管理所有服務的釋出和取用。有了Dubbo之後,呼叫遠端服務就像呼叫一個本地函式一樣簡單,Dubbo會幫我們完成遠端呼叫背後所需要的一切。
-
基於Docker的容器化部署
由於使用了微服務架構後,我們的系統將會由很多子系統構成。為了達到多個系統之間環境隔離的目的,我們可以將它們部署在多臺伺服器上,可這樣的成本會比較高,而且每臺伺服器的效能可能都沒有充分利用起來。所以我們很自然地想到了虛擬機器,在同一臺伺服器上執行多個虛擬機器,從而實現環境的隔離,每個虛擬機器上執行獨立的服務。然而虛擬機器的隔離成本依舊很高,因為它需要佔用伺服器較多的硬體資源和軟體資源。所以,在微服務結構下,要實現服務環境的隔離,Docker是最佳選擇。它比虛擬機器更加輕量級,佔用資源較少,而且能夠實現快速部署。
-
基於Jenkins的自動化構建
當我們採用了微服務架構後,我們會發現這樣一個問題。整個系統由許許多多的服務構成,這些服務都需要執行在單獨的容器中,那麼每次釋出的複雜度將非常高。首先你要搞清楚這些服務之間的依賴關係、啟動的先後順序,然後再將多個子系統挨個編譯、打包、釋出。這些操作技術難度低,卻又容易出錯。那麼有什麼工具能夠幫助我們解決這些問題呢?答案就是——Jenkins。 它是一款自動化構建的工具,簡單的來說,就是我們只需要在它的介面上按一個按鈕,就可以實現上述一系列複雜的過程。
-
產品管理,產品的增刪改查。
-
訂單管理,訂單的增刪改查、購物車功能。
-
使用者管理,使用者的登入、註冊、許可權管理、收貨地址等等。
-
資料分析,提供對本系統資料分析的功能。
-
建立一個Maven Project,命名為“Gaoxi”。這個Project由多個Module構成,每個Module對應著“微服務”的一個子系統,可獨立執行,是一個獨立的專案。 這也是目前主流的專案組織形式,即多模組專案。
-
在Gaoxi這個專案下建立各個子模組,每個自模組都是一個獨立的SpringBoot專案:
Gaoxi-User 使用者服務
Gaoxi-Order 訂單服務
Gaoxi-Product 產品服務
Gaoxi-Analysis 資料分析服務
Gaoxi-Controller 本系統的控制層,和以往三層結構中的Controller層的作用一樣,都是用作請求排程,只不過在微服務架構中,我們將它抽象成一個單獨的系統,可以獨立執行。
Gaoxi-Common-Service-Facade 它處於本系統的最底層,被所有模組依賴,一些公用的類庫都放在這裡。
Gaoxi-Redis 我們將Redis封裝成一個單獨的服務,執行在獨立的容器中,當哪一個模組需要使用Redis的時候,僅需要引入該服務即可,就免去了各種繁瑣的、重覆的配置。而這些配置均在Gaoxi-Redis系統中完成了。
<groupId>com.gaoxigroupId>
<artifactId>gaoxiartifactId>
<version>0.0.1-SNAPSHOTversion>
<groupId>com.gaoxigroupId>
<artifactId>gaoxi-common-service-facadeartifactId>
<version>0.0.1version>
<packaging>jarpackaging>
<groupId>com.gaoxigroupId>
<artifactId>gaoxi-userartifactId>
<version>0.0.1-SNAPSHOTversion>
<packaging>warpackaging>
<modules>
<module>Gaoxi-Analysismodule>
<module>Gaoxi-Ordermodule>
<module>Gaoxi-Productmodule>
<module>Gaoxi-Usermodule>
<module>Gaoxi-Redismodule>
<module>Gaoxi-Controllermodule>
<module>Gaoxi-Common-Service-Facademodule>
modules>
<parent>
<groupId>com.gaoxigroupId>
<artifactId>gaoxiartifactId>
<version>0.0.1-SNAPSHOTversion>
<relativePath>../pom.xmlrelativePath>
parent>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>1.3.1version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<scope>runtimescope>
dependency>
<dependency>
<groupId>io.dubbo.springbootgroupId>
<artifactId>spring-boot-starter-dubboartifactId>
<version>1.0.0version>
dependency>
<dependency>
<groupId>com.gaoxigroupId>
<artifactId>gaoxi-common-service-facadeartifactId>
<version>0.0.1version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-aopartifactId>
dependency>
<dependency>
<groupId>com.google.guavagroupId>
<artifactId>guavaartifactId>
<version>23.3-jreversion>
dependency>
dependencies>}}}
當父模組的pom中配置了公用依賴後,子模組的pom檔案將非常簡潔,如下所示:
{{{<groupId>com.gaoxigroupId>
<artifactId>gaoxi-userartifactId>
<version>0.0.1-SNAPSHOTversion>
<packaging>warpackaging>
<name>gaoxi-username>
<parent>
<groupId>com.gaoxigroupId>
<artifactId>gaoxiartifactId>
<version>0.0.1-SNAPSHOTversion>
<relativePath>../pom.xmlrelativePath>
parent>
docker pull chaimm/tomcat:1.1
-
gaoxi-user
-
gaoxi-product
-
gaoxi-order
-
gaoxi-analysis
-
gaoxi-controller
-
gaoxi-redis
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埠對映到宿主機的8082埠上
-
-v:指定容器資料捲的對映
xxx:yyy 表示將容器yyy目錄對映到宿主機的xxx目錄上,從而訪問宿主機的xxx目錄就相當於訪問容器的yyy目錄。
-
chaimm/tomcat:1.1:表示容器所對應的映象。
docker pull chaimm/zookeeper-dubbo:1.0
docker run --name zookeeper-debug -p 2182:2181 -p 10000:8080 chaimm/zookeeper-dubbo:1.0
-
-p 2182:2181:將容器的2181埠對映到宿主機的2182埠上,該埠是ZooKeeper的埠號。
-
-p 10000:8080:將容器的8080埠對映到宿主機的10000埠上,該埠是Dubbo-Admin所在Tomcat的埠號。
<dependency>
<groupId>io.dubbo.springbootgroupId>
<artifactId>spring-boot-starter-dubboartifactId>
<version>1.0.0version>
dependency>
public interface UserService {
public UserEntity login(LoginReq loginReq);
}
@Service(version = "1.0.0")
public class UserServiceImpl implements UserService {
@Override
public UserEntity login(LoginReq loginReq) {
// 具體的實現程式碼
}
}
spring.dubbo.application.name=user-provider # 本服務的名稱
spring.dubbo.registry.address=zookeeper://IP:2182 # ZooKeeper所在伺服器的IP和埠號
spring.dubbo.protocol.name=dubbo # RPC通訊所採用的協議
spring.dubbo.protocol.port=20883 # 本服務對外暴露的埠號
spring.dubbo.scan=com.gaoxi.user.service # 服務實現類所在的路徑
@RestController
public class UserControllerImpl implements UserController {
@Reference(version = "1.0.0")
private UserService userService;
@Override
public Result login(LoginReq loginReq, HttpServletResponse httpRsp) {
// 登入鑒權
UserEntity userEntity = userService.login(loginReq);
}
}
spring.dubbo.application.name=controller-consumer # 本服務的名稱
spring.dubbo.registry.address=zookeeper://IP:2182 # zookeeper所在伺服器的IP和埠號
spring.dubbo.scan=com.gaoxi # 取用服務的路徑
docker pull docker.io/jenkins/jenkins
docker run --name jenkins -p 10080:8080 docker.io/jenkins/jenkins
<plugin>
<groupId>org.codehaus.cargogroupId>
<artifactId>cargo-maven2-pluginartifactId>
<version>1.6.5version>
<configuration>
<container>
<containerId>tomcat8xcontainerId>
<type>remotetype>
container>
<configuration>
<type>runtimetype>
<cargo.remote.username>Tomcat的使用者名稱cargo.remote.username>
<cargo.remote.password>Tomcat的密碼cargo.remote.password>
configuration>
configuration>
<executions>
<execution>
<phase>deployphase>
<goals>
<goal>redeploygoal>
goals>
execution>
executions>
plugin>
<profiles>
<profile>
<id>devid>
<properties>
<profileActive>devprofileActive>
properties>
<activation>
<activeByDefault>trueactiveByDefault>
activation>
profile>
<profile>
<id>testid>
<properties>
<profileActive>testprofileActive>
properties>
profile>
<profile>
<id>prodid>
<properties>
<profileActive>prodprofileActive>
properties>
profile>
profiles>
<resources>
<resource>
<directory>src/main/resourcesdirectory>
<filtering>truefiltering>
<excludes>
<exclude>application.propertiesexclude>
<exclude>application-dev.propertiesexclude>
<exclude>application-test.propertiesexclude>
<exclude>application-prod.propertiesexclude>
excludes>
resource>
<resource>
<directory>src/main/resourcesdirectory>
<filtering>truefiltering>
<includes>
<include>application.propertiesinclude>
<include>application-${profileActive}.propertiesinclude>
includes>
resource>
resources>
<plugin>
<artifactId>maven-resources-pluginartifactId>
<version>3.0.2version>
<configuration>
<delimiters>
<delimiter>@delimiter>
delimiters>
<useDefaultDelimiters>falseuseDefaultDelimiters>
configuration>
plugin>
spring.profiles.active=@profileActive@
-
在Gaoxi-User系統中實現登入的業務邏輯,併發布成RPC服務。
-
在Gaoxi-Controller中遠端呼叫登入服務,並向前端提供登入的REST介面。
public interface UserService {
public UserEntity login(LoginReq loginReq);
}
@Service(version = "1.0.0")
@org.springframework.stereotype.Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDAO userDAO;
@Override
public UserEntity login(LoginReq loginReq) {
// 校驗引數
checkParam(loginReq);
// 建立使用者查詢請求
UserQueryReq userQueryReq = buildUserQueryReq(loginReq);
// 查詢使用者
List userEntityList = userDAO.findUsers(userQueryReq);
// 查詢失敗
if (CollectionUtils.isEmpty(userEntityList)) {
throw new CommonBizException(ExpCodeEnum.LOGIN_FAIL);
}
// 查詢成功
return userEntityList.get(0);
}
}
@RestController
public class UserControllerImpl implements UserController {
@Reference(version = "1.0.0")
private UserService userService;
@Override
public Result login(LoginReq loginReq, HttpServletResponse httpRsp) {
// 登入鑒權
UserEntity userEntity = userService.login(loginReq);
// 登入成功
doLoginSuccess(userEntity, httpRsp);
return Result.newSuccessResult();
}
}
-
微服務架構
我們藉助於SpringBoot和Dubbo實現了微服務架構。微服務架構的理念就是將一個原本龐大、複雜的系統,按照業務功能拆分成一個個具有獨立功能、可以獨立執行的子系統,系統之間若有依賴,則透過RPC介面通訊。從而最大限度地降低了系統之間的耦合度,從而更加易於擴充套件、更加易於維護。
-
容器化部署
我們藉助於Docker實現了容器化部署。容器能夠幫助我們遮蔽不同環境下的配置問題,使得我們只需要有一個Dockerfile檔案,就可以處處執行。和虛擬機器一樣,Docker也擁有環境隔離的能力,但比虛擬機器更加輕量級,由於每個容器僅僅是一條行程,因此它可以達到秒級的啟動速度。
-
自動化構建
我們藉助於Jenkins實現了所有專案的自動化構建與部署。我們只需要點選“立即構建”這個按鈕,Jenkins就可以幫助我們梳理好錯綜複雜的專案依賴關係,準確無誤地完成構建,並將war包傳送到相應的web容器中。在啟動的過程中,Dubbo會掃描當前專案所需要釋出和取用的服務,將所需要釋出的服務釋出到ZooKeeper上,並向ZooKeeper訂閱所需的服務。 有了Jenkins之後,這一切都是自動化完成。也許你並沒有太強烈地感受到Jenkins所帶來的便利。但是你想一想,對於一個具有錯綜複雜的依賴關係的微服務系統而言,如果每個服務的構建都需要你手動完成的話,你很快就會崩潰,你大把的時間將會投入在無聊但又容易出錯的服務構建上。而Jenkins的出現能讓這一切自動化完成。