服务网关Zuul
(Spring Cloud高级)
一、什么是网关服务
zuul 是netflix开源的一个API Gateway 服务器, 本质上是一个web servlet应用。
Zuul 在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。Zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门。
zuul的例子可以参考netflix在github上的 simple webapp,可以按照netflix 在github wiki 上文档说明来进行使用。
服务网关 = 路由转发 + 过滤器
1、路由转发:接收一切外界请求,转发到后端的微服务上去;
2、过滤器:在服务网关中可以完成一系列的横切功能,例如权限校验、限流以及监控等,这些都可以通过过滤器完成(其实路由转发也是通过过滤器实现的)。
1为什么要使用网关
上述所说的横切功能(以权限校验为例)可以写在三个位置:
- 每个服务自己实现一遍
- 写到一个公共的服务中,然后其他所有服务都依赖这个服务
- 写到服务网关的前置过滤器中,所有请求过来进行权限校验
第一种,缺点太明显,基本不用;
第二种,相较于第一点好很多,代码开发不会冗余,但是有两个缺点:
由于每个服务引入了这个公共服务,那么相当于在每个服务中都引入了相同的权限校验的代码,使得每个服务的jar包大小无故增加了一些,尤其是对于使用docker镜像进行部署的场景,jar越小越好;
由于每个服务都引入了这个公共服务,那么我们后续升级这个服务可能就比较困难,而且公共服务的功能越多,升级就越难,而且假设我们改变了公共服务中的权限校验的方式,想让所有的服务都去使用新的权限校验方式,我们就需要将之前所有的服务都重新引包,编译部署。
而服务网关恰好可以解决这样的问题:
将权限校验的逻辑写在网关的过滤器中,后端服务不需要关注权限校验的代码,所以服务的jar包中也不会引入权限校验的逻辑,不会增加jar包大小;
如果想修改权限校验的逻辑,只需要修改网关中的权限校验过滤器即可,而不需要升级所有已存在的微服务。
所以,需要服务网关!!!
2 网关解决了什么问题
二、 编写网关服务入门案例
1 创建项目
2修改pom文件
<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>1.5.13.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.bjsxt</groupId>
<artifactId>zuul-gateway</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR5</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>
3修改配置文件
spring.application.name=zuul-gateway
server.port=9020
#设置服务注册中心地址,指向另一个注册中心
eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/
4修改启动类
@EnableZuulProxy注解的作用是什么?
Sets up a Zuul server endpoint and installs some reverse proxy filters in it, so it can forward requests to backend servers. The backends can be registered manually through configuration or via DiscoveryClient.
package com.bjsxt;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@EnableZuulProxy
@SpringBootApplication
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
}
5通过网关请求服务
- 启动该网关服务
- 启动e-book-product-provider服务
- 访问http://localhost:9001/product/findAll
- 访问http://localhost:9020/e-book-product-provider/product/findAll
http://网关服务地址:网关服务端口/访问的服务的名称/访问的服务中的接口的地址
三、路由器的4种路由规则方法
1创建项目
Pom文件:
<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>1.5.13.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.bjsxt</groupId>
<artifactId>zuul-gateway-route</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR5</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>
启动类:
package com.bjsxt;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@EnableZuulProxy
@SpringBootApplication
public class ZuulRouteApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulRouteApplication.class, args);
}
}
2采用URL指定路由方式
2.1修改配置文件配置路由规则
spring.application.name=zuul-gateway-route
server.port=9030
#设置服务注册中心地址,指向另一个注册中心
eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/
# 1 ###################### 路由指定:URL指定 #############################
# URL匹配关键字,如果包含关键字就跳转到指定的URL中
# 下面第二个点到第三个点之间的是服务名
zuul.routes.e-book-product-provider.path=/e-book-product-provider/**
zuul.routes.e-book-product-provider.url=http://127.0.0.1:9001/
2.2测试
- 启动zuul-gateway-route
- 启动e-book-product-provider
- 访问http://localhost:9030/e-book-product-provider/product/findAll
2.3通配符含义
3采用服务名称指定路由方式
方式一:
spring.application.name=zuul-gateway-route
server.port=9030
#设置服务注册中心地址,指向另一个注册中心
eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/
# 1 ###################### 路由指定:URL指定 #############################
# URL匹配关键字,如果包含关键字就跳转到指定的URL中
# 下面第二个点到第三个点之间的是服务名
#zuul.routes.e-book-product-provider.path=/e-book-product-provider/**
#zuul.routes.e-book-product-provider.url=http://127.0.0.1:9001/
## 2 ###################### 路由指定:服务指定1 #############################
##将路径的/suibian/引到eureka的e-book-product-provider服务上
##规则:zuul.routes.路径名.path 这个路径名可以随便,但是一般使用服务名
##规则:zuul.routes.路径名.serviceId=eureka的服务名
zuul.routes.e-book-product-provider.path=/suibian/**
zuul.routes.e-book-product-provider.serviceId=e-book-product-provider
测试:
- 启动e-book-product-provider
- 启动zuul-gateway-route
- 访问http://localhost:9030/suibian/product/findAll
方式二:
spring.application.name=zuul-gateway-route
server.port=9030
#设置服务注册中心地址,指向另一个注册中心
eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/
# 1 ###################### 路由指定:URL指定 #############################
# URL匹配关键字,如果包含关键字就跳转到指定的URL中
# 下面第二个点到第三个点之间的是服务名
#zuul.routes.e-book-product-provider.path=/e-book-product-provider/**
#zuul.routes.e-book-product-provider.url=http://127.0.0.1:9001/
## 2 ###################### 路由指定:服务指定1 #############################
##将路径的/suibian/引到eureka的e-book-product-provider服务上
##规则:zuul.routes.路径名.path 这个路径名可以随便,但是一般使用服务名
##规则:zuul.routes.路径名.serviceId=eureka的服务名
#zuul.routes.e-book-product-provider.path=/suibian/**
#zuul.routes.e-book-product-provider.serviceId=e-book-product-provider
## 3 ###################### 路由指定:服务指定2 #############################
#zuul.routes后面跟着的是服务名,服务名后面跟着的是路径规则,这种配置方式更简单。
zuul.routes.e-book-product-provider.path=/suibian/*
测试:
- 启动e-book-product-provider
- 启动zuul-gateway-route
- 访问http://localhost:9030/suibian/product/findAll
4路由的排除方法
排除路由,也就是该路由器会忽悠某几个服务,即使客户端发送了请求也访问不了
spring.application.name=zuul-gateway-route
server.port=9030
#设置服务注册中心地址,指向另一个注册中心
eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/
## 4 ###################### 路由排除:排除某几个服务#############################
##排除后,这个地址将为空http://127.0.0.1:9030/e-book-product-provider/product/findAll
## 多个服务逗号隔开
zuul.ignored-services=e-book-product-provider
测试:
- 启动e-book-product-provider
- 启动zuul-gateway-route
- 访问http://localhost:9030/e-book-product-provider/product/findAll
直接404,但是还是可以通过http://127.0.0.1:9001/product/findAll来直接访问原本的服务
如果有多个服务要排除,服务名称通过","连接
由于服务太多,不可能手工一个个加,故路由排除所有服务,然后针对要路由的服务进行手工加
spring.application.name=zuul-gateway-route
server.port=9030
#设置服务注册中心地址,指向另一个注册中心
eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/
## 5 ###################### 路由排除:排除所有服务#############################
#由于服务太多,不可能手工一个个加,故路由排除所有服务,然后针对要路由的服务进行手工加
zuul.ignored-services=*
zuul.routes.e-book-order-provider.path=/e-book-order-provider/**
测试:
- 启动e-book-product-provider
- 启动e-book-order-provider
- 启动zuul-gateway-route
- 访问http://localhost:9030/e-book-product-provider/product/findAll
因为被屏蔽了,并且只开启了order服务
- 访问http://localhost:9030/e-book-order-provider/order/findAll
也可以通过排除指定关键字的路径
spring.application.name=zuul-gateway-route
server.port=9030
#设置服务注册中心地址,指向另一个注册中心
eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/
## 6 ###################### 路由排除:排除指定关键字的路径#############################
# 排除所有包括/list/的路径
zuul.ignored-patterns=/**/findAll/**
zuul.routes.e-book-order-provider.path=/suibian/**
测试:
- 启动e-book-order-provider
- 启动zuul-gateway-route
- 访问http://localhost:9030/suibian/order/findAll
- 访问http://localhost:9030/suibian/order/findOrderById?orderid=1011
5路由的添加前缀方法
修改zuul-gateway-route配置文件:
spring.application.name=zuul-gateway-route
server.port=9030
#设置服务注册中心地址,指向另一个注册中心
eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/
## 7 ############### 路由添加前缀,为所有路径添加前缀 ############
## http://127.0.0.1:9030/e-book-product-provider/product/findAll
## 得改为
## http://127.0.0.1:9030/api/e-book-product-provider/product/findAll
zuul.prefix=/api
zuul.routes.e-book-product-provider.path=/e-book-product-provider/**
测试:
- 启动e-book-product-provider
- 启动zuul-gateway-route
- 访问http://127.0.0.1:9030/api/e-book-product-provider/product/findAll
- 访问http://127.0.0.1:9030/e-book-product-provider/product/findAll
四、自定义网关过滤器
1编写网关过滤器
1.1创建项目
1.2修改pom文件
<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>1.5.13.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.bjsxt</groupId>
<artifactId>zuul-gateway-filter</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR5</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>
1.3修改配置文件
spring.application.name=zuul-gateway-filter
server.port=9020
#设置服务注册中心地址,指向另一个注册中心
eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/
1.4创建过滤器
package com.bjsxt.filter;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
@Component
public class LogFilter extends ZuulFilter {
private Logger logger = LoggerFactory.getLogger(LogFilter.class);
//是否开启过滤器:默认为false不开启
@Override
public boolean shouldFilter() {
return true;
}
//过滤内容:在run方法编写过滤逻辑
@Override
public Object run() {
//
RequestContext currentContext = RequestContext.getCurrentContext();
HttpServletRequest request = currentContext.getRequest();
http://logger.info("LogFilter.....method={},url={}",request.getMethod(),request.getRequestURI());
return null;
}
//过滤器类型:通过过滤器类型决定了过滤器执行的时间
@Override
public String filterType() {
return "pre";
}
//过滤器的执行顺序:通过整数表示顺序,数值越小,优先级越高
@Override
public int filterOrder() {
return 0;
}
}
1.5修改启动类
package com.bjsxt;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@EnableZuulProxy
@SpringBootApplication
public class ZuulFilterApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulFilterApplication.class, args);
}
}
1.6测试
- 启动e-book-product-provider
- 启动zuul-gateway-filter
- 访问http://localhost:9020/e-book-product-provider/product/findAll
- 查看控制台
2过滤器类型
Routing:路由规则去请求服务去了,这个时候就会执行
3 Zuul 请求的生命周期
4采用网关过滤器实现权限验证
需求:在网关过滤器中通过Token判断用户是否登录
再次:说一下token的原理:
Token就是你登陆的时候,我在redis或者其他分布式缓存中添加token的信息,并且将token的键作为cookie返回回去。下次请求时,先判断有没有token,然后去redis里面查询判断token对不对,然后决定是否要进行请求转发
4.1创建项目
4.2修改pom文件添zuul坐标
<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>1.5.13.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.bjsxt</groupId>
<artifactId>zuul-gateway-example</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR5</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>
4.3修改配置文件
spring.application.name=zuul-gateway-example
server.port=9020
#设置服务注册中心地址,指向另一个注册中心
eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/
4.4创建AccessFilter
package com.bjsxt.filter;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
@Component
public class AccessFilter extends ZuulFilter {
private static final Logger logger = LoggerFactory.getLogger(AccessFilter.class);
//是否开启过滤器:默认为false不开启
@Override
public boolean shouldFilter() {
return true;
}
//过滤内容:在run方法编写过滤逻辑
@Override
public Object run() {
//获取请求上下文
RequestContext currentContext = RequestContext.getCurrentContext();
HttpServletRequest request = currentContext.getRequest();
http://logger.info("--------------pre1--------------------");
//获取表单中的token
String token = request.getParameter("token");
//对token做判断
if(token==null) {
logger.warn("token is null..................");
//代表请求结束。不在继续向下请求
currentContext.setSendZuulResponse(false);
//添加一个响应的状态码
currentContext.setResponseStatusCode(401);
//响应内容
currentContext.setResponseBody("{result:"token is null"}");
//响应类型
currentContext.getResponse().setContentType("text/html;charset=utf-8");
}else {
//访问redis服务 进行验证(看看登陆是否过期)
http://logger.info("token is ok...................");
}
return null;
}
//过滤器类型:通过过滤器类型决定了过滤器执行的时间
@Override
public String filterType() {
return "pre";
}
//过滤器的执行顺序:通过整数表示顺序,数值越小,优先级越高
@Override
public int filterOrder() {
return 0;
}
}
4.5修改启动类
package com.bjsxt;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@EnableZuulProxy
@SpringBootApplication
public class ZuulExampleApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulExampleApplication.class, args);
}
}
4.6测试
- 启动zuul-gateway-example
- 启动e-book-product-provider
- 访问http://localhost:9020/e-book-product-provider/product/findAll
- 访问http://localhost:9020/e-book-product-provider/product/findAll?token=sad
5网关过滤器执行顺序与post类型演示
5.1网关过滤器执行顺序演示
5.1.1AccessFilter
package com.bjsxt.filter;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
@Component
public class AccessFilter extends ZuulFilter {
private static final Logger logger = LoggerFactory.getLogger(AccessFilter.class);
//是否开启过滤器:默认为false不开启
@Override
public boolean shouldFilter() {
return true;
}
//过滤内容:在run方法编写过滤逻辑
@Override
public Object run() {
//获取请求上下文
RequestContext currentContext = RequestContext.getCurrentContext();
HttpServletRequest request = currentContext.getRequest();
http://logger.info("--------------pre1--------------------");
//获取表单中的token
String token = request.getParameter("token");
//对token做判断
if(token==null) {
logger.warn("token is null..................");
//代表请求结束。不在继续向下请求
currentContext.setSendZuulResponse(false);
//添加一个响应的状态码
currentContext.setResponseStatusCode(401);
//响应内容
currentContext.setResponseBody("{result:"token is null"}");
//响应类型
currentContext.getResponse().setContentType("text/html;charset=utf-8");
}else {
//访问redis服务 进行验证(看看登陆是否过期)
http://logger.info("token is ok...................");
}
return null;
}
//过滤器类型:通过过滤器类型决定了过滤器执行的时间
@Override
public String filterType() {
return "pre";
}
//过滤器的执行顺序:通过整数表示顺序,数值越小,优先级越高
@Override
public int filterOrder() {
return 0;
}
}
5.1.2AccessFilter2
package com.bjsxt.filter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.netflix.zuul.ZuulFilter;
@Component
public class AccessFilter2 extends ZuulFilter {
private static final Logger logger = LoggerFactory.getLogger(AccessFilter2.class);
//是否开启过滤器:默认为false不开启
@Override
public boolean shouldFilter() {
return true;
}
//过滤内容:在run方法编写过滤逻辑
@Override
public Object run() {
logger.info("--------------pre2--------------------");
return null;
}
//过滤器类型:通过过滤器类型决定了过滤器执行的时间
@Override
public String filterType() {
return "pre";
}
//过滤器的执行顺序:通过整数表示顺序,数值越小,优先级越高
@Override
public int filterOrder() {
return 1;
}
}
5.2post类型演示
package com.bjsxt.filter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.netflix.zuul.ZuulFilter;
@Component
public class PostFilter extends ZuulFilter {
private static final Logger logger = LoggerFactory.getLogger(PostFilter.class);
//是否开启过滤器:默认为false不开启
@Override
public boolean shouldFilter() {
return true;
}
//过滤内容:在run方法编写过滤逻辑
@Override
public Object run() {
http://logger.info("--------------post--------------------");
return null;
}
//过滤器类型:通过过滤器类型决定了过滤器执行的时间
@Override
public String filterType() {
return "post";
}
//过滤器的执行顺序:通过整数表示顺序,数值越小,优先级越高
@Override
public int filterOrder() {
return 1;
}
}
测试前面三个filter:
- 启动zuul-gateway-example
- 启动e-book-product-provider
- 访问http://localhost:9020/e-book-product-provider/product/findAll?token=sad
- 查看后台
总结一下执行顺序就是:
先分好过滤器类型,然后在类型之上分顺序
6采用网关过滤器对系统异常同一处理
6.1创建ErrorFilter
package com.bjsxt.filter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.netflix.zuul.ZuulFilter;
@Component
public class ErrorFilter extends ZuulFilter {
private static final Logger logger = LoggerFactory.getLogger(ErrorFilter.class);
//是否开启过滤器:默认为false不开启
@Override
public boolean shouldFilter() {
return true;
}
//过滤内容:在run方法编写过滤逻辑
@Override
public Object run() {
http://logger.info("--------------error--------------------");
return null;
}
//过滤器类型:通过过滤器类型决定了过滤器执行的时间
@Override
public String filterType() {
return "error";
}
//过滤器的执行顺序:通过整数表示顺序,数值越小,优先级越高
@Override
public int filterOrder() {
return 1;
}
}
6.2创建处理异常响应的控制器
package com.bjsxt.filter;
import org.springframework.boot.autoconfigure.web.ErrorController;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ExceptionHandler implements ErrorController {
@Override
public String getErrorPath() {
return "/error";
}
@GetMapping("/error")
public String error() {
return "你的服务出错了,哈哈哈哈哈哈哈哈哈!";
}
}
因为出错时,SpringBoot项目会自动地去寻找页面/error,所以我们 哈哈哈
6.3修改AccessFilter2
让它抛出一个异常
package com.bjsxt.filter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.netflix.zuul.ZuulFilter;
@Component
public class AccessFilter2 extends ZuulFilter {
private static final Logger logger = LoggerFactory.getLogger(AccessFilter2.class);
//是否开启过滤器:默认为false不开启
@Override
public boolean shouldFilter() {
return true;
}
//过滤内容:在run方法编写过滤逻辑
@Override
public Object run() {
http://logger.info("--------------pre2--------------------");
throw new RuntimeException("error");
}
//过滤器类型:通过过滤器类型决定了过滤器执行的时间
@Override
public String filterType() {
return "pre";
}
//过滤器的执行顺序:通过整数表示顺序,数值越小,优先级越高
@Override
public int filterOrder() {
return 1;
}
}
6.4测试
- 启动zuul-gateway-example
- 启动e-book-product-provider
- 访问http://localhost:9020/e-book-product-provider/product/findAll?token=sad
- 查看后台
一堆异常信息
五、网关容错
1zuul和hystrix无缝结合
在zuul的jar包中包含了hystrix的jar包。所以我们不需要在项目中添加Hystrix的坐
标
2访问网关服务的数据监控流
- 启动zuul-gateway
- 启动e-book-product-provider
- 启动srpingcloud-eureka-consumer-ribbon-dashboard-view
- 访问http://localhost:9020/e-book-product-provider/product/findAll
- 访问http://localhost:9020/hystrix.stream
3启动dashboard-view服务通过可视化界面查看监控数据
- 访问http://localhost:1001/hystrix
4在网关中实现对服务降级处理
4.1创建项目
4.2修改pom文件
<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>1.5.13.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.bjsxt</groupId>
<artifactId>zuul-gateway-fallback</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR5</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>
4.3修改配置文件
spring.application.name=zuul-gateway-fallback
server.port=9020
#设置服务注册中心地址,指向另一个注册中心
eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/
4.4添加ProviderProductFallback类
package com.bjsxt;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
@Component
public class ProductProviderFallback implements ZuulFallbackProvider {
//给定对哪个服务做降级处理
@Override
public String getRoute() {
return "e-book-product-provider";
}
//当服务无法执行时,该方法返回托底信息
@Override
public ClientHttpResponse fallbackResponse() {
return new ClientHttpResponse() {
//设置响应的头信息
@Override
public HttpHeaders getHeaders() {
HttpHeaders httpHeaders = new HttpHeaders();
MediaType mt = new MediaType("application", "json", Charset.forName("utf-8"));
httpHeaders.setContentType(mt);
return httpHeaders;
}
//设置响应体
@Override
public InputStream getBody() throws IOException {
String content = "商品服务不可用,请与管理员联系";
return new ByteArrayInputStream(content.getBytes());
}
//ClientHttpResponse的fallback的状态码 返回String
@Override
public String getStatusText() throws IOException {
return getStatusCode().getReasonPhrase();
}
//ClientHttpResponse的fallback的状态码 返回HttpStatus
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK;
}
//ClientHttpResponse的fallback的状态码 返回int
@Override
public int getRawStatusCode() throws IOException {
return getStatusCode().value();
}
@Override
public void close() {
}
};
}
}
4.5修改启动类
package com.bjsxt;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@EnableZuulProxy
@SpringBootApplication
public class ZuulFallbackApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulFallbackApplication.class, args);
}
}
4.6测试
- 启动e-book-product-provider
- 启动zuul-gateway-fallback
- 访问http://localhost:9020/e-book-product-provider/product/findAll
- 关闭e-book-product-provider
- 再次访问http://localhost:9020/e-book-product-provider/product/findAll
如果要对多个服务做降级处理,只需要创建多个这样地类即可
六、在高并发情况下,网关实现限流达到自我保护
1创建项目
2修改pom文件
<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>1.5.13.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.bjsxt</groupId>
<artifactId>zuul-gateway-ratelimit</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>com.marcosbarbero.cloud</groupId>
<artifactId>spring-cloud-zuul-ratelimit</artifactId>
<version>1.3.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR5</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>
3修改配置文件
3.1修改一下启动类
package com.bjsxt;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@EnableZuulProxy
@SpringBootApplication
public class ZuulRateLimitApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulRateLimitApplication.class, args);
}
}
3.2全局限流
针对每一个服务都有效果
spring.application.name=zuul-gateway-ratelimit
server.port=9020
#设置服务注册中心地址,指向另一个注册中心
eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/
zuul.routes.e-book-product-provider.path=/product/**
zuul.routes.e-book-product-provider.serviceId=e-book-product-provider
zuul.routes.e-book-order-provider.path=/order/**
zuul.routes.e-book-order-provider.serviceId=e-book-order-provider
#全局配置限流
zuul.ratelimit.enabled=true
##60s 内请求超过 3 次,服务端就抛出异常,60s 后可以恢复正常请求
zuul.ratelimit.default-policy.limit=3
zuul.ratelimit.default-policy.refresh-interval=60
##针对 IP 进行限流,不影响其他 IP
zuul.ratelimit.default-policy.type=origin
测试:
- 启动e-book-order-provider
- 启动e-book-product-provider
- 启动zuul-gateway-ratelimit
- 连续访问http://localhost:9020/product/product/findAll
- 连续访问http://localhost:9020/order/order/findAll
皆会出现:
3.3局部限流
spring.application.name=zuul-gateway-ratelimit
server.port=9020
#设置服务注册中心地址,指向另一个注册中心
eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/
zuul.routes.e-book-product-provider.path=/product/**
zuul.routes.e-book-product-provider.serviceId=e-book-product-provider
zuul.routes.e-book-order-provider.path=/order/**
zuul.routes.e-book-order-provider.serviceId=e-book-order-provider
#全局配置限流
#zuul.ratelimit.enabled=true
##60s 内请求超过 3 次,服务端就抛出异常,60s 后可以恢复正常请求
#zuul.ratelimit.default-policy.limit=3
#zuul.ratelimit.default-policy.refresh-interval=60
##针对 IP 进行限流,不影响其他 IP
#zuul.ratelimit.default-policy.type=origin
# 局部限流:针对某个服务进行限流
##开启限流
zuul.ratelimit.enabled=true
##60s 内请求超过 3 次,服务端就抛出异常,60s 后可以恢复正常请求
zuul.ratelimit.policies.e-book-product-provider.limit=3
zuul.ratelimit.policies.e-book-product-provider.refresh-interval=60
##针对某个 IP 进行限流,不影响其他 IP
zuul.ratelimit.policies.e-book-product-provider.type=origin
以上配置只针对e-book-product的服务一分钟内最多访问3次,超过就会限流,对其他服务就没有影响
测试:
- 启动e-book-order-provider
- 启动e-book-product-provider
- 启动zuul-gateway-ratelimit
- 连续访问http://localhost:9020/product/product/findAll
- 连续访问http://localhost:9020/order/order/findAll
这个三次之后就会这样,另外的不会
4网关限流参数
限流信息的存储
七、zuul性能调优:网关的2层超时调优
1创建项目
2修改pom文件添加zuul依赖
<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>1.5.13.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.bjsxt</groupId>
<artifactId>zuul-gateway-timeout</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR5</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>
3修改e-book-product-provider的ProductServiceImpl
package com.book.product.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.book.product.mapper.ProductMapper;
import com.book.product.pojo.Product;
import com.book.product.pojo.ProductExample;
@Service
public class ProductServiceImpl {
@Autowired
private ProductMapper productMapper;
/**
* 查询所有商品
*/
public List<Product> findProductAll(){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
ProductExample example = new ProductExample();
List<Product> list = productMapper.selectByExampleWithBLOBs(example);
return list;
}
}
4测试一波
- 启动e-book-product-provider
- 启动zuul-gateway-timeout
- 访问http://localhost:9020/e-book-product-provider/product/findAll
5修改zuul-gateway-timeout的配置文件
spring.application.name=zuul-gateway
server.port=9020
#设置服务注册中心地址,指向另一个注册中心
eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/
#第一层 hystrix 超时时间设置
#默认情况下是线程池隔离,超时时间 1000ms
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=8000
#第二层 ribbon 超时时间设置:设置比第一层小
# 请求连接的超时时间: 默认 5s
ribbon.ConnectTimeout=5000
# 请求处理的超时时间: 默认 5s
ribbon.ReadTimeout=5000
刚创建时只有上面的未红色标注的
6测试2
- 重启zuul-gateway-timeout
- 启动e-book-product-provider
- 访问http://localhost:9020/e-book-product-provider/product/findAll
然后访问:虽然卡顿了2秒,但是之后还是访问到了请求