
一、SpringBoot 構(gòu)建項(xiàng)目
在我們使用傳統(tǒng)的 spring 開發(fā)一個(gè) web 應(yīng)用程序通常會(huì)想到一些基本的需要:
- Web.xml 文件(配置 SpringMVC 的 DispatcherServlet,各種過濾器等等);
- 啟用了 SpringMVC 的 spring 配置文件;
- Mybatis 等數(shù)據(jù)庫配置文件等。
以上的這些僅僅只是基本的需求,無論是開發(fā)一個(gè)大型項(xiàng)目或者只是一個(gè) hello word 程序,都需要配置幾乎同等的配置文件,既然這些都是通用的東西,那有什么東西可以把這些給自動(dòng)配置了呢?這時(shí)候 springboot 的自動(dòng)配置功能就派上用場了,springboot 會(huì)為這些常用的配置進(jìn)行自動(dòng)配置。這些自動(dòng)配置涉及很多方面,比如:java 持久化 api,各種 web 模板,springMVC 等等。
1. 起步依賴
平時(shí)我們使用 maven 創(chuàng)建一個(gè) web 項(xiàng)目的時(shí)候,常常需要想項(xiàng)目需要哪些包,以及包的版本。但是在 springboot 創(chuàng)建 web 應(yīng)用的時(shí)候,你只需你只需添加 springboot 的 Web 起步依賴(org.springframework.boot:spring-boot-starter-web)。它會(huì)根據(jù)依賴傳遞把其他所需依賴引入項(xiàng)目里面。
而其它你需要的功能,你只需要引入相關(guān)的的起步依賴即可。
2. 內(nèi)嵌 Servlet 容器
其實(shí) springboot 并不是一個(gè)應(yīng)用服務(wù)器,它之所以可以運(yùn)行 web 應(yīng)用程序,是因?yàn)槠鋬?nèi)部已經(jīng)內(nèi)嵌了一個(gè) Servlet 容器(Tomcat、Jetty 或 Undertow),其運(yùn)行原理是把 web 應(yīng)用直接打包成為一個(gè) jar/war,然后這個(gè) jar/war 是可以直接啟動(dòng)的,不需要另外配置一個(gè) Web Server。相關(guān)的 embed 類就是它的依賴包。
3. Spring Initializr 構(gòu)建 springboot 應(yīng)用程序
本文使用的是 intellij idea 中的 Spring Initializr 工具創(chuàng)建 springboot 應(yīng)用程序。
菜單欄中選擇File=>New=>Project..,步驟大概是選擇構(gòu)建的工程類型,如:maven,Gradle;language 的選擇;選擇 Spring Boot 版本和起步依賴包等等。具體創(chuàng)建步驟這里就省略了。

