Zuul 提供的三大功能:代理 + 路由 + 过滤
Zuul 包含了对请求的 “ 路由 ” 和 “ 过滤 ” 两个最主要的功能:其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础,而过滤器功能则负责对请求的处理过程进行干预,是实现请求校验、服务聚合等功能的基础 。
在 Spring Cloud 中 Zuul 和 Eureka 进行整合,将 Zuul 自身注册为 Eureka 服务治理下的应用,同时从 Eureka 中获得其它微服务列表信息,也即以后的访问微服务都是通过 Zuul 来跳转后获得,特别注意的是,Zuul 服务最终也需要注册进 Eureka的。
在SpringCloud微服务系统中,一种常见的负载均衡方式是,客户端的请求首先经过负载均衡(zuul、Ngnix),再到达服务网关(zuul集群),然后再到具体的服。,服务统一注册到高可用的服务注册中心集群,服务的所有的配置文件由配置服务管理,配置服务的配置文件放在git仓库,方便开发人员随时改配置。
ribbon是对服务之间调用做负载,是服务之间的负载均衡,zuul是可以对外部请求做负载均衡
准备工作
依次启动之前的五个程序,接着创建service-zuul项目。
配置文件
Pom文件配置如下:
<?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>zuul</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>zuul</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.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>
<dependencies>
<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>
<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>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.RC1</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>
配置文件有的版本可能有兼容问题,之前也提过了,2.1.x和2.0.x,我还是以之前的版本做的。
添加注解
@EnableZuulProxy
@EnableDiscoveryClient
在项目的启动类上添加以上两个注解。
配置application.yml文件
eureka:
client:
serviceurl:
defaultZone: http://localhost:8761/eureka/
server:
port: 8775
spring:
application:
name: service-zuul
zuul:
routes:
api-a:
path: /api-a/**
serviceId: service-r
api-b:
path: /api-b/**
serviceId: service-feign
ribbon:
ReadTimeout: 60000
ConnectTimeout: 60000
我这里踩了两个坑,第一个是ribbon:的这个,不加超时的话会显示报错。虽然Idea会识别不了,但实际还是生效了。
另外是api-a和api-b,一定要对齐!!!! 格式很重要,之前我api-b多了两个空格,程序没报错,但是运行之后进入api-b会出错。
访问
http://localhost:8775/api-a/hi?name=ccdbbhttp://localhost:8775/api-b/hi?name=ccdbb 显示:
hi ccdbb,i am from port:8770
hi ccdbb,i am from port:8771
过滤
过滤在我参考的那个博主的文章里面其实写的不是很详细,这里我对代码的内容进行了具体的一些说明。
创建Filter.class。 继承ZuulFilter 里面要写filterType()等方法,没写之前会红字,没关系。
具体的代码如下:
package com.example.zuul;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
@Component
public class Filter extends ZuulFilter {
private static Logger log = LoggerFactory.getLogger(Filter.class);
/**
* 过滤器的类型,决定了过滤器会在请求的那个生命周期中执行,
*一共有四种类型
* pre:可以在请求被路由之前调用
* route:在路由请求时候被调用
* post:在route和error过滤器之后被调用
* error:处理请求时发生错误时被调用
* @return
*/
@Override
public String filterType(){
return "pre";
}
/**
* 过滤器的执行顺序,当请求在一个阶段中存在多个过滤器的时候
* 需要根据该方法返回的值来一次执行。
* @return
*/
@Override
public int filterOrder() {
return 0;
}
/**
* 判断该过滤器是否需要执行,如果返回true,
* 那么该过滤器对所有的请求 都会生效
* @return
*/
@Override
public boolean shouldFilter() {
return true;
}
/**
* 过滤器的具体逻辑,可以通过 requestContext.setSendZuulResponse(false);来过滤请求,
* 不对其进行路由。
* @return
* @throwsZuulException
*/
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
log.info(String.format("%s >>> %s", request.getMethod(), request.getRequestURL().toString()));
Object accessToken = request.getParameter("token");
if(accessToken == null) {
log.warn("token is empty");
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(401);
try {
ctx.getResponse().getWriter().write("token is empty");
}catch (Exception e){}
return null;
}
log.info("ok");
return null;
}
}
这个时候访问 http://localhost:8775/api-a/hi?name=happy 显示
token is empty
加上token后:http://localhost:8775/api-a/hi?name=happy&token=1
hi happy,i am from port:8770