1、 什么是路由网关
网关是系统的唯一对外的入口,介于客户端和服务器端之间的中间层,处理非业务功能 提供路由请求、鉴权、监控、缓存、限流等功能。它将"1对N"问题转换成了"1对1”问题。
通过服务路由的功能,可以在对外提供服务时,只暴露 网关中配置的调用地址,而调用方就不需要了解后端具体的微服务主机。
2、为什么要使用微服务网关
不同的微服务一般会有不同的网络地址,而客户端可能需要调用多个服务接口才能完成一个业务需求,若让客户端直接与各个微服务通信,会有以下问题:
(1)客户端会多次请求不同微服务,增加了客户端复杂性
(2)存在跨域请求,处理相对复杂
(3)认证复杂,每个服务都需要独立认证
(4)难以重构,多个服务可能将会合并成一个或拆分成多个
3、网关的优点
微服务网关介于服务端与客户端的中间层,所有外部服务请求都会先经过微服务网关客户只能跟微服务网关进行交互,无需调用特定微服务接口,使得开发得到简化
总的理解网关优点
服务网关 = 路由转发 + 过滤器
(1)路由转发:接收一切外界请求,转发到后端的微服务上去。
(2)过滤器:在服务网关中可以完成一系列的横切功能,例如权限校验、限流以及监控等,这些都可以通过过滤器完成(其实路由转发也是通过过滤器实现的)。
4、Zuul项目搭建
使用到的组件包括:Eureka、Feign、Zuul,包括以下四个项目:
(1)Eureka-server: 8761 注册中心
(2)product-server :8771 商品微服务
(3)order-server : 8781 订单微服务
(4)zuul-gateway : 9000 Zuul网关
- 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、启动类加入注解
@SpringBootApplication
@EnableZuulProxy
public class ApiGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ApiGatewayApplication.class, args);
}
}
- 3、配置文件
server:
port: 9000
#服务的名称
spring:
application:
name: api-gateway
#指定注册中心地址
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
注册中心可以看到注册的网关:

正常访问order-service的路径:

通过网关访问order-service的路径:

默认访问规则 http://gateway:port/service-id/**
例子:默认 /order-service/api/v1/order/save?user_id=2&product_id=1
可修改配置
#自定义路由转发
zuul:
routes:
order-service: /gateway-order/**
#忽略整个服务
#ignored-services: product-service
#忽略一定规则的服务
#ignored-patterns: /*-service/**
如上配置可用下方方式访问

5、Zuul常用问题分析
1、路由名称定义问题: 路由映射重复会覆盖
如下 这样定义 会覆盖 只有product-service生效
zuul:
routes:
order-service: /gateway-order/**
product-service: /gateway-order/**
2、Http请求头过滤问题

zuul:
sensitive-headers:
5、Zuul流程

6、自定义Zuul过滤器
-
权限校验
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;
}
/**
* 过滤器执行顺序 数字越小越先执行
* @return
*/
@Override
public int filterOrder() {
return 4;
}
/**
* 是否进行过滤
* @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、关键说明
(1)方法说明
filterType : filter类型,分为pre、error、post、 route
filterOrder: filter执行顺序,通过数字指定,数字越小,执行顺序越先
shouldFilter: filter是否需要执行 true执行 false 不执行
run : filter具体逻辑(上面为true那么这里就是具体执行逻辑)
(2)filter类型说明
pre: 请求执行之前filter
route: 处理请求,进行路由
post: 请求处理完成后执行的filter
error: 出现错误时执行的filter
3、测试
(1)该路径访问被拦截

(2)该路径可以正常访问

-
接口限流

它的大致意思就是每一个请求进来先到桶里去拿令牌,拿到令牌的请求放行,假设你设置了1000个令牌,如果拿完了,那么后面来调接口的请求就需要排队等有新的令牌才能调用该接口。
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 {
//每秒产生1000个令牌
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();
//只针对此接口限流
if("/gateway-order/api/v1/order/saveByFeign".equalsIgnoreCase(request.getRequestURI())){
return true;
}
return false;
}
@Override
public Object run() throws ZuulException {
RequestContext currentContext = RequestContext.getCurrentContext();
//就相当于每调用一次tryAcquire()方法,令牌数量减1,当1000个用完后,那么后面进来的用户无法访问上面接口
//当然这里只写类上面一个接口,可以这么写,实际可以在这里要加一层接口判断。
if(!RATE_LIMTER.tryAcquire()){
currentContext.setSendZuulResponse(false);
currentContext.setResponseStatusCode(HttpStatus.SC_REQUEST_TOO_LONG);
}
return null;
}
}
网友评论