spring boot 項(xiàng)目結(jié)構(gòu)如圖所示,整個(gè)項(xiàng)目結(jié)構(gòu)遵循了 maven 項(xiàng)目的布局,主要的應(yīng)用程序代碼位于 src/main/java 目錄里,資源都在 src/main/resources 目錄里,測試代碼則在 src/test/java 目錄里。不同的是,web 頁面模板移到 templates 了,我的項(xiàng)目現(xiàn)在主要用 thymeleaf 模板作為 web 頁面。
在結(jié)構(gòu)圖你會(huì)發(fā)現(xiàn)一些與 springboot 密切項(xiàng)目的文件:
- WebGatewayApplication.java:應(yīng)用程序的啟動(dòng)引導(dǎo)類(bootstrap class),也是主要的 Spring 配置類;
- application.properties:用于配置應(yīng)用程序和 Spring Boot 的屬性;
- ReadingListApplicationTests.java:一個(gè)基本的集成測試類。
- banner.txt:spring boot 應(yīng)用程序啟動(dòng)時(shí)加載的文件。
3.1 啟動(dòng)引導(dǎo) Spring
前面我們看到的 WebGatewayApplication.java 在 springboot 應(yīng)用程序中主要有兩個(gè)作用:配置和啟動(dòng)引導(dǎo)。而也是 Spring 的主要配置類。雖然 springboot 的自動(dòng)配置免除了很多 Spring 配置,但你還需要進(jìn)行少量配置來啟用自動(dòng)配置。
程序清單:
package com.crm;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication // 開啟組件掃描和自動(dòng)配置
public class WebGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(WebGatewayApplication.class, args);// 啟動(dòng)引導(dǎo)應(yīng)用程序
}
}
3.2 配置應(yīng)用程序?qū)傩?/h3>
用 Spring Initializr 生成的 application.properties 文件只是一個(gè)空文件,它可以刪除完全不影響應(yīng)用程序的運(yùn)行,但是,如果你想修改應(yīng)用程序的屬性,你就得在里面配置相關(guān)屬性了,比如你在里面配置了 server.port=9010,嵌入式的 tomcat 服務(wù)器的監(jiān)聽端口就不是默認(rèn)的 8080 了,變成了 9010。而且這個(gè)屬性文件是自動(dòng)被加載的。
這是我的項(xiàng)目 application.properties 屬性配置:
###### MySQL配置
spring.datasource.name=test
spring.datasource.url=jdbc:mysql://localhost:3306/crm?characterEncoding=UTF8
spring.datasource.username=zch
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.filters=stat
spring.datasource.maxActive=20
spring.datasource.initialSize=1
spring.datasource.maxWait=60000
spring.datasource.minIdle=1
spring.datasource.timeBetweenEvictionRunsMillis=60000
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=select 'x'
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false
spring.datasource.poolPreparedStatements=true
spring.datasource.maxOpenPreparedStatements=20
###### mybatis
mybatis.typeAliasesPackage=com.joosure.integral.cloud.pojo.cloud
mybatis.mapperLocations=classpath:mapper/*.xml
####### thymeleaf
spring.thymeleaf.cache=false
spring.thymeleaf.check-template-location=true
spring.thymeleaf.content-type=text/html
spring.thymeleaf.enabled=true
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.excluded-view-names=
spring.thymeleaf.mode=HTML5
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.template-resolver-order=
3.3 構(gòu)建過程解釋
我的項(xiàng)目用的是 maven 作為構(gòu)建工具,因此用 Spring Initializr 會(huì)生成 pom.xml 文件,這與創(chuàng)建普通的 maven 項(xiàng)目一樣,代碼清單如下:
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>crm</name>
<description>crm-system</description>
<parent> <!-- 從spring-boot-starterparent繼承版本號(hào) -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies><!-- 起步依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--web及模板引擎-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--數(shù)據(jù)庫-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
<!--測試-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build><!-- 運(yùn)行spring boot插件 -->
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- 其中 Artifact ID 為 spring-boot-starter-xxx 的都是 spring boot 起步依賴;
- 構(gòu)建插件的主要功能是把項(xiàng)目打包成一個(gè)可執(zhí)行的超級 JAR(uber-JAR),包括把應(yīng)用程序的所有依賴打入 JAR 文件內(nèi),并為 JAR 添加一個(gè)描述文件,其中的內(nèi)容能讓你用 java -jar 來運(yùn)行應(yīng)用程序;
- Maven 構(gòu)建說明中還將 spring-boot-starter-parent 作為上一級,這樣一來就能利用 Maven 的依賴管理功能,繼承很多常用庫的依賴版本,在你聲明依賴時(shí)就不用再去指定版本號(hào)了。
二、服務(wù)注冊與發(fā)現(xiàn)
現(xiàn)在公司的積分聯(lián)盟平臺(tái)系統(tǒng)構(gòu)建于公司內(nèi)部的第 4 代架構(gòu)中,而第 4 代就是 基于 SpringCloud 的微服務(wù)架構(gòu),趁著項(xiàng)目上手,花了幾天研究了一下。
SpringCloud 是一個(gè)龐大的分布式系統(tǒng),它包含了眾多模塊,其中主要有:服務(wù)發(fā)現(xiàn)(Eureka),斷路器(Hystrix),智能路由(Zuul),客戶端負(fù)載均衡(Ribbon)等。也就是說微服務(wù)架構(gòu)就是將一個(gè)完整的應(yīng)用從數(shù)據(jù)存儲(chǔ)開始垂直拆分成多個(gè)不同的服務(wù),每個(gè)服務(wù)都能獨(dú)立部署、獨(dú)立維護(hù)、獨(dú)立擴(kuò)展,服務(wù)與服務(wù)間通過諸如 RESTful API 的方式互相調(diào)用。
1. 創(chuàng)建服務(wù)注冊中心
在搭建 SpringCloud 分布式系統(tǒng)前我們需要?jiǎng)?chuàng)建一個(gè)注冊服務(wù)中心,以便監(jiān)控其余模塊的狀況。這里需要在 pom.xml 中引入:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
并且在 SpringBoot 主程序中加入@EnableEurekaServer 注解:
@EnableEurekaServer
@SpringCloudApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
接下來在 SpringBoot 的屬性配置文件 application.properties 中如下配置:
server.port=9100
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.client.serviceUrl.defaultZone=http://localhost:${server.port}/eureka/
server.port 就是你指定注冊服務(wù)中心的端口號(hào),在啟動(dòng)服務(wù)后,可以通過訪問http://localhost:9100服務(wù)發(fā)現(xiàn)頁面,如下:

2. 創(chuàng)建服務(wù)方
我們可以發(fā)現(xiàn)其它系統(tǒng)在這里注冊并顯示在頁面上了,想要注冊到服務(wù)中心,需要在系統(tǒng)上做一些配置,步驟跟創(chuàng)建服務(wù)注冊中心類似,這里 web-gateway 系統(tǒng)做例子:
首先在 pom.xml 中加入:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
在 SpringBoot 主程序中加入@EnableDiscoveryClient 注解,該注解能激活 Eureka 中的DiscoveryClient實(shí)現(xiàn),才能實(shí)現(xiàn) Controller 中對服務(wù)信息的輸出:
@EnableDiscoveryClient
@SpringBootApplication
public class WebGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(WebGatewayApplication.class, args);
}
}
在 SpringBoot 的屬性配置文件 application.properties 中如下配置:
spring.application.name=web-gateway
server.port=9010
eureka.client.serviceUrl.defaultZone=http://localhost:9100/eureka/
eureka.instance.leaseRenewalIntervalInSeconds=5
再次啟動(dòng)服務(wù)中心,打開鏈接:http://localhost:9100/,就可以看到剛剛創(chuàng)建的服務(wù)了。

三、服務(wù)消費(fèi)者
在系統(tǒng)與系統(tǒng)之間,如何進(jìn)行相互間的調(diào)用呢?也就是說怎么去調(diào)用服務(wù)提供的接口內(nèi)容呢?這里就要說一下 Ribbon 了,Ribbon 是一個(gè)基于 http 和 tcp 客戶端的負(fù)載均衡器。
下面我來簡單介紹如何在 SpringCloud 分布式系統(tǒng)下使用 Ribbon 來實(shí)現(xiàn)負(fù)載均衡。
首先在 pom.xml 中引入一下依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
然后在 spring boot 主程序中創(chuàng)建 RestTemplate 類,并為它加上@LoadBalanced 注解開啟負(fù)載均衡的能力:
@EnableDiscoveryClient
@SpringBootApplication
public class WebGatewayApplication {
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(WebGatewayApplication.class, args);
}
}
RestTemplate 類是 Spring 用于構(gòu)建 Restful 服務(wù)而提供的一種 Rest 服務(wù)可客戶端,RestTemplate 提供了多種便捷訪問遠(yuǎn)程 Http 服務(wù)的方法。
在 apllication.properties 配置文件中配置 eureka 服務(wù),并注冊到服務(wù)中心:
spring.application.name=integral-server
server.port=9600
eureka.client.serviceUrl.defaultZone=http://localhost:9100/eureka/
在公司項(xiàng)目中正是通過 RestTemplate 來訪問各個(gè)微服務(wù)提供的接口,比如在項(xiàng)目中要訪問積分系統(tǒng) integral-server,添加積分用戶:
JSONObject integralServerResult = restTemplate.postForObject("http://integral-server/shop/add", RequestHandler.getRestRawRequestEntity(integralShopJson), JSONObject.class);
這樣就可以調(diào)用 integral-server 系統(tǒng)的添加用戶的接口實(shí)現(xiàn)在別的系統(tǒng)中添加用戶了。
我們也可以在 application.properties 配置文件中加入:
###### Ribbon
ribbon.ReadTimeout=60000
這個(gè)是設(shè)置負(fù)載均衡的超時(shí)時(shí)間的。

四、斷路器
微服務(wù)架構(gòu)中,各個(gè)系統(tǒng)被拆分成一個(gè)個(gè)服務(wù)單元,鏈路調(diào)用可能包括很多個(gè)服務(wù)單元,而每個(gè)單元又會(huì)個(gè) N 個(gè)服務(wù)單元提供服務(wù),因此如果有一個(gè)服務(wù)單元出現(xiàn)故障,就可能導(dǎo)致其它依賴此服務(wù)的服務(wù)單元出現(xiàn)延遲,導(dǎo)致整個(gè)微服務(wù)系統(tǒng)出現(xiàn)雪崩效應(yīng)。
在 SpringCloud 模塊中有一個(gè)叫 Netflix Hystrix 的斷路器模塊,就是專門解決這個(gè)問題而生的,Hystrix 是 Netflix 開源的微服務(wù)框架套件之一,該框架目標(biāo)在于通過控制那些訪問遠(yuǎn)程系統(tǒng)、服務(wù)和第三方庫的節(jié)點(diǎn),從而對延遲和故障提供更強(qiáng)大的容錯(cuò)能力。
下面來說一下 Hystrix 在微服務(wù)系統(tǒng)中的具體用法:
首先還是在 pom.xml 中加入以下依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
在 spring boot 主程序中加入@EnableCircuitBreaker 注解開啟斷路器模式:
@EnableEurekaClient
@EnableCircuitBreaker
@EnableDiscoveryClient
@SpringBootApplication
public class WebGatewayApplication {
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(WebGatewayApplication.class, args);
}
如果在調(diào)用過程中返回類似這樣的響應(yīng):
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Sat May 13 00:10:22 CST 2017
There was an unexpected error (type=Internal Server Error, status=500).
400 null
斷路器也就開啟了。
我們也可以在 application.properties 配置文件中加入:
## hystrix
hystrix.commond.default.execution.isolation.thread.timeoutInMilliseconds=60000
這個(gè)設(shè)置可以更改返回錯(cuò)誤響應(yīng)的超時(shí)時(shí)間。
如果不想返回默認(rèn)的錯(cuò)誤響應(yīng)信息,我們還可以通過自定義來更改錯(cuò)誤響應(yīng)信息,我們需要一個(gè)類中注入一個(gè) RestTemplate 類:
@Autowired
RestTemplate restTemplate;
這個(gè)類在上面已經(jīng)通過 Spring 創(chuàng)建好了,這里直接注入在類中即可,接下來我們在類中寫一個(gè)方法:
@HystrixCommand(fallbackMethod = "addServiceFallback")
public String addService() {
return restTemplate.postForObject("http://integral-server/shop/add", RequestHandler.getRestRawRequestEntity(integralShopJson), JSONObject.class);
}
public String addServiceFallback() {
return "error";
}
當(dāng)調(diào)用 integral-server 系統(tǒng)的添加接口超出延時(shí)的時(shí)間時(shí),就會(huì)返回“error”。
五、服務(wù)網(wǎng)關(guān)
前面我們通過 Ribbon 實(shí)現(xiàn)服務(wù)的消費(fèi)和負(fù)載均衡,但還有些不足的地方,舉個(gè)例子,服務(wù) A 和服務(wù) B,他們都注冊到服務(wù)注冊中心,這里還有個(gè)對外提供的一個(gè)服務(wù),這個(gè)服務(wù)通過負(fù)載均衡提供調(diào)用服務(wù) A 和服務(wù) B 的方法,那么問題來了,每個(gè)服務(wù)都變得有狀態(tài)了,即每個(gè)服務(wù)都需要維護(hù)一套校驗(yàn)邏輯,這樣會(huì)帶來對外接口有污染。而且權(quán)限等不好集中管理,整個(gè)集群處于混亂之中。
最好的方法就是把所有請求都集中在最前端的地方,這地方就是 zuul 服務(wù)網(wǎng)關(guān)。
服務(wù)網(wǎng)關(guān)是微服務(wù)架構(gòu)組件中處于最外一層,通過服務(wù)網(wǎng)關(guān)統(tǒng)一,可以將鏈路前端集中管理起來,除了具備服務(wù)路由、均衡負(fù)載功能之外,它還需要具備權(quán)限控制等功能。Spring Cloud Netflix 中的 Zuul 就擔(dān)任了這樣的一個(gè)角色,為微服務(wù)披上了一層保護(hù)層,也方便了權(quán)限校驗(yàn)集中管理,增加了接口的通用性。

1. 配置服務(wù)路由
要使用 zuul,就要引入它的依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
在 spring boot 主程序中加入@EnableZuulProxy 注解開啟 zuul:
@EnableEurekaClient
@EnableZuulProxy
@EnableDiscoveryClient
@SpringBootApplication
public class WebGatewayApplication {
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(WebGatewayApplication.class, args);
}
}
在 application.properties 配置文件中配置 zuul 路由 url:
spring.application.name=web-gateway
server.port=9010
到這里,一個(gè)微服務(wù) zuul 服務(wù)網(wǎng)關(guān)系統(tǒng)已經(jīng)可以運(yùn)行了,接下來就是如何配置訪問其它微服務(wù)系統(tǒng)的 url,zuul 提供了兩種配置方式,一種是通過 url 直接映射,另一種是利用注冊到 eureka server 中的服務(wù) id 作映射:
url 直接映射:
zuul.routes.api-integral.path=/api-integral-url/**
zuul.routes.api-integral.url=http://localhost:8080/
以上規(guī)則意思是 /api-integral-url/** 的訪問都會(huì)被路由到 http://localhost:8080/上。
但是這么做必須得知道所有的微服務(wù)的地址,才能完成配置,這時(shí)我們可以利用注冊到 eureka server 中的服務(wù) id 作映射:
###### Zuul配置
zuul.routes.api-integral.path=/integral/**
zuul.routes.api-integral.serviceId=integral-server
zuul.routes.api-member.path=/member/**
zuul.routes.api-member.serviceId=member-server
integral-server 和 member-server 是這倆微服務(wù)系統(tǒng)注冊到微服務(wù)中心的一個(gè) serverId,我們通過配置,訪問http://localhost:9010/integual/add?a=1&b=2,該請求就會(huì)訪問 integral-server 系統(tǒng)中的 add 服務(wù)。
2. 服務(wù)過濾
在定義 zuul 網(wǎng)關(guān)服務(wù)過濾只需要?jiǎng)?chuàng)建一個(gè)繼承 ZuulFilter 抽象類并重寫四個(gè)方法即可,下面是 ZuulFilter 的一些解釋:
- filterType:過濾類型,具體如下:
- pre:請求路由之前執(zhí)行;
- routing:請求路由時(shí)執(zhí)行;
- post:在 routing 和 error 過濾器之后執(zhí)行;
- error:在請求發(fā)生錯(cuò)誤的時(shí)候執(zhí)行;
- filterOrder:定義過濾器的執(zhí)行順序
- shouldFilter:判斷該過濾器是否要執(zhí)行,
- run:過濾器的具體邏輯。
標(biāo)準(zhǔn)實(shí)例程序:
public class ErrFilter extends ZuulFilter {
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
Object accessToken = request.getParameter("accessToken");
if(accessToken == null) {
log.warn("access token is empty");
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(401);
return null;
}
return null;
}
}
在自定過濾器之后,我們還需要在 SpringBoot 主程序中加入@EnableZuulProxy 注解來開啟 zuul 路由的服務(wù)過濾:
@EnableZuulProxy
@EnableEurekaClient
@RibbonClients
@SpringCloudApplication
public class ApiGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ApiGatewayApplication.class, args);
}
@Bean
PosPreFilter posPreFilter(){
return new PosPreFilter();
}
到這里,微服務(wù)系統(tǒng)的 zuul 路由功能基本搭建完成。

六、Feign
之前說過了微服務(wù)間,我是通過 Spring 的 RestTemplate 類來相互調(diào)用的,它可通過整合 Ribbon 實(shí)現(xiàn)負(fù)載均衡,但發(fā)現(xiàn)了這樣寫不夠優(yōu)雅,且不夠模板化,因此本篇介紹一下 Feign。
Feign 是一種聲明式、模板化的 HTTP 客戶端,在 Spring Cloud 中使用 Feign 其實(shí)就是創(chuàng)建一個(gè)接口類,它跟普通接口沒啥兩樣,因此通過 Feign 調(diào)用 HTTP 請求,開發(fā)者完全感知不到這是遠(yuǎn)程方法。
1. 整合 Feign
添加 Feign 依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
創(chuàng)建 一個(gè) Feign 接口:
@FeignClient(value = FeignConst.COUPON_PROVIDER, url = "${feign.coupon.url:}")
public interface CouponClient {
@GetMapping(value = "/coupon/list/page", headers = LocalsEncoder.CONTENT_TYPE_LOCALS_GET)
RestResponse couponList(@ModelAttribute CouponCriteria criteria);
}
啟動(dòng) Feign 類
@EnableFeignClients(basePackages = {"com.objcoding"})
@SpringCloudApplication
public class ProviderApplication {
}
2. 服務(wù)降級
當(dāng)網(wǎng)絡(luò)不穩(wěn)定時(shí),一個(gè)接口響應(yīng)非常慢,就會(huì)一直占用這個(gè)連接資源,如果長時(shí)間不做處理,會(huì)導(dǎo)致系統(tǒng)雪崩,幸好,F(xiàn)eign 已經(jīng)繼承了熔斷器 Hystrix
@FeignClient(value = FeignConst.COUPON_PROVIDER, url = "${feign.coupon.url:}", fallback = CouponClient.CouponClientFallBack.class)
public interface CouponClient {
@GetMapping(value = "/coupon/list/page", headers = LocalsEncoder.CONTENT_TYPE_LOCALS_GET)
RestResponse couponList(@ModelAttribute CouponCriteria criteria);
@Component
class CouponClientFallBack implements CouponClient {
@Override
public RestResponse couponList(CouponCriteria criteria) {
return RestResponse.failed("網(wǎng)絡(luò)超時(shí)");
}
}
}
3. 攔截器
有時(shí)候微服務(wù)間的調(diào)用,需要傳遞權(quán)限信息,這些信息都包含在請求頭了,這時(shí)我們可以通過 Feign 攔截器實(shí)現(xiàn)權(quán)限穿透:
@Configuration
public class WebRequestInterceptor {
@Bean
public RequestInterceptor headerInterceptor() {
return template -> {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes == null) {
return;
}
HttpServletRequest request = attributes.getRequest();
Enumeration<String> headerNames = request.getHeaderNames();
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
String values = request.getHeader(name);
template.header(name, values);
}
}
};
}
}
讀者福利:點(diǎn)擊下方傳送門,即可免費(fèi)領(lǐng)取筆者整理的Java后端面試題及Java架構(gòu)師成長路線圖?。?!