下一篇:使用Nacos作為分布式配置中心
以前那個(gè)Spring Cloud Alibaba系列寫的簡直太差勁了橡庞,項(xiàng)目的依賴亂七八糟,很多無用的依賴也沒有去掉印蔗。
正好最近參加一個(gè)比賽扒最,要寫一個(gè)比較小型的微服務(wù)項(xiàng)目,正好借此機(jī)會(huì)重新學(xué)習(xí)Spring Cloud Alibaba华嘹,并且在這里記錄一下自己遇到的坑吧趣。
可能你要問了:為啥這么想不開,要一個(gè)人開發(fā)一套微服務(wù)系統(tǒng)呢耙厚?
原因是:項(xiàng)目定位(我們隊(duì)設(shè)計(jì)的項(xiàng)目是在有第一群用戶時(shí)峰值QPS就會(huì)達(dá)到2000+而且隨著用戶的增加日均流量和QPS會(huì)增長得非城看欤快,而且未來將會(huì)不斷擴(kuò)展薛躬、不斷接入新功能的大型Web應(yīng)用俯渤。架構(gòu)的橫向擴(kuò)展能力和伸縮性必須非常高)。這就決定了微服務(wù)架構(gòu)是最適合這個(gè)項(xiàng)目的選擇型宝,而且核心業(yè)務(wù)不是很復(fù)雜八匠,仔細(xì)想了想服務(wù)提供者+服務(wù)消費(fèi)者大概也就10~20個(gè),不是很多趴酣,在眾多微服務(wù)架構(gòu)的系統(tǒng)中也算是小型的吧臀叙,而且時(shí)間比較寬裕且不要求100%完成,一個(gè)人開發(fā)沒有特別大的問題价卤。
其實(shí)還是全隊(duì)暫時(shí)只有我可以勝任開發(fā)與整合這套微服務(wù)系統(tǒng)一些基礎(chǔ)組件(如緩存劝萤,SSO,中間件整合等)的工作慎璧,所以……
因?yàn)槭潜荣愑玫捻?xiàng)目床嫌,所以暫時(shí)不放出所有代碼,等比賽結(jié)束后再把代碼上傳到GitHub供大家學(xué)習(xí)和參考胸私。
技術(shù)棧(隨時(shí)更新)
- 架構(gòu):微服務(wù)架構(gòu)厌处,前后端分離
- 服務(wù)風(fēng)格:對(duì)內(nèi)RPC,對(duì)外RESTful
- Java版本:1.8(本來用的是11岁疼,結(jié)果
SkyWalking探針
在Java11下居然不能正常工作阔涉,其他框架也出現(xiàn)了或多或少的問題缆娃,放棄) - 微服務(wù)框架:Spring Cloud
- Web框架:Spring Boot
- RPC框架:Dubbo
- ORM框架:SpringDataMongoDB
- 數(shù)據(jù)庫:MongoDB
- 緩存數(shù)據(jù)庫:Redis
- 消息隊(duì)列:Kafka
- 容器化引擎:Docker
- 服務(wù)注冊(cè)與發(fā)現(xiàn)中心/服務(wù)配置中心:Nacos
- 服務(wù)監(jiān)控:Spring Boot Admin
- 斷路器:Sentinel
- 服務(wù)鏈路追蹤:Skywalking
- 微服務(wù)網(wǎng)關(guān):Spring Cloud Gateway
- 認(rèn)證鑒權(quán)機(jī)制:Spring Cloud Security + OAuth2.0 + JWT
- 日志采集與分析:ELK
搭建Demo項(xiàng)目
為了避免一上來就踩坑,影響到項(xiàng)目進(jìn)度瑰排,我決定先建立一個(gè)Demo項(xiàng)目贯要,把要選擇的框架都試一遍,再以這個(gè)Demo項(xiàng)目為基準(zhǔn)開發(fā)其他基礎(chǔ)框架和業(yè)務(wù)邏輯椭住。其實(shí)現(xiàn)參考了上次寫的那一個(gè)——傳入一個(gè)Message崇渗,返回一個(gè)包含端口的字符串。
先看一下效果吧:
父級(jí)依賴管理項(xiàng)目
還是使用一個(gè)父級(jí)項(xiàng)目來管理所有微服務(wù)的依賴:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
</parent>
<groupId>com.timeline</groupId>
<artifactId>timeline-web</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<!-- 項(xiàng)目依賴版本管理 -->
<!-- Spring Cloud 采用最新的H(Hoxton)版 -->
<spring.cloud.version>Hoxton.RELEASE</spring.cloud.version>
<!-- Spring Cloud Alibaba 采用最新的2.2.0版本 -->
<spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>
<!-- Spring Boot Admin 采用最新的2.2.2版本京郑,以適配項(xiàng)目使用的 Spring Boot 2.2.5版本 -->
<spring.boot.admin.version>2.2.2</spring.boot.admin.version>
</properties>
<modules>
<module>timeline-api</module>
<module>timeline-provider</module>
<module>timeline-consumer</module>
<module>timeline-gateway</module>
<module>timeline-admin-server</module>
</modules>
<dependencyManagement>
<dependencies>
<!-- Spring Cloud -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Spring Cloud Alibaba -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring.cloud.alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 公共API模塊 -->
<dependency>
<groupId>com.timeline</groupId>
<artifactId>timeline-api</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Spring Boot Admin -->
<!-- Server -->
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
<version>${spring.boot.admin.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
相比之前那個(gè)宅广,刪除了所有可以被代替或者被包含的依賴。綜合算下來些举,其實(shí)只有Spring Cloud
跟狱、Spring Cloud Alibaba
的父級(jí)依賴和Spring Boot Admin
。
其實(shí)Spring Boot Admin的依賴可以單獨(dú)放在相應(yīng)的模塊中户魏,但是為了統(tǒng)一管理依賴版本方便兽肤,就放在父級(jí)pom.xml里了。
服務(wù)提供者
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.timeline</groupId>
<artifactId>timeline-web</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>timeline-provider</artifactId>
<dependencies>
<!-- 公共API模塊 -->
<dependency>
<groupId>com.timeline</groupId>
<artifactId>timeline-api</artifactId>
</dependency>
<!-- Dubbo -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-dubbo</artifactId>
</dependency>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Nacos Discovery -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Nacos Config -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- Alibaba Spring Context Support -->
<dependency>
<groupId>com.alibaba.spring</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
</dependencies>
</project>
解釋一下這里為什么沒有spring-boot-admin-starter-client
:
除了使用Spring Boot Admin Client
能把微服務(wù)注冊(cè)到Spring Boot Admin Server
之外绪抛,Spring Boot Admin
在和Spring Cloud
結(jié)合使用時(shí),Spring Boot Admin Server
可以使用每個(gè)微服務(wù)的服務(wù)注冊(cè)信息來注冊(cè)和管理服務(wù)电禀,就不需要再使用Client了幢码,再使用Client就會(huì)把一個(gè)實(shí)例識(shí)別成兩個(gè)了。服務(wù)啟動(dòng)后可以在控制臺(tái)看到尖飞,服務(wù)向Spring Boot Admin
注冊(cè)使用的是向Nacos
注冊(cè)的元數(shù)據(jù):
Spring Boot Admin Server的實(shí)現(xiàn)很簡單症副,就是一個(gè)注解的事,網(wǎng)上也有很多教程政基,就不放出來了贞铣。
(搞不懂為什么要這么實(shí)現(xiàn),跟Nacos和Consul一樣做成獨(dú)立部署沮明、對(duì)應(yīng)用無侵入的不好嘛辕坝?)
bootstrap.yml:
server:
port: 10222
spring:
application:
name: timeline-provider
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
# 注冊(cè)到TIMELINE_DEMO分組
group: TIMELINE_DEMO
config:
server-addr: 127.0.0.1:8848
file-extension: yml
main:
allow-bean-definition-overriding: true
# 服務(wù)健康檢查
management:
endpoints:
web:
exposure:
include: "*"
dubbo:
registry:
address: spring-cloud://localhost:8848
scan:
base-packages: com.timeline.provider.service
# 使用dubbo協(xié)議,端口從20880開始自增以防重復(fù)
protocol:
name: dubbo
port: -1
cloud:
subscribed-services: "*"
timeline:
value: 2
包含了Nacos
的一些信息和Dubbo
的信息荐健。其實(shí)有一部分配置可以遷移到Nacos Config
中酱畅,這樣方便統(tǒng)一管理,比如Dubbo的部分配置江场。咱們之后再實(shí)現(xiàn)這個(gè)纺酸。
業(yè)務(wù)代碼與上一個(gè)差不多,想必用過Dubbo
的各位大佬也很容易看出來是怎么實(shí)現(xiàn)的址否,就不贅述了餐蔬。
服務(wù)消費(fèi)者
pom.xml與服務(wù)提供者一模一樣,就是artifactId
不同。
bootstrap.yml:
server:
port: 10200
spring:
application:
name: timeline-consumer
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
# 注冊(cè)到TIMELINE_DEMO分組
group: TIMELINE_DEMO
config:
server-addr: 127.0.0.1:8848
file-extension: yml
main:
allow-bean-definition-overriding: true
# 服務(wù)健康檢查
management:
endpoints:
web:
exposure:
include: "*"
dubbo:
registry:
# 注冊(cè)到 Spring Cloud 注冊(cè)中心(Nacos)
address: spring-cloud://localhost:8848
scan:
base-packages: com.timeline.consumer.service
# 使用dubbo協(xié)議樊诺,端口號(hào)為-1表示從20880開始自增端口仗考,以防重復(fù)
protocol:
name: dubbo
port: -1
cloud:
# 訂閱 timeline-provider 服務(wù)提供者
subscribed-services: timeline-provider
主要的不同點(diǎn)就是subscribed-services
,聲明了訂閱timeline-provider
啄骇。
微服務(wù)網(wǎng)關(guān)
據(jù)說這個(gè)東西在真正的微服務(wù)系統(tǒng)中不常用痴鳄,好像是因?yàn)橄窬W(wǎng)關(guān)這樣流量大的中心節(jié)點(diǎn)很容易全部掛掉而導(dǎo)致單點(diǎn)故障。但是這次為了系統(tǒng)的完善和前端調(diào)用方便缸夹,我們還是把它實(shí)現(xiàn)一下吧痪寻,反正又不會(huì)真的上線啦。
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>timeline-web</artifactId>
<groupId>com.timeline</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>timeline-gateway</artifactId>
<dependencies>
<!-- Spring Cloud Gateway -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Nacos Discovery -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
</project>
bootstrap.yml:
server:
port: 10100
spring:
application:
name: timeline-gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
# 注冊(cè)到TIMELINE_DEMO分組
group: TIMELINE_DEMO
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true
routes:
- id: timeline-consumer
# lb表示開啟負(fù)載均衡虽惭,timeline-consumer是應(yīng)用名稱
uri: lb://timeline-consumer
predicates:
- Path=/timeline/**
management:
endpoints:
web:
exposure:
include: "*"
主要是配置了路由信息橡类,將所有/timeline/
開頭的請(qǐng)求都轉(zhuǎn)發(fā)到timeline-consumer
里去。
同樣芽唇,有很多配置是可以移動(dòng)到Nacos Config
中的顾画。
使用docker-compose搭建SkyWalking環(huán)境
“鏈路追蹤”是微服務(wù)中比較重要的一個(gè)組成部分,但是在用過了Cat
和Zipkin
之后匆笤,我發(fā)現(xiàn)還是SkyWalking
最好:
- SkyWalking使用javaagent埋點(diǎn)研侣,對(duì)應(yīng)用無任何侵入。
- Zipkin雖然能與Spring Cloud無縫集成炮捧,但是在Dubbo當(dāng)前版本
2.7.4.1
上不能正常使用庶诡,按照能找到的資料里的方法進(jìn)行配置,會(huì)導(dǎo)致服務(wù)消費(fèi)者無法啟動(dòng)(可能是我使用的方法不對(duì)咆课?)末誓。 - Cat雖然表現(xiàn)比Zipkin好一些,但是與Spring Cloud配合不佳书蚪、參考資料太少喇澡。
- SkyWalking生成的報(bào)表比較豐富,對(duì)服務(wù)狀態(tài)殊校、調(diào)用狀態(tài)的展示比較清楚晴玖。
- 據(jù)說跟Cat一樣是國人開發(fā)的,666666为流!
docker-compose啟動(dòng)單機(jī)SkyWalking
單獨(dú)搭建SkyWalking
比較繁瑣窜醉,所以還是使用docker
來搭建。
SkyWalking
官方已經(jīng)給出了docker-compose
文件供我們方便地搭建環(huán)境艺谆,我們使用官方的文件就可以了榨惰。GitHub地址:https://github.com/apache/skywalking-docker
具體步驟也很簡單:
git clone git@github.com:apache/skywalking-docker.git # 需要配置SSH
cd skywalking-docker/6/6.6/compose-es7
docker-compose -f docker-compose.yml up -d
啟動(dòng)完畢后就可以在localhost:8080
看到SkyWalking
控制臺(tái)了。
在IDEA中部署SkyWalking應(yīng)用探針
打開右上角的Edit Configurations...
静汤,在每個(gè)服務(wù)的VM options
里都加上這么一段:
-javaagent:path to/skywalking-agent.jar
-Dskywalking.agent.service_name=你的應(yīng)用名稱
-Dskywalking.collector.backend_service=SkyWalking OAP的地址琅催,我這里是localhost:11800
例如:
然后啟動(dòng)應(yīng)用居凶,可以發(fā)現(xiàn)控制臺(tái)輸出了SkyWalking Agent
相關(guān)的內(nèi)容:
使用SkyWalking監(jiān)測(cè)服務(wù)調(diào)用
回到瀏覽器中刷新幾次,就可以在SkyWalking中看到服務(wù)調(diào)用的信息了(可能因?yàn)槲译娔X比較渣藤抡,導(dǎo)致它反應(yīng)比較慢侠碧,如果沒反應(yīng)可以等一會(huì)):
可以發(fā)現(xiàn),除了我們直接進(jìn)行的HTTP調(diào)用缠黍,Dubbo
的RPC調(diào)用也可以正常被檢測(cè)到弄兜。不過為啥Spring Cloud Gateway
沒被寫進(jìn)來呢(我調(diào)用的時(shí)候是通過了Gateway的)?
SkyWalking還有一個(gè)很有意思的功能:拓?fù)鋱D瓷式。因?yàn)镾kyWalking把健康檢測(cè)也算成用戶對(duì)服務(wù)的調(diào)用了替饿,所以User
和timeline-provider
之間也有線,但實(shí)際上是沒有直接調(diào)用的贸典。這里服務(wù)太少了视卢,這個(gè)圖看起來沒啥意思,不知為何Spring Cloud Gateway沒有檢測(cè)到(雖然其實(shí)它是不是被檢測(cè)好像并沒有太大的意義廊驼,因?yàn)镚ateway一旦掛了据过,那無論請(qǐng)求什么都是500了):
踩坑記錄
坑1:項(xiàng)目啟動(dòng)時(shí)拋出"Connection Refused"異常
描述:在啟動(dòng)項(xiàng)目時(shí),莫名其妙地拋出了Conncetion Refused
的異常妒挎,但是功能一切正常绳锅。
解決:這是Dubbo
一些莫名其妙的默認(rèn)行為的問題。通過斷點(diǎn)調(diào)試后酝掩,發(fā)現(xiàn)如果不配置dubbo.registry.address: spring-cloud://localhost
鳞芙,在讀取配置的時(shí)候就會(huì)自動(dòng)加上這一條,并且端口指明為9090
(咱也不知道為啥要這么干庸队,在我印象中除了Eureka默認(rèn)是需要自己指定以外,好像沒有什么注冊(cè)中心默認(rèn)端口是9090)闯割,然后在啟動(dòng)時(shí)會(huì)自動(dòng)連接localhost:9090
彻消。因?yàn)槲覀兊淖?cè)中心是跑在8848
端口上的,9090沒有任何東西宙拉,所以自然會(huì)Connection Refused
宾尚。解決方法就是配置dubbo.registry.address: spring-cloud://localhost:8848
,如下圖谢澈。這樣配置好就可以了煌贴。
并且,由于這樣配置的含義是“把Dubbo的注冊(cè)信息掛載到Spring Cloud的注冊(cè)中心(在這里就是Nacos)”锥忿,所以Nacos服務(wù)列表里顯示的信息就不包含Dubbo的元數(shù)據(jù)了牛郑,更加精簡。