1、 什么是路由網(wǎng)關(guān)
網(wǎng)關(guān)是系統(tǒng)的唯一對(duì)外的入口,介于客戶端和服務(wù)器端之間的中間層,處理非業(yè)務(wù)功能 提供路由請(qǐng)求、鑒權(quán)、監(jiān)控、緩存、限流等功能。它將"1對(duì)N"問題轉(zhuǎn)換成了"1對(duì)1”問題。
通過服務(wù)路由的功能,可以在對(duì)外提供服務(wù)時(shí),只暴露 網(wǎng)關(guān)中配置的調(diào)用地址,而調(diào)用方就不需要了解后端具體的微服務(wù)主機(jī)。
2、為什么要使用微服務(wù)網(wǎng)關(guān)
不同的微服務(wù)一般會(huì)有不同的網(wǎng)絡(luò)地址,而客戶端可能需要調(diào)用多個(gè)服務(wù)接口才能完成一個(gè)業(yè)務(wù)需求,若讓客戶端直接與各個(gè)微服務(wù)通信,會(huì)有以下問題:
(1)客戶端會(huì)多次請(qǐng)求不同微服務(wù),增加了客戶端復(fù)雜性
(2)存在跨域請(qǐng)求,處理相對(duì)復(fù)雜
(3)認(rèn)證復(fù)雜,每個(gè)服務(wù)都需要獨(dú)立認(rèn)證
(4)難以重構(gòu),多個(gè)服務(wù)可能將會(huì)合并成一個(gè)或拆分成多個(gè)
3、網(wǎng)關(guān)的優(yōu)點(diǎn)
微服務(wù)網(wǎng)關(guān)介于服務(wù)端與客戶端的中間層,所有外部服務(wù)請(qǐng)求都會(huì)先經(jīng)過微服務(wù)網(wǎng)關(guān)客戶只能跟微服務(wù)網(wǎng)關(guān)進(jìn)行交互,無(wú)需調(diào)用特定微服務(wù)接口,使得開發(fā)得到簡(jiǎn)化
總的理解網(wǎng)關(guān)優(yōu)點(diǎn)
服務(wù)網(wǎng)關(guān) = 路由轉(zhuǎn)發(fā) + 過濾器
(1)路由轉(zhuǎn)發(fā):接收一切外界請(qǐng)求,轉(zhuǎn)發(fā)到后端的微服務(wù)上去。
(2)過濾器:在服務(wù)網(wǎng)關(guān)中可以完成一系列的橫切功能,例如權(quán)限校驗(yàn)、限流以及監(jiān)控等,這些都可以通過過濾器完成(其實(shí)路由轉(zhuǎn)發(fā)也是通過過濾器實(shí)現(xiàn)的)。
4、Zuul項(xiàng)目搭建
使用到的組件包括:Eureka、Feign、Zuul,包括以下四個(gè)項(xiàng)目:
(1)Eureka-server: 8761 注冊(cè)中心
(2)product-server :8771 商品微服務(wù)
(3)order-server : 8781 訂單微服務(wù)
(4)zuul-gateway : 9000 Zuul網(wǎng)關(guān)
- 1、加入依賴
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.yuan</groupId>
<artifactId>api-gateway</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>api-gateway</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
</properties>
<dependencies>
<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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- 2、啟動(dòng)類加入注解
@SpringBootApplication
@EnableZuulProxy
public class ApiGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ApiGatewayApplication.class, args);
}
}
- 3、配置文件
server:
port: 9000
#服務(wù)的名稱
spring:
application:
name: api-gateway
#指定注冊(cè)中心地址
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
注冊(cè)中心可以看到注冊(cè)的網(wǎng)關(guān):

正常訪問order-service的路徑:

通過網(wǎng)關(guān)訪問order-service的路徑:

