為什么需要網(wǎng)關(guān)
根據(jù)前面的內(nèi)容嘁灯,我們已經(jīng)能做到下面的內(nèi)容:
- Eureka進(jìn)行服務(wù)的注冊和發(fā)現(xiàn)
- Ribbon進(jìn)行負(fù)載均衡
- 通過Feign趟卸,RestTemplate進(jìn)行服務(wù)的調(diào)用
- Hystrix進(jìn)行服務(wù)降級携龟,隔離故障
- Spring Cloud Config進(jìn)行集群配置
- Spring Cloud Bus進(jìn)行配置的刷新
下面我們要考慮一下這問題:
- 外部應(yīng)用如何來訪問服務(wù)
- 外部應(yīng)用如何進(jìn)行服務(wù)訪問的權(quán)限控制
- 如何將請求均衡分發(fā)給后臺服務(wù)
解決方案:
Spring Cloud Zuul路由是微服務(wù)架構(gòu)的不可或缺的一部分崇堰,提供動態(tài)路由亿虽,監(jiān)控,彈性它呀,安全等的邊緣服務(wù)螺男。
網(wǎng)關(guān)路由
- 在pom.xml文件中增加依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
- 增加注解
@SpringBootApplication
@EnableZuulProxy
@EnableDiscoveryClient
public class ZuulApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(ZuulApplication.class).web(true).run(args);
}
}
- 配置文件中進(jìn)行路由的定義
#這里的配置表示,訪問/a/** 直接重定向到服務(wù)eurekaClient/**
zuul.routes.a.path=/a/**
zuul.routes.a.serviceId=eurekaClient
服務(wù)過濾
- 書寫一個過濾類纵穿,繼承ZuulFilter
public class AccessFilter extends ZuulFilter {
private static Logger log = LoggerFactory.getLogger(AccessFilter.class);
/**
* 返回一個字符串代表過濾器的類型下隧,在zuul中定義了四種不同生命周期的過濾器類型
*
* pre:可以在請求被路由之前調(diào)用
* routing:在路由請求時(shí)候被調(diào)用
* post:在routing和error過濾器之后被調(diào)用
* error:處理請求時(shí)發(fā)生錯誤時(shí)被調(diào)用
*/
@Override
public String filterType() {
return "pre";
}
/**
* 通過int值來定義過濾器的執(zhí)行順序
*/
@Override
public int filterOrder() {
return 0;
}
/**
* 返回一個boolean類型來判斷該過濾器是否要執(zhí)行,所以通過此函數(shù)可實(shí)現(xiàn)過濾器的開關(guān)政恍。
*/
@Override
public boolean shouldFilter() {
return true;
}
/**
* 過濾器的具體邏輯汪拥。需要注意,這里我們通過ctx.setSendZuulResponse(false)令zuul過濾該請求篙耗,不對其進(jìn)行路由迫筑,
* 然后通過ctx.setResponseStatusCode(401)設(shè)置了其返回的錯誤碼,當(dāng)然我們也可以進(jìn)一步優(yōu)化我們的返回宗弯,
* 比如脯燃,通過ctx.setResponseBody(body)對返回body內(nèi)容進(jìn)行編輯等。
*/
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
log.info(String.format("%s request to %s", request.getMethod(), request
.getRequestURL().toString()));
Object accessToken = request.getParameter("accessToken");
if (accessToken == null) {
log.warn("access token is empty");
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(401);
return null;
}
log.info("access token ok");
return null;
}
}
- 這個過濾器需要裝載
// 裝載過濾器
@Bean
public AccessFilter accessFilter() {
return new AccessFilter();
}
- 所有的請求加上accessToken才能正常訪問
下圖為filterType的生命周期示意圖