教練拷获,我想……
- 什么是Spring Cloud?
- Spring Cloud和Dubbo有什么不同减细?
- 怎么搭建Spring Cloud匆瓜?
1. 什么是Spring Cloud?
“Spring Cloud是一系列框架的有序集合未蝌。它利用Spring Boot的開(kāi)發(fā)便利性巧妙地簡(jiǎn)化了分布式系統(tǒng)基礎(chǔ)設(shè)施的開(kāi)發(fā)驮吱,如服務(wù)發(fā)現(xiàn)注冊(cè)、配置中心萧吠、消息總線(xiàn)左冬、負(fù)載均衡、斷路器纸型、數(shù)據(jù)監(jiān)控等拇砰,都可以用Spring Boot的開(kāi)發(fā)風(fēng)格做到一鍵啟動(dòng)和部署。Spring Cloud并沒(méi)有重復(fù)制造輪子狰腌,它只是將目前各家公司開(kāi)發(fā)的比較成熟毕匀、經(jīng)得起實(shí)際考驗(yàn)的服務(wù)框架組合起來(lái),通過(guò)Spring Boot風(fēng)格進(jìn)行再封裝屏蔽掉了復(fù)雜的配置和實(shí)現(xiàn)原理癌别,最終給開(kāi)發(fā)者留出了一套簡(jiǎn)單易懂、易部署和易維護(hù)的分布式系統(tǒng)開(kāi)發(fā)工具包蹋笼≌菇悖”——度娘百科
簡(jiǎn)單來(lái)講,Spring Cloud就是一個(gè)基于Spring Boot架設(shè)的可以用來(lái)開(kāi)發(fā)分布式微服務(wù)應(yīng)用的框架剖毯。
但是圾笨,它不僅僅可以用來(lái)開(kāi)發(fā)微服務(wù)。
2. Spring Cloud和Dubbo有什么不同逊谋?
目前說(shuō)到Spring Cloud就不得不提到Alibaba開(kāi)源擂达,最后貢獻(xiàn)給Apache托管維護(hù)的分布式框架Dubbo,他們二者在目的上十分相近胶滋,但是實(shí)現(xiàn)原理以及開(kāi)發(fā)方式又有許多不同板鬓。比如Dubbo是RPC悲敷,Spring Cloud是Http。
如果從理論上說(shuō)俭令,Dubbo的數(shù)據(jù)傳輸方式為二進(jìn)制碼后德,而Spring Cloud則是序列化的Json字符串,所以效率后者肯定比前者低一些抄腔,但是曾有人測(cè)試這個(gè)差距在現(xiàn)代設(shè)備上已經(jīng)微乎其微瓢湃,所以我們可以忽略。
從開(kāi)發(fā)角度說(shuō)赫蛇,Dubbo的jar依賴(lài)過(guò)于繁雜绵患,版本也是十分混亂,可能是由于之前斷更了許多年悟耘,之后又交給了Apache管理落蝙,所以你在網(wǎng)上看到的關(guān)于Dubbo的教程,依賴(lài)一會(huì)兒是Alibaba的作煌,一會(huì)兒是Apache的掘殴,出現(xiàn)沖突十分普遍。相反粟誓,Spring Cloud的依賴(lài)則十分統(tǒng)一奏寨,即使版本繁雜,只要你指定了一個(gè)版本鹰服,也基本上不會(huì)出現(xiàn)jar包依賴(lài)沖突的問(wèn)題病瞳。
撇開(kāi)依賴(lài)問(wèn)題,從開(kāi)發(fā)角度說(shuō)悲酷,兩者各有優(yōu)勢(shì)套菜,但總體上Dubbo難度略高一丟丟。
3. 怎么搭建Spring Cloud设易?
3.1 創(chuàng)建一個(gè)注冊(cè)中心
首先我用的IDE是idea逗柴,點(diǎn)File > new > Project...
,彈出窗口后顿肺,左側(cè)選擇Spring Initializr
(Spring初始化)戏溺,右側(cè)選擇JDK,Next
屠尊。
下一頁(yè)填寫(xiě)項(xiàng)目相關(guān)信息旷祸,創(chuàng)建一個(gè)名為spring-cloud-demo
的,空的Spring Boot項(xiàng)目讼昆,作為整個(gè)工程的父項(xiàng)目托享。你可以刪掉src,因?yàn)楦疙?xiàng)目只是作為一個(gè)殼,留下一個(gè)pom.xml
即可闰围。
為什么我不之間創(chuàng)建Maven項(xiàng)目呢赃绊?很簡(jiǎn)單,懶辫诅。因?yàn)槲覀兊母疙?xiàng)目要以Spring Boot為父項(xiàng)目凭戴,方便后續(xù)開(kāi)發(fā)。
另外提一下炕矮,我這里創(chuàng)建的Spring Boot版本號(hào)為2.1.6.RELEASE
么夫,你可以選用其他版本,但后續(xù)其他引用版本請(qǐng)自行更改肤视。
現(xiàn)在右鍵剛才創(chuàng)建的項(xiàng)目档痪,new一個(gè)Module
,還是選擇Spring Initializr
邢滑,名字叫scdemo-server
腐螟,作為服務(wù)的注冊(cè)中心。
scdemo-server
請(qǐng)修改pom.xml
困后,將parent修改為剛才創(chuàng)建的spring-cloud-demo
乐纸。
例如我的如下:
<parent>
<groupId>com.yq</groupId>
<artifactId>scdemo-cloud-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
請(qǐng)確保你填寫(xiě)的groupId和其他信息都符合自己幾分鐘前命名的。
請(qǐng)?jiān)?code>dependencies標(biāo)簽下引入eureka的pom摇予,例如:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
這時(shí)你的maven可能會(huì)報(bào)錯(cuò)汽绢,因?yàn)槲覀儧](méi)有指定版本,這里之所以創(chuàng)建一個(gè)父項(xiàng)目侧戴,就是為了統(tǒng)一一個(gè)全局的版本號(hào)宁昭,避免因?yàn)楦鱾€(gè)項(xiàng)目引入不同版本的jar包引發(fā)麻煩。
別著急酗宋,打開(kāi)剛才父項(xiàng)目的pom.xml
积仗,添加一個(gè)dependencyManagement
,這個(gè)標(biāo)簽屬于project
的直接子標(biāo)簽蜕猫,請(qǐng)不要寫(xiě)錯(cuò)了位置寂曹。并且dependencyManagement
中只是聲明全局引用jar包的版本,并非真的把jar引入了項(xiàng)目回右。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
在這里我們限定了spring-cloud全局版本號(hào)為Greenwich.RELEASE
稀颁,即未來(lái)子項(xiàng)目中只要不特意聲明<version>
,都會(huì)默認(rèn)遵循此版本楣黍。
如此一來(lái),應(yīng)該可以成功導(dǎo)入spring-cloud-starter-netflix-eureka-server
這個(gè)jar包了棱烂,并且從idea的MavenProjects窗口可以看到其版本號(hào)為2.1.0.RELEASE
租漂。
此時(shí),請(qǐng)?jiān)谀愕腟pring Boot啟動(dòng)入口類(lèi)上加上
@EnableEurekaServer
注解,以啟動(dòng)注冊(cè)中心服務(wù)哩治。打開(kāi)
application.yml
(如果你沒(méi)有這個(gè)文件秃踩,請(qǐng)?jiān)?code>resources目錄下新建這個(gè)文件,當(dāng)然你也可以自行改成properties格式)文件业筏,添加如下配置:
server:
port: 8761
spring:
application:
name: spring-cloud-server
eureka:
instance:
hostname: 127.0.0.1
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
run你的程序憔杨,然后瀏覽器訪(fǎng)問(wèn)http://127.0.0.1:8761/就可以看到注冊(cè)中心的頁(yè)面,這個(gè)頁(yè)面顯示了當(dāng)前已注冊(cè)服務(wù)和其他一些系統(tǒng)信息蒜胖,當(dāng)然消别,現(xiàn)在啥服務(wù)都沒(méi)有。
這就相當(dāng)于Dubbo你啟動(dòng)了一個(gè)Zookeeper或者Nacos作為注冊(cè)中心一樣台谢。
3.2 創(chuàng)建一個(gè)服務(wù)提供者Provider
為了統(tǒng)一消費(fèi)者和提供者的接口定義寻狂,避免不必要麻煩的發(fā)生概率,這里我們寫(xiě)controller時(shí)會(huì)才用接口和實(shí)現(xiàn)的方式朋沮,將controller作為實(shí)現(xiàn)類(lèi)蛇券,與接口分開(kāi)打包,這樣在需要調(diào)用的地方會(huì)方便很多樊拓。你現(xiàn)在看不懂這一段纠亚,很正常。
再到父工程下new一個(gè)Module
筋夏,名字就叫scdemo-api
吧蒂胞,同樣請(qǐng)將父項(xiàng)目指向spring-cloud-demo
。
在pom.xml
中添加如下依賴(lài):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
其中spring-boot-starter-web
是引入了web框架叁丧,因?yàn)槲覀兊姆?wù)提供者對(duì)外提供了http服務(wù)啤誊。spring-cloud-starter-openfeign
是一個(gè)訪(fǎng)問(wèn)http請(qǐng)求的客戶(hù)端,原屬于Netflix拥娄,后被整合到Spring Cloud集合下蚊锹,衍生出openfeign,其實(shí)是一個(gè)東西稚瘾。
在src下創(chuàng)建一個(gè)名為ITestCloudService
的接口牡昆,形如:
@FeignClient(name = "spring-cloud-provider", path = "/api/v1/test")
public interface ITestCloudService {
@GetMapping("/t")
Map<String, Object> testFnFeign();
}
其中@FeignClient
聲明這是一個(gè)Feign客戶(hù)端可訪(fǎng)問(wèn)的接口,參數(shù)name指的是注冊(cè)中心服務(wù)的名稱(chēng)摊欠,path指的是接口映射的前綴丢烘,類(lèi)似于RequestMapping。
然后刪除這個(gè)項(xiàng)目的啟動(dòng)類(lèi)些椒,因?yàn)檫@個(gè)項(xiàng)目只作為接口依賴(lài)播瞳,并不會(huì)單獨(dú)啟動(dòng)服務(wù)。同時(shí)請(qǐng)修改父項(xiàng)目的pom.xml
免糕,將這個(gè)接口聲明依賴(lài)添加到全局版本聲明中赢乓,至此你的父項(xiàng)目應(yīng)該包含如下定義:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.yq</groupId>
<artifactId>scdemo-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</dependencyManagement>
緊接著忧侧,再新建一個(gè)Module
,這里就叫scdemo-provider
吧牌芋。
這個(gè)項(xiàng)目的pom.xml
也請(qǐng)修改父項(xiàng)目蚓炬,并且引入如下依賴(lài):
<dependency>
<groupId>com.yq</groupId>
<artifactId>scdemo-api</artifactId>
</dependency>
<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>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
請(qǐng)?jiān)趕rc下新建一個(gè)TestCloudServiceImpl
類(lèi),實(shí)現(xiàn)ITestCloudService
接口躺屁,如果你無(wú)法importITestCloudService
接口肯夏,請(qǐng)檢查pom依賴(lài)是否正確。
TestCloudServiceImpl
類(lèi)內(nèi)容形如:
@RestController
@RequestMapping("/api/v1/test")
public class TestCloudServiceImpl implements ITestCloudService {
@Value("${server.port}")
private String serverPort;
@Value("${eureka.instance.appname}")
private String eurekaInstanceAppName;
@Override
public Map<String, Object> testFnFeign() {
Map<String, Object> map = new HashMap<>();
map.put("serverPort", serverPort);
map.put("eurekaInstanceAppName", eurekaInstanceAppName);
map.put("me", "provider");
return map;
}
}
這里注入的2個(gè)Value是為了鑒別測(cè)試結(jié)果犀暑,接口的內(nèi)容也很簡(jiǎn)單驯击,主要說(shuō)一下Spring的@RestController
和@RequestMapping
這兩個(gè)注解,并非只能寫(xiě)到controller上母怜。聯(lián)系上剛才寫(xiě)的Interface余耽,這個(gè)‘Controller’的接口映射是@RequestMapping("/api/v1/test")
+@GetMapping("/t")
,也就是/api/v1/test/t
苹熏。
接下來(lái)是provider的配置文件碟贾,application.yml
:
server:
port: 8762
spring:
application:
name: spring-cloud-provider
eureka:
instance:
appname: ${spring.application.name}
client:
service-url:
defaultZone: http://127.0.0.1:8761/eureka/
請(qǐng)啟動(dòng)Spring Boot的程序入口,然后訪(fǎng)問(wèn)http://127.0.0.1:8762/api/v1/test/t
如果沒(méi)問(wèn)題轨域,你將拿到一個(gè)返回結(jié)果:
{"me":"provider","serverPort":"8762","eurekaInstanceAppName":"spring-cloud-provider"}
至此服務(wù)提供者已經(jīng)部署完成袱耽。
3.3 創(chuàng)建一個(gè)消費(fèi)者consumer
重復(fù)內(nèi)容就不多說(shuō)了,修改pom.xml
符項(xiàng)目干发,添加如下依賴(lài):
<dependency>
<groupId>com.yq</groupId>
<artifactId>scdemo-api</artifactId>
</dependency>
<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>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
可以看到朱巨,這里也引入了scdemo-api
這個(gè)依賴(lài),但并不是為了去實(shí)現(xiàn)它枉长,而是調(diào)用它冀续。
消費(fèi)者配置文件application.xml
如下:
server:
port: 8763
spring:
application:
name: spring-cloud-consumer
eureka:
instance:
appname: ${spring.application.name}
client:
service-url:
defaultZone: http://127.0.0.1:8761/eureka/
請(qǐng)注意,消費(fèi)者調(diào)用服務(wù)有兩種可選方式必峰。
第一種是RestTemplate
洪唐,如需要使用,請(qǐng)?jiān)赟pring Boot的配置類(lèi)中注入Bean吼蚁,可參考我的注入:
@Bean
@LoadBalanced // 開(kāi)啟負(fù)載均衡
public RestOperations restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
第二種是使用Feign客戶(hù)端凭需,請(qǐng)?jiān)赟pring Boot的配置類(lèi)上加上@EnableFeignClients(basePackages = "com.yq.scdemo.api.*")
,用來(lái)啟用Feign客戶(hù)端肝匆,其中basePackages指明掃描API接口的包名粒蜈,即之前在那個(gè)scdemo-api
項(xiàng)目下創(chuàng)建的interface的包名。
因?yàn)镾pring Boot默認(rèn)掃描本項(xiàng)目啟動(dòng)類(lèi)目錄下的注解旗国,如果API interface的包名在此之外枯怖,需要額外指定。
本示例同時(shí)演示了2種客戶(hù)端工具能曾,實(shí)際情況請(qǐng)自行選擇嫁怀。
最后來(lái)編寫(xiě)消費(fèi)者的測(cè)試controller:
@RestController
public class TestConsumerController {
@Autowired
private RestOperations restTemplate;
@Autowired
private ITestCloudService testCloudService;
@GetMapping("/rest")
public Map testFnRest() {
Map map = restTemplate.getForObject("http://spring-cloud-provider/api/v1/test/t", Map.class);
map.put("me", "consumer");
return map;
}
@GetMapping("/feign")
public Map testFnFeign() {
Map<String, Object> map = testCloudService.testFnFeign();
map.put("me", "consumer");
return map;
}
}
/rest
和/feign
分別演示了兩種客戶(hù)端的調(diào)用方式设捐。
其中rest的調(diào)用是寫(xiě)死的url,不方便維護(hù)塘淑,url中spring-cloud-provider為服務(wù)名,后面是接口地址蚂斤。而feign的調(diào)用方式在我們提取出公共API Interface之后存捺,極其類(lèi)似Dubbo的開(kāi)發(fā)模式,消費(fèi)端無(wú)需關(guān)心接口地址和其他聲明曙蒸,因?yàn)檫@些都是服務(wù)端開(kāi)發(fā)controller時(shí)順便定義的捌治。
接下來(lái)啟動(dòng)消費(fèi)者,如果沒(méi)問(wèn)題纽窟,
訪(fǎng)問(wèn)http://127.0.0.1:8763/rest
你將通過(guò)restTemplate方式調(diào)用云服務(wù)肖油。
訪(fǎng)問(wèn)http://127.0.0.1:8763/feign
使用的則是feign客戶(hù)端方式。
當(dāng)然臂港,在這里因?yàn)檎{(diào)用的是同一個(gè)服務(wù)森枪,所以返回結(jié)果都是:
{"me":"consumer","serverPort":"8762","eurekaInstanceAppName":"spring-cloud-provider"}
3.4 測(cè)試
當(dāng)這里,基本的Spring Cloud服務(wù)已經(jīng)搭建完成审孽,但是既然是微服務(wù)县袱,怎么能不測(cè)試分布式呢?
我們沒(méi)有錢(qián)買(mǎi)許多電腦部署provider佑力,但是我們可以模擬式散。
接下來(lái),請(qǐng)自行打開(kāi)scdemo-provider
項(xiàng)目的application.yml打颤,修改
server:
port: 填寫(xiě)任何一個(gè)未被占用的端口號(hào)
然后依據(jù)自己電腦的性能暴拄,開(kāi)啟多個(gè)provider服務(wù)。
訪(fǎng)問(wèn)Eureka注冊(cè)中心頁(yè)面http://127.0.0.1:8761/编饺,你可以看到自己已經(jīng)啟動(dòng)的各項(xiàng)服務(wù)乖篷,請(qǐng)至少啟動(dòng)2個(gè)吧?
第一行是消費(fèi)者反肋,第二行是服務(wù)提供者那伐,這里我只啟動(dòng)了2個(gè)provider,一個(gè)是8762端口石蔗,一個(gè)是8764端口罕邀。
由于之前的配置中已經(jīng)開(kāi)啟了負(fù)載均衡,所以此時(shí)再訪(fǎng)問(wèn)http://127.0.0.1:8763/rest或者http://127.0.0.1:8763/feign养距,你都將按照一定規(guī)則請(qǐng)求到2個(gè)不同的服務(wù)上诉探,可以通過(guò)端口號(hào)來(lái)判斷。至于負(fù)載均衡的規(guī)則棍厌,有興趣的話(huà)可以自行研究并配置嘗試竖席。
附錄
項(xiàng)目源碼已提交至github,歡迎star
敬肚。
https://github.com/lemtasev/spring-cloud-demo