一、網關概念
1、什么是路由網關
網關是系統的唯一對外的入口,介於客戶端和服務器端之間的中間層,處理非業務功能 提供路由請求、鑒權、監控、緩存、限流等功能。它將"1對N"問題轉換成了"1對1”問題。
通過服務路由的功能,可以在對外提供服務時,只暴露 網關中配置的調用地址,而調用方就不需要了解后端具體的微服務主機。
2、為什么要使用微服務網關
不同的微服務一般會有不同的網絡地址,而客戶端可能需要調用多個服務接口才能完成一個業務需求,若讓客戶端直接與各個微服務通信,會有以下問題:
(1)客戶端會多次請求不同微服務,增加了客戶端復雜性
(2)存在跨域請求,處理相對復雜
(3)認證復雜,每個服務都需要獨立認證
(4)難以重構,多個服務可能將會合並成一個或拆分成多個
3、網關的優點
微服務網關介於服務端與客戶端的中間層,所有外部服務請求都會先經過微服務網關客戶只能跟微服務網關進行交互,無需調用特定微服務接口,使得開發得到簡化
總的理解網關優點
服務網關 = 路由轉發 + 過濾器
(1)路由轉發:接收一切外界請求,轉發到后端的微服務上去。
(2)過濾器:在服務網關中可以完成一系列的橫切功能,例如權限校驗、限流以及監控等,這些都可以通過過濾器完成(其實路由轉發也是通過過濾器實現的)。
4、服務網關技術選型
引入服務網關后的微服務架構如上,總體包含三部分:服務網關、open-service和service。
(1)總體流程
服務網關、open-service和service啟動時注冊到注冊中心上去;
用戶請求時直接請求網關,網關做智能路由轉發(包括服務發現,負載均衡)到open-service,這其中包含權限校驗、監控、限流等操作
open-service聚合內部service響應,返回給網關,網關再返回給用戶
(2)引入網關的注意點
增加了網關,多了一層轉發(原本用戶請求直接訪問open-service即可),性能會下降一些(但是下降不大,通常,網關機器性能會很好,而且網關與open-service的訪問通常
是內網訪問,速度很快);
(3)服務網關基本功能
智能路由:接收外部一切請求,並轉發到后端的對外服務open-service上去;
注意:我們只轉發外部請求,服務之間的請求不走網關,這就表示全鏈路追蹤、內部服務API監控、內部服務之間調用的容錯、智能路由不能在網關完成;
當然,也可以將所有的服務調用都走網關,那么幾乎所有的功能都可以集成到網關中,但是這樣的話,網關的壓力會很大,不堪重負。
權限校驗:可在微服務網關上進行認證,然后在將請求轉發給微服務,無須每個微服務都進行認證,不校驗服務內部的請求。服務內部的請求有必要校驗嗎?
API監控:只監控經過網關的請求,以及網關本身的一些性能指標(例如,gc等);
限流:與監控配合,進行限流操作;
API日志統一收集:類似於一個aspect切面,記錄接口的進入和出去時的相關日志。
二、Zuul項目搭建
使用到的組件包括:Eureka、Feign、Zuul,包括以下四個項目:
(1)Eureka-server: 7001 注冊中心
(2)product-server :8001 商品微服務
(3)order-server : 9001 訂單微服務
(4)zuul-gateway : 6001 Zuul網關
注冊中心、商品微服務、order在之前博客都已搭建,這里就不重復寫。這里只寫zuul-gateway微服務。
1、pom.xml
<!--客戶端jar包,這個在訂單微服務,商品微服務都要添加--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!--zuuljar包--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency>
2、application.yml
server:
port: 9999
#服務的名稱
spring:
application:
name: api-gateway
zuul:
#自定義路由轉發
routes:
order-service: /loveliuqianqian/order/**
product-service: /loveliuqianqian/product/**
#環境隔離配置:不想讓默認的服務對外暴露接口(就是下面配置的路徑進行攔截不能訪問)
#ignored-patterns:
# /*-service/api/v1/order/save
3、SpringBoot啟動類
@SpringBootApplication
//網關注解
@EnableZuulProxy
public class ApiGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ApiGatewayApplication.class, args);
}
}
4、測試
(1)直接訂單服務調商品服務
(2)通過Zuul網關實現訂單接口調商品服務
5、自定義Zuul過濾器實現登錄鑒權實戰
(1)在網關服務項目中獎勵一個filter 繼承ZuulFilter
@Component
public class LoginFilter extends ZuulFilter {
// 前置過濾器
@Override
public String filterType() {
return PRE_TYPE;
}
//設置過濾器級別
@Override
public int filterOrder() {
return 4;
}
//過濾器是否生效
@Override
public boolean shouldFilter() {
//zool 使用時,上下文對象RequestContext,是共享的。 所以通過RequestContext 獲取值
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
//設置需要攔截的路徑
if ("/loveliuqianqian/order/api/v1/order/save".equalsIgnoreCase(request.getRequestURI())) {
//攔住了
return true;
}
//放行
return false;
}
//業務邏輯
@Override
public Object run() throws ZuulException {
//zool 使用時,上下文對象RequestContext,是共享的。 所以通過RequestContext 獲取值
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
String token = request.getHeader("token");
String cookie = request.getHeader("Cookie");
if (StringUtils.isBlank(token)) {
token = request.getParameter("token");
cookie = request.getParameter("Cookie");
}
//如果token 等於null 返回狀態碼和沒有權限的信息給前台
if (StringUtils.isBlank(token)) {
requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
}
//(JWT)解析token 獲取權限列表 鑒權。
return null;
}
}
7、高級篇幅之高並發情況下接口限流特技
(1)借助谷歌guava框架介紹,網關限流使用(下面這個filte 可以和上面鑒權的過濾器結合使用,具體看自己業務就行,下面代碼中加黑的就是限流的主要代碼)
@Component
public class OrderRateLimiterFilter extends ZuulFilter {
//每秒產生500個令牌(請求進來后會拿去一個令牌,如果沒拿到就不讓訪問。)
private static final RateLimiter RATE_LIMITER = RateLimiter.create(1000);
@Override
public String filterType() {
return PRE_TYPE;
}
@Override
public int filterOrder() {
return -4;
}
@Override
public boolean shouldFilter() {
//zool 使用時,上下文對象RequestContext,是共享的。 所以通過RequestContext 獲取值
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
//設置需要攔截的路徑
if ("/loveliuqianqian/order/api/v1/order/save".equalsIgnoreCase(request.getRequestURI())) {
//攔住了
return true;
}
//放行
return false;
}
@Override
public Object run() throws ZuulException {
//zool 使用時,上下文對象RequestContext,是共享的。 所以通過RequestContext 獲取值
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
String token = request.getHeader("token");
if (StringUtils.isBlank(token)) {
token = request.getParameter("token");
}
//限流 如果qps 訪問過高(超過上面定義的,就停止訪問)
if (!RATE_LIMITER.tryAcquire()||StringUtils.isBlank(token)) {
//如果token 等於null 返回狀態碼和沒有權限的信息給前台
requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
}
//(JWT)解析token 獲取權限列表 鑒權。
return null;
}
}
5、注意些細節
(1)url不能重復,否則會覆蓋
order-service: /apigateway/order/** product-service: /apigateway/product/**
(2)通過zuul后,request中的cookie值獲取不到,那是因為網關給過濾掉了。
@RequestMapping("list") public void list(@RequestParam("user_id")int userId, @RequestParam("product_id") int productId, HttpServletRequest request){ String token = request.getHeader("token"); String cookie = request.getHeader("cookie"); //會發現token值能夠獲取,cookie無法獲取,原因是因為網關會過濾掉敏感詞 System.out.println("token="+token); System.out.println("cookie="+cookie); }
想要不過濾掉cookie值那么在配置里配置
zuul: #處理http請求頭為空的問題 sensitive-headers:
6、問題
自己遇到兩個問題記錄下,后期再來思考解決。
1、現在通過訂單服務地址可以直接訪問訂單微服務,如何配置成訂單微服務不能直接服務,只能通過網關訪問。
思考,是不是以后訂單微服務配置到內網就不會有這個問題了。
2、當我的訂單服務調商品服務異常時,直接訪問訂單微服務熔斷降級能夠完成,通過網關竟然直接報異常了。
我在商品微服務相關接口添加:
//睡眠兩秒,微服務默認一秒就超時,所以會到降級方法 TimeUnit.SECONDS.sleep(2);
直接調訂單服務,降級信息返回正常。如果通過網關訪問。
返回的是異常,這不是很蛋疼嗎,總是有解決辦法讓降級信息返回來的,以后解決來再來寫。
上面的代碼如果需要 可以留言給我 ,無條件發給你 。我這是微服務一套的代碼,記住需要看前面幾篇文章,你會理解的更好。