介紹:服務網關是微服務架構中一個不可或缺的部分。通過服務網關統一向外系統提供REST API的過程中,除了具備服務路由、均衡負載功能之外,它還具備了權限控制等功能。
Zuul是Netflix開源的微服務網關,他可以和Eureka,Ribbon,Hystrix等組件配合使用。Zuul組件的核心是一系列的過濾器,這些過濾器可以完成以下功能:
#身份認證和安全: 識別每一個資源的驗證要求,並拒絕那些不符的請求
#審查與監控:
#動態路由:動態將請求路由到不同後端集羣
#壓力測試:逐漸增加指向集羣的流量,以瞭解性能
#負載分配:爲每一種負載類型分配對應容量,並棄用超出限定值的請求
#靜態響應處理:邊緣位置進行響應,避免轉發到內部集羣
搭建一個Zuul服務網關
1.新建模塊ZuulGateWay,增加所需依賴包
<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-netflix-zuul</artifactId>
</dependency>
注意,eureka-client導包,是:spring-cloud-starter-netflix-eureka-client
不是:spring-cloud-netflix-eureka-client
2.在程序啓動類增加註解@EnableZuulProxy開啓Zuul
@SpringCloudApplication註解,通過源碼我們看到,它整合了@SpringBootApplication、@EnableDiscoveryClient、@EnableCircuitBreaker,主要目的還是簡化配置。
package com.offcn;
import com.offcn.filter.AccessFilter;
import org.springframework.boot.SpringApplication;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.context.annotation.Bean;
@SpringCloudApplication //組合註解
@EnableZuulProxy
public class ZuulGatwayStarter {
public static void main(String[] args) {
SpringApplication.run(ZuulGatwayStarter.class,args);
}
//初始化過濾器爲bean
@Bean
public AccessFilter createAccessFilter(){
return new AccessFilter();
}
}
3.修改application.yml屬性配置文件
spring:
application:
name: ZUULGATEWAY
server:
port: 80
完成上面的工作後,Zuul已經可以運行了,但是如何讓它爲我們的微服務集羣服務,還需要我們另行配置,下面詳細的介紹一些常用配置內容。
Zuul服務網關路由配置
方式一:通過url直接映射
1.修改項目ZuulGateWay的屬性配置文件application.yml
zuul:
routes:
userprovider001:
path: /userprovider001/**
url: http://localhost:9003/
userprovider002:
path: /userprovider002/**
url: http://localhost:9004/
該配置,定義了,所有到Zuul中的規則爲:/userprovider001/**的訪問都映射到http://localhost:9003/上,也就是說當我們訪問http://localhost/userprovider001/的時候,Zuul會將該請求路由到:http://localhost:9003/上
注意:配置屬性zuul.routes.userprovider001.path中的userprovider001部分爲路由的名字,可以任意定義,但是一組映射關係的path和url要相同
2測試url直接映射方式
(測試該服務的某個接口功能)
http://localhost/userprovider001/user/getAll
方式二:通過在Eureka服務註冊的serviceId進行映射
通過url映射的方式對於Zuul來說,並不是特別友好,Zuul需要知道我們所有微服務的地址,才能完成所有的映射配置。而實際上,我們在實現微服務架構時,服務名與服務實例地址的關係在eureka server中已經存在了,所以只需要將Zuul註冊到eureka server上去發現其他服務,我們就可以實現對serviceId的映射。
1.修改服務網關ZuulGateWay的屬性配置文件application.yml
eureka:
client:
service-url:
defaultZone: http://localhost:10086/eureka,http://localhost:10087/eureka
zuul:
routes:
userprovider:
path: /service/** #自定義訪問規則
service-id: USERPROVIDER #註冊的提供服務名,就不需要再配置服務地址了
2.測試ServiceId映射方式
http://localhost/service/user/getAll
3.修改客戶端調用Zuul網關地址
使用Fegin方式實現接口的
1.修改註解FeignClient
@FeignClient(value = “ZUULGATEWAY”,configuration = feignConfig.class,fallback = UserServiceImpl.class)
2.調用方法按照Zuul定義的規則修改即可
例:
@GetMapping("/service/user/getAll")
public List getAll(…);
Zuul服務網關過濾器使用
在服務網關中定義過濾器只需要繼承ZuulFilter抽象類實現其定義的四個抽象函數就可對請求進行攔截與過濾。
比如下面的例子,定義了一個Zuul過濾器,實現了在請求被路由之前檢查請求中是否有accessToken參數,若有就進行路由,若沒有就拒絕訪問,返回401 Unauthorized錯誤。
1.編寫Zuul過濾器(在模塊ZuulGateWay下)
package com.offcn.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import javax.servlet.http.HttpServletRequest;
public class AccessFilter extends ZuulFilter {
//設置過濾器的類型,決定了過濾器的執行時間
@Override
public String filterType() {
//常見過濾器類型pre 路由請求轉發之前執行 routing 在路由轉發同時執行 post 在routing和error過濾器之後被調用 error:處理請求時發生錯誤時被調用
return "pre";
}
@Override
public int filterOrder() {
return 0; //過濾器的執行順序,數字越小越先執行
}
//開關
@Override
public boolean shouldFilter() {
return true; //true 表示丐蘿氯氣處於可運行狀態 false表示該過濾器不可用
}
//該過濾器做身份驗證
@Override
public Object run() throws ZuulException {
//獲取到當前請求上下文環境
RequestContext context = RequestContext.getCurrentContext();
//從上下文環境獲取當前請求對象
HttpServletRequest request = context.getRequest();
//從請求對象獲取傳遞的憑證
String token = request.getParameter("token");
//判斷憑證是否存在
if(token==null){
//憑證不存在,禁止路由轉發
context.setSendZuulResponse(false);
//提示錯誤狀態碼 401 權限不足的意思
context.setResponseStatusCode(401);
}
return null;
}
}
2.實例化該過濾器
我們只需要在啓動類中增加如下內容:
@Bean
public AccessFilter accessFilter() {
return new AccessFilter();
}
3.測試過濾器
http://localhost/service/user/getAll
發現:
因爲沒有傳token的值,所以被過濾器攔截了
我們再測試:
http://localhost/service/user/getAll?token=token
可以發現能正常訪問該接口了