遠(yuǎn)程辦公”">國(guó)務(wù)院聯(lián)防聯(lián)控機(jī)制新聞發(fā)布會(huì),多次肯定“云辦公”、“遠(yuǎn)程辦公”
797
2025-03-31
1 Spring Cloud簡(jiǎn)介
Spring Cloud 是基于Spring Boot 的一整套實(shí)現(xiàn)微服務(wù)的框架。他提供了微服務(wù)開(kāi)發(fā)所需的配置管理、服務(wù)發(fā)現(xiàn)、斷路器、智能路由、微代理、控制總線、全局鎖、決策競(jìng)選、分布式會(huì)話和集群狀態(tài)管理等組件。最重要的是,跟spring boot 框架一起使用的話,會(huì)讓你開(kāi)發(fā)微服務(wù)架構(gòu)的云服務(wù)非常好的方便。 Spring Cloud 包含了非常多的子框架,其中,Spring Cloud Netflix是其中一套框架,由Netflix開(kāi)發(fā)后來(lái)又并入Spring Cloud大家庭,它主要提供的模塊包括:服務(wù)發(fā)現(xiàn)、斷路器和監(jiān)控、智能路由、客戶端負(fù)載均衡等。
Spring Cloud Netflix項(xiàng)目的時(shí)間還不長(zhǎng),并入spring cloud大家族還是2年前,所以相關(guān)的使用文檔還比較少,除了官方文檔,國(guó)內(nèi)也有一個(gè)中文社區(qū)。但是,如果是剛開(kāi)始接觸這個(gè),想使用它搭建一套微服務(wù)的應(yīng)用架構(gòu),總是會(huì)有不知如何下手的感覺(jué)。所以,這篇文章就是從整體上來(lái)看看這個(gè)框架的各個(gè)組件、用處是什么、如何相互作用。最后再結(jié)合實(shí)際的經(jīng)驗(yàn),介紹一下可能會(huì)出現(xiàn)的問(wèn)題,以及針對(duì)一些問(wèn)題,采用什么樣的解決方案。
首先,我們來(lái)看看一般的微服務(wù)架構(gòu)需要的功能或使用場(chǎng)景:
1.我們把整個(gè)系統(tǒng)根據(jù)業(yè)務(wù)拆分成幾個(gè)子系統(tǒng)。
2.每個(gè)子系統(tǒng)可以部署多個(gè)應(yīng)用,多個(gè)應(yīng)用之間使用負(fù)載均衡。
3.需要一個(gè)服務(wù)注冊(cè)中心,所有的服務(wù)都在注冊(cè)中心注冊(cè),負(fù)載均衡也是通過(guò)在注冊(cè)中心注冊(cè)的服務(wù)來(lái)使用一定策略來(lái)實(shí)現(xiàn)。
4.所有的客戶端都通過(guò)同一個(gè)網(wǎng)關(guān)地址訪問(wèn)后臺(tái)的服務(wù),通過(guò)路由配置,網(wǎng)關(guān)來(lái)判斷一個(gè)URL請(qǐng)求由哪個(gè)服務(wù)處理。請(qǐng)求轉(zhuǎn)發(fā)到服務(wù)上的時(shí)候也使用負(fù)載均衡。
5.服務(wù)之間有時(shí)候也需要相互訪問(wèn)。例如有一個(gè)用戶模塊,其他服務(wù)在處理一些業(yè)務(wù)的時(shí)候,要獲取用戶服務(wù)的用戶數(shù)據(jù)。
6.需要一個(gè)斷路器,及時(shí)處理服務(wù)調(diào)用時(shí)的超時(shí)和錯(cuò)誤,防止由于其中一個(gè)服務(wù)的問(wèn)題而導(dǎo)致整體系統(tǒng)的癱瘓。
7.還需要一個(gè)監(jiān)控功能,監(jiān)控每個(gè)服務(wù)調(diào)用花費(fèi)的時(shí)間等。
3 Spring Cloud Netflix組件以及部署
Spring Cloud Netflix框架剛好就滿足了上面所有的需求,而且最重要的是,使用起來(lái)非常的簡(jiǎn)單。Spring Cloud Netflix包含的組件及其主要功能大致如下:
1.Eureka,服務(wù)注冊(cè)和發(fā)現(xiàn),它提供了一個(gè)服務(wù)注冊(cè)中心、服務(wù)發(fā)現(xiàn)的客戶端,還有一個(gè)方便的查看所有注冊(cè)的服務(wù)的界面。所有的服務(wù)使用Eureka的服務(wù)發(fā)現(xiàn)客戶端來(lái)將自己注冊(cè)到Eureka的服務(wù)器上。
2.Zuul,網(wǎng)關(guān),所有的客戶端請(qǐng)求通過(guò)這個(gè)網(wǎng)關(guān)訪問(wèn)后臺(tái)的服務(wù)。他可以使用一定的路由配置來(lái)判斷某一個(gè)URL由哪個(gè)服務(wù)來(lái)處理。并從Eureka獲取注冊(cè)的服務(wù)來(lái)轉(zhuǎn)發(fā)請(qǐng)求。
3.Ribbon,即負(fù)載均衡,Zuul網(wǎng)關(guān)將一個(gè)請(qǐng)求發(fā)送給某一個(gè)服務(wù)的應(yīng)用的時(shí)候,如果一個(gè)服務(wù)啟動(dòng)了多個(gè)實(shí)例,就會(huì)通過(guò)Ribbon來(lái)通過(guò)一定的負(fù)載均衡策略來(lái)發(fā)送給某一個(gè)服務(wù)實(shí)例。
4.Feign,服務(wù)客戶端,服務(wù)之間如果需要相互訪問(wèn),可以使用RestTemplate,也可以使用Feign客戶端訪問(wèn)。它默認(rèn)會(huì)使用Ribbon來(lái)實(shí)現(xiàn)負(fù)載均衡。
5.Hystrix,監(jiān)控和斷路器。我們只需要在服務(wù)接口上添加Hystrix標(biāo)簽,就可以實(shí)現(xiàn)對(duì)這個(gè)接口的監(jiān)控和斷路器功能。
6.Hystrix Dashboard,監(jiān)控面板,他提供了一個(gè)界面,可以監(jiān)控各個(gè)服務(wù)上的服務(wù)調(diào)用所消耗的時(shí)間等。
7.Turbine,監(jiān)控聚合,使用Hystrix監(jiān)控,我們需要打開(kāi)每一個(gè)服務(wù)實(shí)例的監(jiān)控信息來(lái)查看。而Turbine可以幫助我們把所有的服務(wù)實(shí)例的監(jiān)控信息聚合到一個(gè)地方統(tǒng)一查看。這樣就不需要挨個(gè)打開(kāi)一個(gè)個(gè)的頁(yè)面一個(gè)個(gè)查看。
下面就是使用上述的子框架實(shí)現(xiàn)的為服務(wù)架構(gòu)的組架構(gòu)圖:
在上圖中,有幾個(gè)需要說(shuō)明的地方:
ZUUL網(wǎng)關(guān)也在注冊(cè)中心注冊(cè),把它也當(dāng)成一個(gè)服務(wù)來(lái)統(tǒng)一查看。
負(fù)載均衡不是一個(gè)獨(dú)立的組件,它運(yùn)行在網(wǎng)關(guān)、服務(wù)調(diào)用等地方,每當(dāng)需要訪問(wèn)一個(gè)服務(wù)的時(shí)候,就會(huì)通過(guò)Ribbon來(lái)獲得一個(gè)該服務(wù)的實(shí)例去掉用。Ribbon從Eureka注冊(cè)中心獲得服務(wù)和實(shí)例的列表,而不是發(fā)送每個(gè)請(qǐng)求的時(shí)候從注冊(cè)中心獲得。
我們可以使用RestTemplate來(lái)進(jìn)行服務(wù)間調(diào)用,也可以配置FeignClient來(lái)使用,不管什么方式,只要使用服務(wù)注冊(cè),就會(huì)默認(rèn)使用Ribbon負(fù)載均衡。(RestTemplate需要添加@LoadBalanced)
每個(gè)服務(wù)都可以開(kāi)啟監(jiān)控功能,開(kāi)啟監(jiān)控的服務(wù)會(huì)提供一個(gè)servlet接口/hystrix.stream,如果你需要監(jiān)控這個(gè)服務(wù)的某一個(gè)方法的運(yùn)行統(tǒng)計(jì),就在這個(gè)方法上加一個(gè)@HystrixCommand的標(biāo)簽。
查看監(jiān)控信息,就是在Hystrix Dashboard上輸入這個(gè)服務(wù)的監(jiān)控url:
http://serviceIp:port/hystrix.stream,就可以用圖表的方式查看運(yùn)行監(jiān)控信息。
如果要把所有的服務(wù)的監(jiān)控信息聚合在一起統(tǒng)一查看,就需要使用Turbine來(lái)聚合所需要的服務(wù)的監(jiān)控信息。
我們也可以從上圖中看出該架構(gòu)的部署方式:
1.獨(dú)立部署一個(gè)網(wǎng)關(guān)應(yīng)用
2.服務(wù)注冊(cè)中心和監(jiān)控可以配置在一個(gè)應(yīng)用里,也可以是2個(gè)應(yīng)用。
3.服務(wù)注冊(cè)中心也可以部署多個(gè),通過(guò)區(qū)域zone來(lái)區(qū)分,來(lái)實(shí)現(xiàn)高可用。
4.每個(gè)服務(wù),根據(jù)負(fù)載和高可用的需要,部署一個(gè)或多個(gè)實(shí)例。
4 Spring Cloud Netflix組件開(kāi)發(fā)
上面說(shuō)到,開(kāi)發(fā)基于Spring Cloud Netflix的微服務(wù)非常簡(jiǎn)單,一般我們是和spring boot一起使用,如果你想在自己原先的java web應(yīng)用中使用也可以通過(guò)添加相關(guān)配置來(lái)實(shí)踐。 有關(guān)開(kāi)發(fā)的詳細(xì)內(nèi)容,可以參考spring cloud中文社區(qū)的這個(gè)系列文章,里面詳細(xì)介紹了每一種組件的開(kāi)發(fā)。這里,就只是來(lái)看一下服務(wù)注冊(cè)中和監(jiān)控模塊的開(kāi)發(fā),還有服務(wù)調(diào)用的開(kāi)發(fā),其他的可以直接參考上面的系列文章。
4.1 注冊(cè)和監(jiān)控中心的開(kāi)發(fā)
這個(gè)非常簡(jiǎn)單,就下面一個(gè)類:
// 省略import@SpringBootApplication@EnableEurekaServer@EnableHystrixDashboardpublic class ApplicationRegistry { public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } }
1
2
3
4
5
這里使用spring boot標(biāo)簽的 @SpringBootApplication 說(shuō)明當(dāng)前的應(yīng)用是一個(gè)spring boot應(yīng)用。這樣我就可以直接用main函數(shù)在IDE里面啟動(dòng)這個(gè)應(yīng)用,也可以打包后用命令行啟動(dòng)。當(dāng)然也可以把打包的war包用tomcat之類的服務(wù)器啟動(dòng)。 使用標(biāo)簽 @EnableEurekaServer ,就能在啟動(dòng)過(guò)程中啟動(dòng)Eureka服務(wù)注冊(cè)中心的組件。它會(huì)監(jiān)聽(tīng)一個(gè)端口,默認(rèn)是8761,來(lái)接收服務(wù)注冊(cè)。并提供一個(gè)web頁(yè)面,打開(kāi)以后,可以看到注冊(cè)的服務(wù)。 添加 @EnableHystrixDashboard 就會(huì)提供一個(gè)監(jiān)控的頁(yè)面,我們可以在上面輸入要監(jiān)控的服務(wù)的地址,就可以查看啟用了Hystrix監(jiān)控的接口的調(diào)用情況。 當(dāng)然,為了使用上面的組件,我們需要在maven的POM文件里添加相應(yīng)的依賴,比如使用 spring-boot-starter-parent ,依賴 spring-cloud-starter-eureka-server 和 spring-cloud-starter-hystrix-dashboard 等。
4.2 服務(wù)間調(diào)用
在網(wǎng)上的各種文檔中,對(duì)服務(wù)間調(diào)用,都沒(méi)有說(shuō)明的很清楚,所以這里特別說(shuō)明一下這個(gè)如何開(kāi)發(fā)。 有兩種方式可以進(jìn)行服務(wù)調(diào)用,RestTemplate和FeignClient。不管是什么方式,他都是通過(guò)REST接口調(diào)用服務(wù)的http接口,參數(shù)和結(jié)果默認(rèn)都是通過(guò)jackson序列化和反序列化。因?yàn)镾pring MVC的RestController定義的接口,返回的數(shù)據(jù)都是通過(guò)jackson序列化成json數(shù)據(jù)。
4.2.1 RestTemplate
使用這種方式,只需要定義一個(gè)RestTemplate的Bean,設(shè)置成 LoadBalanced 即可:
@Configurationpublic class SomeCloudConfiguration { @LoadBalanced @Bean RestTemplate restTemplate() { return new RestTemplate(); } }
1
2
3
4
5
6
7
這樣我們就可以在需要用的地方注入這個(gè)bean使用:
public class SomeServiceClass { @Autowired private RestTemplate restTemplate; public String getUserById(Long userId) { UserDTO results = restTemplate.getForObject("http://users/getUserDetail/" + userId, UserDTO.class); return results; } }
1
2
3
4
5
6
7
8
其中, users 是服務(wù)ID,Ribbon會(huì)從服務(wù)實(shí)例列表獲得這個(gè)服務(wù)的一個(gè)實(shí)例,發(fā)送請(qǐng)求,并獲得結(jié)果。對(duì)象 UserDTO 需要序列號(hào),它的反序列號(hào)會(huì)自動(dòng)完成。 FeignClient
除了上面的方式,我們還可以用FeignClient。還是直接看代碼:
4.2.2 FeignClient
除了上面的方式,我們還可以用 FeignClient 。還是直接看代碼:
@FeignClient(value = "users", path = "/users")public interface UserCompositeService { @RequestMapping(value = "/getUserDetail/{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) UserDTO getUserById(@PathVariable Long id); }
1
2
3
4
我們只需要使用 @FeignClient 定義一個(gè)借口,Spring Cloud Feign會(huì)幫我們生成一個(gè)它的實(shí)現(xiàn),從相應(yīng)的users服務(wù)獲取數(shù)據(jù)。 其中, @FeignClient(value = “users”, path = “/users/getUserDetail”) 里面的value是服務(wù)ID,path是這一組接口的path前綴。 在下面的方法定義里,就好像設(shè)置Spring MVC的接口一樣,對(duì)于這個(gè)方法,它對(duì)應(yīng)的URL是 /users/getUserDetail/{id} 。 然后,在使用它的時(shí)候,就像注入一個(gè)一般的服務(wù)一樣注入后使用即可:
public class SomeOtherServiceClass { @Autowired private UserCompositeService userService; public void doSomething() { // ..... UserDTO results = userService.getUserById(userId); // other operation... } }
1
2
3
4
5
6
7
8
9
5 遇到的問(wèn)題
由于Spring Cloud說(shuō)明文檔較少,微服務(wù)的架構(gòu)相對(duì)來(lái)說(shuō)也比較復(fù)雜,在開(kāi)發(fā)的時(shí)候,難免會(huì)遇到很多問(wèn)題,有一些是如何更好地使用這套框架去搭建架構(gòu),也有一些問(wèn)題是如何配置。這里就一些我在搭建微服務(wù)架構(gòu)的時(shí)候遇到的問(wèn)題提供一些方法。
5.1 請(qǐng)求超時(shí)問(wèn)題
ZUUL網(wǎng)關(guān)默認(rèn)的超時(shí)時(shí)間非常短,這是為了保證調(diào)用服務(wù)的時(shí)候能夠很快的響應(yīng)。但是,我們會(huì)有一些業(yè)務(wù)方法運(yùn)行的時(shí)間比較長(zhǎng),特別是在測(cè)試服務(wù)器。這時(shí)候,就需要調(diào)整超時(shí)時(shí)間。這個(gè)超時(shí)有幾個(gè)地方:
負(fù)載均衡Ribbon,負(fù)載均衡有一個(gè)超時(shí)的設(shè)置,包括鏈接時(shí)間和讀取時(shí)間
Hystrix斷路器也有一個(gè)超時(shí)設(shè)置,它需要在適當(dāng)?shù)臅r(shí)候返回,而不是一直等在一個(gè)請(qǐng)求上。
對(duì)應(yīng)的配置如下:
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds = 30000 ribbon: ReadTimeout: 30000 ConnectTimeout: 15000
1
2
3
4
5.2 服務(wù)ID的問(wèn)題
服務(wù)的ID,也就是服務(wù)名,可以通過(guò)在application.yml或者bootstrap.yml里面設(shè)置:
spring: application: name: users
1
2
3
5.3 管理路徑的問(wèn)題
Spring Boot的應(yīng)用默認(rèn)都是開(kāi)放一些管理的接口,如 /info 、 /health 和metrics監(jiān)控的接口 /metrics 等。如果你使用默認(rèn)的路徑,使用Hystrix監(jiān)控、服務(wù)注冊(cè)中心的監(jiān)聽(tīng)服務(wù)狀態(tài)都不會(huì)有問(wèn)題,但是,如果你想使用別的路徑,例如 /management/info 、 /management/health ,那就牽扯到很多地方,而且,每個(gè)版本可能會(huì)或多或少的有一些問(wèn)題,導(dǎo)致你遇到的問(wèn)題還會(huì)不一樣。我遇到過(guò)的問(wèn)題有:
5.3.1 注冊(cè)成功卻找不到服務(wù)
首先,注冊(cè)可以成功,在Eureka服務(wù)器頁(yè)面上也可以看到各個(gè)服務(wù)。但是,當(dāng)你通過(guò)網(wǎng)關(guān)調(diào)用的時(shí)候,卻總是提示服務(wù)找不到。這時(shí)候可能就需要在每個(gè)服務(wù)的application.yml里面進(jìn)行如下配置:
eureka: instance: nonSecurePort: ${server.port} appname: ${spring.application.name} statusPageUrlPath: ${management.context-path}/info healthCheckUrlPath: ${management.context-path}/health
1
2
3
4
5
6
簡(jiǎn)單來(lái)說(shuō),這就是告訴在注冊(cè)的時(shí)候,同時(shí)告訴Eureka服務(wù)器,服務(wù)的端口是什么,用來(lái)監(jiān)聽(tīng)狀態(tài)的路徑是什么。這是因?yàn)槲覀兪褂昧瞬煌墓芾斫涌诼窂剑鳨ureka服務(wù)器沒(méi)有使用相應(yīng)的路徑。 如果一切正常,你在Eureka服務(wù)器上點(diǎn)擊一個(gè)注冊(cè)的服務(wù),應(yīng)該能打開(kāi)一個(gè)info頁(yè)面。他可能是空白的,但是,至少Eureka服務(wù)器能通過(guò)這個(gè)知道服務(wù)的運(yùn)行正常。 這個(gè)問(wèn)題也不是在所有的版本都存在,只是在某一些spring cloud的版本存在。
5.3.2 設(shè)置了管理路徑的Hystrix監(jiān)控
剛才說(shuō)了Hystrix監(jiān)控的路徑是 http://serviceIp:port/hystrix.stream ,如果你設(shè)置了管理接口的路徑,那么這個(gè)監(jiān)控路徑也會(huì)變成:
http://serviceIp:port/${management.context-path}/hystrix.stream
如果這時(shí)候,你再想使用Turbine聚合,Turbine就會(huì)找不到了,因?yàn)樗J(rèn)使用Eureka服務(wù)器上的服務(wù)器地址和端口,在后面添加/hystrix.stream。這時(shí)候,你就需要設(shè)置
Turbine: turbine: aggregator: clusterConfig: USER appConfig: USER instanceUrlSuffix: USER: /user/hystrix.stream
1
2
3
4
5
6
7
5.3.3 管理路徑的安全性
對(duì)于微服務(wù)部署的幾臺(tái)機(jī)器,可以通過(guò)開(kāi)通防火墻來(lái)控制誰(shuí)可以訪問(wèn)管理接口,但是,即使是這樣,為了安全性等,我一般還是會(huì)把管理端接口也用spring security來(lái)保護(hù)。這樣一來(lái),監(jiān)控接口就沒(méi)法直接訪問(wèn)了。
5.4 服務(wù)間調(diào)用的權(quán)限驗(yàn)證
一般我們的API接口都需要某種授權(quán)才能訪問(wèn),登陸成功以后,然后通過(guò)token或者cookie等方式才能調(diào)用接口。 使用Spring Cloud Netfix框架的話,登錄的時(shí)候,把登錄請(qǐng)求轉(zhuǎn)發(fā)到相應(yīng)的用戶服務(wù)上,登陸成功后,會(huì)設(shè)置cookie或header token等。然后客戶端接下來(lái)的請(qǐng)求就會(huì)帶著這些驗(yàn)證信息,從ZUUL網(wǎng)關(guān)傳到相應(yīng)的服務(wù)上進(jìn)行驗(yàn)證。 ZUUL網(wǎng)關(guān)在把請(qǐng)求轉(zhuǎn)發(fā)到后臺(tái)的服務(wù)的時(shí)候,會(huì)默認(rèn)把一些header傳到服務(wù)端,如:Cookie, Set-Cookie, Authorization。這樣,客戶端請(qǐng)求的相關(guān)headers就可以傳遞到服務(wù)端,服務(wù)端設(shè)置的cookie也可以傳到客戶端。 但是,如果你想禁止某些header透?jìng)鞯椒?wù)端,可以在ZUUL網(wǎng)關(guān)的application.yml配置里通過(guò)下面的方式禁用:
zuul: routes: users: path: /users/** sensitiveHeaders: Cookie,Set-Cookie,Authorization serviceId: user
1
2
3
4
5
6
剛才說(shuō)了我們的某個(gè)服務(wù)有時(shí)候需要調(diào)用另一個(gè)服務(wù),這時(shí)候,這個(gè)請(qǐng)求不是客戶端發(fā)起,他的請(qǐng)求的header里面也不會(huì)有任何驗(yàn)證信息。這時(shí)候,要么,通過(guò)防火墻等設(shè)置,保證服務(wù)間調(diào)用的接口,只能某幾個(gè)地址訪問(wèn);要么,就通過(guò)某種方式設(shè)置header。 同時(shí),如果你想在某個(gè)服務(wù)里面獲得這個(gè)請(qǐng)求的真是IP,(因?yàn)檎?qǐng)求的通過(guò)網(wǎng)關(guān)轉(zhuǎn)發(fā)而來(lái),你直接通過(guò)request獲得ip得到的是網(wǎng)關(guān)的IP),就可以從 headerX-Forwarded-Host 獲得。如果想禁用這個(gè)header,也可以:
zuul.addProxyHeaders = false
1
如果你使用 RestTemplate 的方式調(diào)用,可以在請(qǐng)求里面添加一個(gè)有header的 Options 。 也可以通過(guò)如下的-的方式設(shè)置,它對(duì) RestTemplate 方式和 FeignClient 的方式都可以起作用:
@Beanpublic RequestInterceptor requestInterceptor() { return new RequestInterceptor() { @Override public void apply(RequestTemplate template) { String authToken = getToken(); template.header(AUTH_TOKEN_HEADER, authToken); } }; }
1
2
3
4
5
6
7
8
9
10
Spring Spring Cloud 微服務(wù)
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。