一、Zuul背景
不同的微服务一般会有不同的网络地址,而外部客户端需要调用多个服务器才能完成一个业务需求。网关是介于客户端与服务端之间的中间层,所有的外部请求都会先经过网关。
二、Zuul简介
Zuul是Netflix开源的为服务网关,它可以和Eureka、Ribbon、Hystrix等组件配合使用。Zuul的核心是一系列的过滤器,这些过滤器可以完成以下功能。
- 身份认证与安全:识别每个资源的验证要求,并拒绝那些与要求不符的请求。
- 审查与监控:在边缘位置追踪有意义的数据和统计结果,从而带来精确的生产视图。
- 动态路由:动态的将请求路由到不同的后端集群。
- 压力测试:逐渐增加指向集群的流量,以了解性能。
- 负载分配:为每一种负载类型分配对应容量,并弃用超出限定值的请求。
- 静态响应处理:在边缘位置直接建立部分响应,从而避免其转发到内部集群。
- 多区域弹性:跨越AWS Region进行请求路由,旨在实现ELB使用的多样化,以及让系统的边缘更贴近系统使用者。
三、准备工作
在原有的工程上,创建一个新的工程。
四、创建service-zuul工程
pom.xml文件如下:
<?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>
<groupId>com.example</groupId>
<artifactId>da-zuul-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>da-zuul-server</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.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>
<spring-cloud.version>Finchley.M8</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-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-eureka-client</artifactId>
<version>RELEASE</version>
</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>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
在其入口applicaton类加上注解@EnableZuulProxy,开启zuul的功能:
@EnableZuulProxy
@SpringBootApplication
public class DaZuulServerApplication {
public static void main(String[] args) {
SpringApplication.run(DaZuulServerApplication.class, args);
}
}
加上配置文件application.yml加上以下的配置代码:
eureka:
client:
serviceUrl:
defaultZone: http://localhost:1001/eureka/
server:
port: 4001
spring:
application:
name: zuul-service
zuul:
routes:
api-1:
path: /api-1/**
serviceId: user-service
api-1:
path: /api-2/**
serviceId: ribbon-service
首先指定服务注册中心的地址为http://localhost:1001/eureka/。服务名为service-zuul,服务的端口为4001;以/api-1/ 开头的请求都转发给feign-service服务;以/api-2/开头的请求都转发给ribbon-service服务;依次运行这六个工程;
打开浏览器访问:http://localhost:4001/api-1/getHi?name=zhangsan;浏览器显示:
打开浏览器访问:http://localhost:4001/api-2/getHi?name=zhangsan;浏览器显示:
zuul起到了路由的作用
五、服务过滤
zuul不仅只是路由,并且还能过滤,做一些安全验证。修改启动类。
@EnableZuulProxy
@EnableEurekaClient
@SpringBootApplication
public class DaZuulServerApplication {
public static void main(String[] args) {
SpringApplication.run(DaZuulServerApplication.class, args);
}
@Bean
public MyFilter myFilter(){
return new MyFilter();
}
}
过滤代码编写如下:
@Component
public class MyFilter extends ZuulFilter{
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
RequestContext requestContext = new RequestContext().getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
Object token = request.getParameter("token");
if (token ==null){
try {
requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(401);
requestContext.getResponse().getWriter().write("token is empty!");
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
return null;
}
}
filterType:返回一个字符串代表过滤器的类型,在zuul中定义了四种不同生命周期的过滤器类型。
- pre:路由之前
- routing:路由之时
- post: 路由之后
- error:发送错误调用
- filterOrder:过滤的顺序
- shouldFilter:这里可以写逻辑判断,是否要过滤,本文true,永远过滤。
- run:过滤器的具体逻辑。可用很复杂,包括查sql,nosql去判断该请求到底有没有权限访问。
这时访问:http://localhost:4001/api-1/getHi?name=zhangsan ;网页显示:
访问 http://localhost:4001/api-1/getHi?name=zhangsan&token=1 ;网页显示: