1 . 路由网关的介绍
路由网关(Zuul)的主要功能是路由转发和过滤器 . 路由功能是微服务的一部分 , 比如/service/user转发到到用户服务 , /service/shop转发到到商店服务等 . zuul默认和Ribbon结合实现了负载均衡的功能
2 . 路由实现
2.1 与之前类似 , 新建模块zuul , 引入路由依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
<version>1.4.4.RELEASE</version>
</dependency>
2.2 在其启动applicaton类加上注解@EnableZuulProxy,开启zuul的功能
@EnableZuulProxy
@EnableEurekaClient
@SpringBootApplication
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
}
2.3 yml配置文件 , 配置路由路径
spring:
application:
name: szuul
server:
port: 8950
eureka:
client:
serviceUrl:
defaultZone: http://localhost:9999/eureka/
zuul:
routes:
api-a:
path: /service-a/**
serviceId: client-ribbon # 表示/service-a/路径下的请求分发到client-ribbon服务
api-b:
path: /service-b/**
serviceId: client-feign # 表示/service-b/路径下的请求分发到client-feign服务
2.4 结果测试
依次启动eurekraserver , service-hello , client-ribbon , client-feign , zuul 6个模块 , 访问网址 http://localhost:8950/service-a/hello?id=123 , http://localhost:8950/service-b/hello?id=123 , 可以看到执行结果(因为我们未启动service-hello , 由于负载均衡的请求分发 , 所以正常会出现交替出现一次成功一次失败的情况) , 能够访问成功 .
接下来 , 我们停掉client-ribbon , 再访问 http://localhost:8950/service-a/hello?id=123 , 页面就无法访问了 , 而 http://localhost:8950/service-b/hello?id=123 不受影响 , 这说明路由起到了作用 , 以/service-a/ 开头的请求都转发给client-ribbon服务 , 以/service-b/开头的请求都转发给client-feign服务 .
3 . 服务过滤
zuul不仅只是路由,并且还能过滤,做一些安全验证 . 为了演示 , 接下来我们继续改造工程
3.1 增加自定义过滤器 继承 ZuulFilter
package com.xbz.zuul.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
@Component
public class MyZuulFilter extends ZuulFilter {
/**
* 返回过滤器类型
* @return
* pre:可以在请求被路由之前调用
* routing:在路由请求时候被调用
* post:在routing和error过滤器之后被调用
* error:处理请求时发生错误时被调用
*/
@Override
public String filterType() {
return "pre";
}
/**
* 通过int值来定义过滤器的执行顺序 , 数字越小优先级越高
*/
@Override
public int filterOrder() {
return 0;
}
/**
* 判断过滤器是否执行 , 返回false则不执行该过滤器
*/
@Override
public boolean shouldFilter() {
return true;
}
/**
* 过滤器的具体逻辑
* ctx.setSendZuulResponse(false)令zuul不允许请求,
* ctx.setResponseStatusCode(401)设置了其返回的错误码
* ctx.setResponseBody(body)编辑返回body内容
* @return
*/
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
System.out.println(String.format("%s >>> %s", request.getMethod(), request.getRequestURL().toString()));
Object accessToken = request.getParameter("token");
if (accessToken == null) {
System.out.println("error ! no token !");
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(401);
try {
ctx.getResponse().getWriter().write("error ! no token !");
} catch (Exception e) {
}
return null;
}
System.out.println("~ ok ~");
return null;
}
}
3.2 启动类中加入MyZuulFilter
package com.xbz.zuul;
import com.xbz.zuul.filter.MyZuulFilter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.context.annotation.Bean;
@EnableZuulProxy
@EnableEurekaClient
@SpringBootApplication
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
@Bean
public MyZuulFilter getZuulFilter(){
return new MyZuulFilter();
}
}
3.3 结果测试
重启zuul模块 , 再次访问 http://localhost:8950/service-a/hello?id=123 , 会看到如下信息
error ! no token !error ! no token !这说明我们配置的过滤器已经生效 , 必须有token参数才可以正常请求 . 访问 http://localhost:8950/service-a/hello?id=123&token=456 , 得到正常结果
hello 123 , service-hello , I am from port:8900