默認(rèn)訪問規(guī)則 http://gateway:port/service-id/**
例子:默認(rèn) /order-service/api/v1/order/save?user_id=2&product_id=1
可修改配置
#自定義路由轉(zhuǎn)發(fā)
zuul:
routes:
order-service: /gateway-order/**
#忽略整個(gè)服務(wù)
#ignored-services: product-service
#忽略一定規(guī)則的服務(wù)
#ignored-patterns: /*-service/**
如上配置可用下方方式訪問

5、Zuul常用問題分析
1、路由名稱定義問題: 路由映射重復(fù)會(huì)覆蓋
如下 這樣定義 會(huì)覆蓋 只有product-service生效
zuul:
routes:
order-service: /gateway-order/**
product-service: /gateway-order/**
2、Http請(qǐng)求頭過濾問題

zuul:
sensitive-headers:
5、Zuul流程

6、自定義Zuul過濾器
-
權(quán)限校驗(yàn)
1、LoginFilter類
package com.yuan.apigateway.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpStatus;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;
@Component
public class LoginFilter extends ZuulFilter {
/**
* 過濾器類型 前置過濾器
* @return
*/
@Override
public String filterType() {
return PRE_TYPE;
}
/**
* 過濾器執(zhí)行順序 數(shù)字越小越先執(zhí)行
* @return
*/
@Override
public int filterOrder() {
return 4;
}
/**
* 是否進(jìn)行過濾
* @return
*/
@Override
public boolean shouldFilter() {
RequestContext currentContext = RequestContext.getCurrentContext();
HttpServletRequest request = currentContext.getRequest();
String requestURI = request.getRequestURI();
//只攔截此路徑
if("/gateway-order/api/v1/order/saveByFeign".equalsIgnoreCase(requestURI)){
return true;
}
return false;
}
@Override
public Object run() throws ZuulException {
System.out.println("攔截啦");
RequestContext currentContext = RequestContext.getCurrentContext();
HttpServletRequest request = currentContext.getRequest();
String token = request.getHeader("token");
if(StringUtils.isBlank(token)){
token=request.getParameter("token");
}
if(StringUtils.isBlank(token)){
currentContext.setSendZuulResponse(false);
currentContext.setResponseStatusCode(HttpStatus.SC_UNAUTHORIZED);
}
return null;
}
2、關(guān)鍵說明
(1)方法說明
filterType : filter類型,分為pre、error、post、 route
filterOrder: filter執(zhí)行順序,通過數(shù)字指定,數(shù)字越小,執(zhí)行順序越先
shouldFilter: filter是否需要執(zhí)行 true執(zhí)行 false 不執(zhí)行
run : filter具體邏輯(上面為true那么這里就是具體執(zhí)行邏輯)
(2)filter類型說明
pre: 請(qǐng)求執(zhí)行之前filter
route: 處理請(qǐng)求,進(jìn)行路由
post: 請(qǐng)求處理完成后執(zhí)行的filter
error: 出現(xiàn)錯(cuò)誤時(shí)執(zhí)行的filter
3、測(cè)試
(1)該路徑訪問被攔截

(2)該路徑可以正常訪問

-
接口限流

它的大致意思就是每一個(gè)請(qǐng)求進(jìn)來(lái)先到桶里去拿令牌,拿到令牌的請(qǐng)求放行,假設(shè)你設(shè)置了1000個(gè)令牌,如果拿完了,那么后面來(lái)調(diào)接口的請(qǐng)求就需要排隊(duì)等有新的令牌才能調(diào)用該接口。
package com.yuan.apigateway.filter;
import com.google.common.util.concurrent.RateLimiter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.http.HttpStatus;
import javax.servlet.http.HttpServletRequest;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;
public class OrderRateLimiterFilter extends ZuulFilter {
//每秒產(chǎn)生1000個(gè)令牌
private static RateLimiter RATE_LIMTER=RateLimiter.create(1000);
@Override
public String filterType() {
return PRE_TYPE;
}
@Override
public int filterOrder() {
return -4;
}
@Override
public boolean shouldFilter() {
RequestContext currentContext = RequestContext.getCurrentContext();
HttpServletRequest request = currentContext.getRequest();
//只針對(duì)此接口限流
if("/gateway-order/api/v1/order/saveByFeign".equalsIgnoreCase(request.getRequestURI())){
return true;
}
return false;
}
@Override
public Object run() throws ZuulException {
RequestContext currentContext = RequestContext.getCurrentContext();
//就相當(dāng)于每調(diào)用一次tryAcquire()方法,令牌數(shù)量減1,當(dāng)1000個(gè)用完后,那么后面進(jìn)來(lái)的用戶無(wú)法訪問上面接口
//當(dāng)然這里只寫類上面一個(gè)接口,可以這么寫,實(shí)際可以在這里要加一層接口判斷。
if(!RATE_LIMTER.tryAcquire()){
currentContext.setSendZuulResponse(false);
currentContext.setResponseStatusCode(HttpStatus.SC_REQUEST_TOO_LONG);
}
return null;
}
}