通过先前的内容我们可以很容易实现如上图所示的架构。在常规的项目中,调用服务前基本都需要经过鉴权之类的操作。使用上面的架构,每个开放服务都需要实现鉴权,不仅提高了开发的复杂程度,而且使得代码变得冗余。因此我们需要一个组件来搭建开放服务和用户端之间的桥梁,在这一层面上实现代理、鉴权等操作。Netfilx Zuul就能达成这一目的。
在原有架构中加入Zuul组件后,架构如上图所示,使用了Zuul之后,用户调用我们系统服务的请求将会先到达Zuul网关,再由网关对不同的请求进行路由转发。在执行转发操作之前,Zuul可以通过过滤器执行一系列代码,实现请求过滤,达到类似Spring MVC中拦截器的效果。此时各个微服务的开发人员不需要在关注诸如鉴权之类的问题,鉴权由Zuul统一处理,只有鉴权通过的请求才会被路由,否则Zuul直接过滤掉该请求。
我们在项目中新建一个Maven模块,我这里给模块命名为zuulgateway,在模块下pom.xml中新增如下的依赖
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.0.6.RELEASEversion>
parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>Finchley.SR1version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
<version>2.0.2.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-zuulartifactId>
<version>2.0.3.RELEASEversion>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
然后在resource目录下新建application.yml,配置一下Zuul的路由转发规则。application.yml内容如下
server:
port: 8070 #端口号
spring:
application:
name: app-zuul-gateway #应用名
##配置eureka,从注册中心获取服务地址
eureka:
client:
service-url:
defaultZone: http://localhost:9090/eureka/
###因为该应用为服务提供者,是eureka的一个客户端,需要注册到注册中心
register-with-eureka: true
###是否需要从eureka上检索服务
fetch-registry: true
instance:
prefer-ip-address: true #将自己的ip地址注册到Eureka服务中
ip-address: 127.0.0.1
instance-id: ${
spring.application.name}###${server.port} #指定实例ID
##zuul路由规则
zuul:
host:
connect-timeout-millis: 15000 #HTTP连接超时大于Hystrix的超时时间
socket-timeout-millis: 60000 #socket超时
routes:
cargo-service: #这是个随便取的名字,用于识别不同的路由,从原地址到目的地址
path: /cargo-service/** #访问的地址
serviceid: app-cargo #转发到哪个微服务
cargolist-service:
path: /cargolist-service/**
serviceid: app-cargolist
ribbon: #设置ribbon的超时时间小于zuul的超时时间
ReadTimeout: 10000
ConnectTimeout: 10000
这里注意一下,一定要让Zuul的超时时间大于Ribbon的超时时间,否则Zuul无法正常工作,请求会失败。
然后在src/main/java下新建一个runner包,在包下新建启动类,类上添加@EnableZuulProxy注解,表示启动Zuul,启动微服务即可测试查看效果。启动类完整代码如下
package runner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@SpringBootApplication
@EnableZuulProxy//启用Zuul
public class ZuulGatewayApp {
public static void main(String[] args) {
SpringApplication.run(ZuulGatewayApp.class, args);
}
}
微服务启动完成后,访问http://localhost:8070/cargolist-service/cargoList/123,通过Zuul微服务来调用app-cargo-list微服务,页面正常返回结果,Zuul在此时已经起到了路由代理的作用,Zuul的基本配置就完成了。
Zuul的过滤器实现同样非常简单,只需要新建一个过滤器类,并且让SpringBoot框架扫描到它就可以实现了,我们在模块下再新建一个filter包用来存放过滤器类,新建一个TokenZuulFilter类,并继承ZuulFilter。ZuulFilter是一个抽象方法,里面定义了一系列拦截器执行过程。TokenZuulFilter类完整代码如下。
package 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 javax.servlet.http.HttpServletRequest;
@Component
public class TokenZuulFilter extends ZuulFilter {
@Override
public String filterType() {
//pre:请求被路由前执行
//routing:路由请求调用时执行
//post:路由完成或路由提交后出错后返回前执行
//error:处理请求时发生错误执行
return "pre";
}
@Override
public int filterOrder() {
//数字越小优先级越高
return 0;
}
@Override
public boolean shouldFilter() {
//true执行,false不执行
return true;
}
@Override
public Object run() throws ZuulException {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
String token = request.getParameter("token");
if(StringUtils.isEmpty(token)||!"123".equals(token)){
//检查token
requestContext.setSendZuulResponse(false); // 过滤该请求,不对其进行路由
requestContext.setResponseStatusCode(401); // 设置响应状态码
requestContext.setResponseBody("TOKEN ERROR"); // 设置响应状态码
return null;
}
return null;
}
}
在启动类@SpringBootApplication注解加入包扫描的参数,添加filter包,重启微服务即可测试查看结果。
@SpringBootApplication(scanBasePackages = "filter")
访问http://localhost:8070/cargolist-service/cargoList/123,发现页面返回TOKEN ERROR,F12查看请求状态码为401。我们在URL上加入参数再访问http://localhost:8070/cargolist-service/cargoList/123?token=123,此时发现页面可以正常返回,至此Zuul过滤器实现就完成了。