今天我们结合上一个案例来聊聊SpringCloud中路由器-Zuul。Zuul其实也是一个过滤器或者说是一个网关。你可以想象成Zuul是一个包裹的集散中心,他负责分布式系统中请求的分发映射和过滤等一系列操作。下面是SpringCloud官网(地址为:https://springcloud.cc/spring-cloud-dalston.html#_router_and_filter_zuul)对其的一些介绍:
Zuul的路由或者过滤器规则的编写基本是基于JVM所支持的语言都可以写,很不错的是SpringCloud里面内置了Java和Groovy语言,所以这里用的是Java来聊聊。
来先看看个架构图:
说明一下这个架构图:
1:服务的消费者(consumer)、服务的提供者(server-provider-1,server-provider-2)、路由服务的提供者(Zuul)都向服务注册中心注册。
2:服务的消费者在使用SpringCloud路由器之前,是直接调用服务的提供者。但是在加上路由器以后是直接调用路由器的服务。当用户(consumer)的请求到路由器(Zuul)后,路由器根据提前配置好的路由规则然后进行匹配,将匹配到的服务转发到相应的服务提供者的服务器上(server-provider-1或者server-provider-2上)。
好了接下来简单的搭建一个Zuul路由服务。
第一步:创建SpringCloud的服务选择相应的SpringCloud插件
第二步:配置application.properties文件
对路由规则的编写说明一下:
1:上图中的1处为路由服务的名称,这个的名称可以随意起,但要见名知意。起好名称以后开始为这个服务名称指定映射路径,这个映射路径是在以后访问的时候消费者使用的。
2:在4处中为3处配置好的路由服务地址配置对应的服务提供者(也就是配置服务提供者在注册中心的名称)这个服务提供者必须在注册中心注册并健康。
3:在4处和6处的名称是一样的。因为我有两个名称相同的服务开启(相当于做负载均衡)。如果服务提供者的这名称不一致那么是指向两个不同的服务无法做负载均衡。
第三步:在启动主类上添加相应的注解并启动
第四步:在浏览器中访问http://localhost:6080/server-1/getUserMassage?username=XX或者http://localhost:6080/server-2/getUserMassage?username=YY,就可以看到访问不同的路由地址就可以调用不同的服务的提供者。
路由的最大好处是在于按需导引,灵活性和安全性更高。接下来看一下Zuul的过滤器。在说过滤器之前呢,先看这个架构图:
上面就是Zuul过滤器的架构图。对这个图进行一个简单的说明,上图中有四种过滤位置不同的过滤器,四个颜色分别代表四种不同的过滤位置。下面就是对过滤器的一个简单使用:
package com.alibaba; 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; import java.util.Map; @Component public class MyZuulFilter extends ZuulFilter { /** * 过滤器的类型 --> 返回一个字符串代表过滤器的类型,在zuul中定义了四种不同生命周期的过滤器类型,具体如下: * pre:可以在请求被路由之前调用 * route:在路由请求时候被调用 * post:在route和error过滤器之后被调用 * error:处理请求时发生错误时被调用 * @return */ @Override public String filterType() { return "pre"; } /** * 过滤的规则或者顺序 * 通过int值来定义过滤器的执行顺序 * 优先级为0,数字越大,优先级越低 * @return */ @Override public int filterOrder() { return 0; } /** * 过滤器的状态 * 返回一个boolean类型来判断该过滤器是否要执行,所以通过此函数可实现过滤器的开关。 * 在上例中,我们直接返回true,所以该过滤器总是生效 * @return */ @Override public boolean shouldFilter() { return true; } /** * 过滤的逻辑 * @return * @throws ZuulException */ @Override public Object run() throws ZuulException { try{ //RequestContext对象是基于本地线程ThreadLocal,多个RequestContext对象之间 //是基于Map来调用 RequestContext requestContext = RequestContext.getCurrentContext(); //获取request对象 HttpServletRequest httpServletRequest = requestContext.getRequest(); //根据获取到的request对象可对用户的请求参数或者路径等进行过滤 //在实际的开发中一般是对用户的请求路径、请求参数、以及对请求参数中的值等进行过滤判断 //接下来就逐个说一下 String requestURL = httpServletRequest.getServletPath(); String requestParam = httpServletRequest.getParameter("username"); Map<String,String[]> requestParamKeyUse = httpServletRequest.getParameterMap(); System.out.println("请求的路径--->" + requestURL); System.out.println("请求的参数值--->" + requestParam); //上面是我们获取到了,用户的请求路径里面的一些参数,下面我们来看,如果用户 //给我们没有传入指定的参数或者参数的值不合法那么我们就根据不同的情况来分别的进行处理 //如果用户的请求中没有指定的参数那么就返回一个400的错误并返回提示信息 if (!requestParamKeyUse.containsKey("username")){ requestContext.setSendZuulResponse(false);//false为不进行路由,也就是不进行发送到服务提供者上去 requestContext.setResponseStatusCode(400);//返回错误码 requestContext.getResponse().setCharacterEncoding("UTF-8");//设置相应编码字符集 requestContext.getResponse().setContentType("text/html;charset=UTF-8");//设置页面的编码字符集 requestContext.setResponseBody("八戒说:当前的"+ requestURL +"请求没有指定的参数"); return null; } //如果用户的请求中有指定的参数但参数值非法就返回一个400错误并返回提示信息 if (!requestParamKeyUse.get("username")[0].equals("你好")){ requestContext.setSendZuulResponse(false); requestContext.setResponseStatusCode(400);//返回错误码 requestContext.getResponse().setCharacterEncoding("UTF-8");//设置相应编码字符集 requestContext.getResponse().setContentType("text/html;charset=UTF-8");//设置页面的编码字符集 requestContext.setResponseBody("八戒说:当前的"+ requestURL +"请求的参数不合法"); return null; } //如果用户的请求没有问题直接放行或者其他处理 requestContext.setSendZuulResponse(true); return null;//放行 }catch (Exception e){ System.out.println("MyZuulFilter/run Exception:" + e.getMessage()); return null; } } } 以上就是Zuul过滤器的使用。我们根据不同的业务场景和不同的需求可以实现不同位置而定过滤器,在运行过滤器的时候要注意把过滤器交给springBoot进行托管使用注解或者直接在启动的主类中直接new出进行创建。