- 客户端维护大量的ip和port信息,直接访问指定服务
- 认证和授权操作,需要在每一个模块中都添加认证和授权的操作
- 项目的迭代,服务要拆分,服务要合并,需要客户端进行大量的变化
- 统一的把安全性校验都放在Zuul中

1 Zuul的快速入门
创建Maven项目,修改为SpringBoot
导入依赖
<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>
添加一个注解
@EnableEurekaClient@EnableZuulProxy
编写配置文件
# 指定Eureka服务地址eureka:client:service-url:defaultZone: http://root:root@localhost:8761/eureka,http://root:root@localhost:8762/eureka#指定服务的名称spring:application:name: ZUULserver:port: 80
2 Zuul的监控界面
导入依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency>
访问地址:
http://localhost/actuator/routes (http://ip:port/actuator/routes)
编写配置文件
# 查看zuul的监控界面(开发时,配置为*,上线,不要配置)management:endpoints:web:exposure:include: "*"
3 忽略服务配置
# zuul的配置zuul:# 基于服务名忽略服务,无法查看 ,如果要忽略全部的服务 "*",默认配置的全部路径都会被忽略掉(自定义服务的配置,无法忽略的)ignored-services: eureka# 监控界面依然可以查看,在访问的时候,404ignored-patterns: /**/search/**
4 自定义服务配置
# zuul的配置zuul:# 指定自定义服务(方式一 , key(服务名):value(路径))# routes:# search: /ss/**# customer: /cc/**# 指定自定义服务(方式二)routes:kehu: # 自定义名称path: /ccc/** # 映射的路径serviceId: customer # 服务名称

5 灰度发布
添加一个配置类
@Beanpublic PatternServiceRouteMapper serviceRouteMapper() {return new PatternServiceRouteMapper("(?<name>^.+)-(?<version>v.+$)","${version}/${name}");}
准备一个服务,提供2个版本
version: v1#指定服务的名称spring:application:name: CUSTOMER-${version}
-Dversion=v2 -Dserver.port=9099
修改Zuul的配置
# zuul的配置zuul:# 基于服务名忽略服务,无法查看 , 如果需要用到-v的方式,一定要忽略掉# ignored-services: "*"
6 Zuul的过滤器执行流程
客户端请求发送到Zuul服务上,首先通过PreFilter链,如果正常放行,会吧请求再次转发给RoutingFilter,请求转发到一个指定的服务,在指定的服务响应一个结果之后,再次走一个PostFilter的过滤器链,最终再将响应信息交给客户端。

Zuul过滤器入门
创建POJO类,继承ZuulFilter抽象类
@Componentpublic class TestZuulFilter extends ZuulFilter {}
指定当前过滤器的类型
@Overridepublic String filterType() {return FilterConstants.PRE_TYPE;}
指定过滤器的执行顺序
@Overridepublic int filterOrder() {return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1;}
配置是否启用
@Overridepublic boolean shouldFilter() {// 开启当前过滤器return true;}
指定过滤器中的具体业务代码
@Overridepublic Object run() throws ZuulException {System.out.println("prefix过滤器执行~~~");return null;}
PreFilter实现token校验 (作业)
准备访问路径,请求参数传递token
http://localhost/v2/customer/version?token=123
创建AuthenticationFilter
@Componentpublic class AuthenticationFilter extends ZuulFilter {@Overridepublic String filterType() {return FilterConstants.PRE_TYPE;}@Overridepublic int filterOrder() {return PRE_DECORATION_FILTER_ORDER - 2;}@Overridepublic boolean shouldFilter() {return true;}@Overridepublic Object run() throws ZuulException {//..}}
在run方法中编写具体的业务逻辑代码
@Overridepublic Object run() throws ZuulException {//1. 获取Request对象RequestContext requestContext = RequestContext.getCurrentContext();HttpServletRequest request = requestContext.getRequest();//2. 获取token参数String token = request.getParameter("token");//3. 对比tokenif(token == null || !"123".equalsIgnoreCase(token)) {//4. token校验失败,直接响应数据requestContext.setSendZuulResponse(false);requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());}return null;}
Zuul的降级
创建POJO类,实现接口FallbackProvider
@Componentpublic class ZuulFallBack implements FallbackProvider {}
重写两个方法
@Overridepublic String getRoute() {return "*"; // 代表指定全部出现问题的服务,都走这个降级方法}@Overridepublic ClientHttpResponse fallbackResponse(String route, Throwable cause) {System.out.println("降级的服务:" + route);cause.printStackTrace();return new ClientHttpResponse() {@Overridepublic HttpStatus getStatusCode() throws IOException {// 指定具体的HttpStatusreturn HttpStatus.INTERNAL_SERVER_ERROR;}@Overridepublic int getRawStatusCode() throws IOException {// 返回的状态码return HttpStatus.INTERNAL_SERVER_ERROR.value();}@Overridepublic String getStatusText() throws IOException {// 指定错误信息return HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase();}@Overridepublic void close() {}@Overridepublic InputStream getBody() throws IOException {// 给用户响应的信息String msg = "当前服务:" + route + "出现问题!!!";return new ByteArrayInputStream(msg.getBytes());}@Overridepublic HttpHeaders getHeaders() {// 指定响应头信息HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_JSON);return headers;}};}
Zuul动态路由(作业)
创建一个过滤器
// 执行顺序最好放在Pre过滤器的最后面
在run方法中编写业务逻辑
@Overridepublic Object run() throws ZuulException {//1. 获取Request对象RequestContext context = RequestContext.getCurrentContext();HttpServletRequest request = context.getRequest();//2. 获取参数,redisKeyString redisKey = request.getParameter("redisKey");//3. 直接判断if(redisKey != null && redisKey.equalsIgnoreCase("customer")){// http://localhost:8080/customercontext.put(FilterConstants.SERVICE_ID_KEY,"customer-v1");context.put(FilterConstants.REQUEST_URI_KEY,"/customer");}else if(redisKey != null && redisKey.equalsIgnoreCase("search")){// http://localhost:8081/search/1context.put(FilterConstants.SERVICE_ID_KEY,"search");context.put(FilterConstants.REQUEST_URI_KEY,"/search/1");}return null;}
