一、 什么是网关服务
网关服务是单一访问点,并充当多项服务的代理。服务网关启用了跨所有服务的变换、路由和公共处理。
基本原理
服务网关模块是单一调解,用于处理对多个服务使用者和提供者的请求。任何服务网关都有如下四个典型步骤:
1)常用处理 - 一旦网关接收到消息,就对所有消息执行常用处理,例如添加协议级的头或者记录该消息。
2)服务标识 - 必须将网关所处理的消息标识为特定服务类型。例如,查询消息以确定它是针对服务提供者 A、B 还是 C。
3)端点路由 - 当它确定某消息将传递到特定服务提供者时,它将映射到网络可寻址端点,以便可以将该消息转发到服务提供者。
4)特定于服务的处理 - 执行特定目标服务所需的任何处理。
用户可以根据不同的场景更改这四个步骤的顺序。
1 为什么要使用网关
协调请求与服务的调用关系,协调服务之间的关系。
2 网关解决了什么问题
![996cb76c68ba2ace754127f9f16673f5.png](https://i-blog.csdnimg.cn/blog_migrate/9f56d83d98797626b92c488a8ff488ad.jpeg)
二、 编写网关服务入门案例
1 创建项目
![1376aea17aaeaa0d9f3f695b5f2503f3.png](https://i-blog.csdnimg.cn/blog_migrate/49ad14f3c791b0a1dfe3af50528c54ef.png)
修改 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>
<groupId>com.sxt</groupId>
<artifactId>zuul-gateway</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.13.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<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>
<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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
修改配置文件
spring.application.name=zuul-gateway
server.port=9020
eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/
修改启动类
@SpringBootApplication
@EnableZuulProxy
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
}
通过网关访问请求服务
![800252a9515e0400d94bfe0887da61fc.png](https://i-blog.csdnimg.cn/blog_migrate/8de5b665efe0618a8ffddc23a9839fc0.jpeg)
http://网关服务地址:网关服务端口/访问的服务的名称/访问的服务中的接口的地址
三、 路由器的 4 种路由规则方法
1 创建项目
![8c03fe53db4b366510b6dcf47561bd65.png](https://i-blog.csdnimg.cn/blog_migrate/e9014d3e0750dedbbd2d01e8b213e5e8.png)
2 采用 URL 指定路由方式
修改配置文件配置路由规则
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/
![53c8d3bea17edadf1f8a7e7a794b93e4.png](https://i-blog.csdnimg.cn/blog_migrate/b3ae81e38f31383eaae21a86a073ec17.jpeg)
通配符含义
![3fda20ed31414a001c6827111c820a2c.png](https://i-blog.csdnimg.cn/blog_migrate/fdc03709764e1ffb1dd3d4bbca5e522e.png)
3 采用服务名称指定路由方式
## 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/**
4 路由的排除方法
## 4 ###################### 路由排除:排除某几个服务 ###############################排除后,这个地址将为空
http://127.0.0.1:9030/e-book-product-provider/product/findAll
## 多个服务逗号隔开
#zuul.ignored-services=e-book-product-provider
## 5 ###################### 路由排除:排除所有服务 #############################
#由于服务太多,不可能手工一个个加,故路由排除所有服务,然后针对要路由的服务进行手工加
#zuul.ignored-services=*
#zuul.routes.e-book-order-provider.path=/e-book-order-provider/**
## 6 ###################### 路由排除:排除指定关键字的路径 #############################
# 排除所有包括/list/的路径
zuul.ignored-patterns=/**/findAll/**
zuul.routes.e-book-order-provider.path=/suibian/**
![b1d5f715a6f52834fc302ce834e04323.png](https://i-blog.csdnimg.cn/blog_migrate/d7a4ffa73b6c64d8dca5841d6a9482cc.png)
5 路由的添加前缀方法
##http://127.0.0.1:9030/suibian/product-provider/product/findAll
zuul.prefix=/suibian
zuul.routes.e-book-product-provider.path=/product-provider/**
四、自定义网关过滤器
1 编写网关过滤器
1.1创建项目
![d2ed0929bf6bf9b364328941bb99a279.png](https://i-blog.csdnimg.cn/blog_migrate/cfd95e6c96a66f4445d015a3a9cadfd3.png)
修改 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>
<groupId>com.sxt</groupId>
<artifactId>zuul-gateway-filter</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.13.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<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>
<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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
修改配置文件
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/
创建过滤器
package com.sxt.zuul.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 static final Logger logger = LoggerFactory.getLogger(LogFilter.class);
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
//获取请求上下文
RequestContext rc = RequestContext.getCurrentContext();
HttpServletRequest request = rc.getRequest();
logger.info("LogFilter.....method={},url={}",request.getMethod(),request.getRequestURL().toString());
return null;
}
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 0;
}
}
![42e421255791a0f0aa6ace1fa4625580.png](https://i-blog.csdnimg.cn/blog_migrate/866f40e1f12984f7d564039ab6946a56.png)
2 过滤器类型
![58700abca351c3c155702f16cd0d2f01.png](https://i-blog.csdnimg.cn/blog_migrate/cd4f35a29910fc1febfe344645c143d1.jpeg)
3 Zuul 请求的生命周期
![24fd4aa7543310748d2bbe9dfeb3b4ae.png](https://i-blog.csdnimg.cn/blog_migrate/f00dca2c0fa91c271f45c5ab63bf8d44.jpeg)
4 采用网关过滤器实现权限验证
需求:在网关过滤器中通过 Token 判断用户是否登录
4.1创建项目
![bc7599b6c94f329c8a47009425b3566f.png](https://i-blog.csdnimg.cn/blog_migrate/64a487eb05fb58812dfb73e394ed87a3.png)
修改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>
<groupId>com.sxt</groupId>
<artifactId>zuul-gateway-example</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.13.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<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>
<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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
修改配置文件
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/
创建 AccessFilter
package com.sxt.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);
/**
* 是否开启过滤器,默认不开启
*/
@Override
public boolean shouldFilter() {
return true;
}
/**
* 过滤内容在:run方法中编写逻辑
*/
@Override
public Object run() {
//获取请求上下文
RequestContext rc = RequestContext.getCurrentContext();
HttpServletRequest request = rc.getRequest();
logger.info("----------------------------------------");
//获取表单中的token
String token = request.getParameter("token");
//对token做出判断
if(token == null) {
logger.warn("token is null....");
rc.setSendZuulResponse(false);//代表请求结束,不在继续向下请求
rc.setResponseStatusCode(401);//添加一个响应的状态码
rc.setResponseBody("{"result":"token is null"}");//响应内容
rc.getResponse().setContentType("text/html;charset=utf-8");
}else {
//访问Redis服务,进行验证
logger.info("token is ok");
}
return null;
}
/**
* 过滤器类型:通过过滤器类型决定了过滤器执行的时间
*/
@Override
public String filterType() {
return "pre";
}
/**
* 过滤器的执行顺序:通过整数表示顺序,数值越小,优先级越高
*/
@Override
public int filterOrder() {
return 0;
}
}
![0e05d65335bef88e704094e79ab5517f.png](https://i-blog.csdnimg.cn/blog_migrate/3081d36a40f426c4af32806b27f9c66c.png)
![ffcb5a67c4821deb480b65669c85bd5f.png](https://i-blog.csdnimg.cn/blog_migrate/c562486130bbb920bf116d53d940a00d.png)
![2e10c444a06a32fc516c5be7bf50d188.png](https://i-blog.csdnimg.cn/blog_migrate/0547787a188962a7546a9c1efbf4977b.png)
5 网关过滤器执行顺序与 post 类型演示
5.1网关过滤器执行顺序演示
AccessFilter
package com.sxt.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;
/**
* per类型演示
*/
@Component
public class AccessFilter extends ZuulFilter{
private static final Logger logger = LoggerFactory.getLogger(AccessFilter.class);
/**
* 是否开启过滤器,默认不开启
*/
@Override
public boolean shouldFilter() {
return true;
}
/**
* 过滤内容在:run方法中编写逻辑
*/
@Override
public Object run() {
//获取请求上下文
RequestContext rc = RequestContext.getCurrentContext();
HttpServletRequest request = rc.getRequest();
logger.info("------------------pre1----------------------");
//获取表单中的token
String token = request.getParameter("token");
//对token做出判断
if(token == null) {
logger.warn("token is null....");
rc.setSendZuulResponse(false);//代表请求结束,不在继续向下请求
rc.setResponseStatusCode(401);//添加一个响应的状态码
rc.setResponseBody("{"result":"token is null"}");//响应内容
rc.getResponse().setContentType("text/html;charset=utf-8");
}else {
//访问Redis服务,进行验证
logger.info("token is ok");
}
return null;
}
/**
* 过滤器类型:通过过滤器类型决定了过滤器执行的时间
*/
@Override
public String filterType() {
return "pre";
}
/**
* 过滤器的执行顺序:通过整数表示顺序,数值越小,优先级越高
*/
@Override
public int filterOrder() {
return 0;
}
}
AccessFilter 2
/**
* 执行顺序演示
*/
@Component
public class AccessFilter2 extends ZuulFilter{
private static final Logger logger = LoggerFactory.getLogger(AccessFilter2.class);
/**
* 是否开启过滤器,默认不开启
*/
@Override
public boolean shouldFilter() {
return true;
}
/**
* 过滤内容在:run方法中编写逻辑
*/
@Override
public Object run() {
//获取请求上下文
RequestContext rc = RequestContext.getCurrentContext();
HttpServletRequest request = rc.getRequest();
logger.info("------------------pre2----------------------");
return null;
}
/**
* 过滤器类型:通过过滤器类型决定了过滤器执行的时间
*/
@Override
public String filterType() {
return "pre";
}
/**
* 过滤器的执行顺序:通过整数表示顺序,数值越小,优先级越高
*/
@Override
public int filterOrder() {
return 1;
}
}
![26cf311e86340c4f6bb8c0ee5dc31b83.png](https://i-blog.csdnimg.cn/blog_migrate/4a4292b436caafdc8c68a54ff22eb0b1.png)
post 类型演示
/**
* Post类型演示
*/
@Component
public class PostFilter extends ZuulFilter{
private static final Logger logger = LoggerFactory.getLogger(PostFilter.class);
/**
* 是否开启过滤器,默认不开启
*/
@Override
public boolean shouldFilter() {
return true;
}
/**
* 过滤内容在:run方法中编写逻辑
*/
@Override
public Object run() {
//获取请求上下文
RequestContext rc = RequestContext.getCurrentContext();
HttpServletRequest request = rc.getRequest();
logger.info("------------------Post----------------------");
return null;
}
/**
* 过滤器类型:通过过滤器类型决定了过滤器执行的时间
*/
@Override
public String filterType() {
return "post";
}
/**
* 过滤器的执行顺序:通过整数表示顺序,数值越小,优先级越高
*/
@Override
public int filterOrder() {
return 0;
}
}
![dd09491ee618901fa1907d9946fc2e93.png](https://i-blog.csdnimg.cn/blog_migrate/0d4a71e651cf8b0cee0991bee7ed5ae6.jpeg)
6.采用网关过滤器对系统异常同一处理
6.1创建 ErrorFilter
/**
* 异常处理演示
*/
@Component
public class ErrorFilter extends ZuulFilter{
private static final Logger logger = LoggerFactory.getLogger(ErrorFilter.class);
/**
* 是否开启过滤器,默认不开启
*/
@Override
public boolean shouldFilter() {
return true;
}
/**
* 过滤内容在:run方法中编写逻辑
*/
@Override
public Object run() {
//获取请求上下文
RequestContext rc = RequestContext.getCurrentContext();
HttpServletRequest request = rc.getRequest();
logger.info("------------------error----------------------");
return null;
}
/**
* 过滤器类型:通过过滤器类型决定了过滤器执行的时间
*/
@Override
public String filterType() {
return "error";
}
/**
* 过滤器的执行顺序:通过整数表示顺序,数值越小,优先级越高
*/
@Override
public int filterOrder() {
return 1;
}
}
创建处理异常响应的控制器
/**
* 对异常响应内容进行处理
*/
@RestController
public class ExceptionHandler implements ErrorController {
@Override
public String getErrorPath() {
return "/error";
}
@RequestMapping(value="/error")
public String error() {
return "{"result":"500 error!!!"}";
}
}
![d42260b13d5d2e80e4fcfdb241213dc9.png](https://i-blog.csdnimg.cn/blog_migrate/fb9ee295eb94af4e8e367933c45f459d.jpeg)
![a41a8c44823b546000e22876ab2eb33e.png](https://i-blog.csdnimg.cn/blog_migrate/a2c266529fd9c422c6e4f6ac9c5c49c4.png)