微服务-zuul 逻辑梳理

需求

  1. 在zuul中执行 身份验证
  2. 拦截请求的结果
  3. 返回自己想输出的结果

zuul 过滤器类型

pre:可以在请求被路由之前调用
	ServletDetectionFilter
	Servlet30WrapperFilter
	FormBodyWrapperFilter
	...
route:在路由请求时候被调用
	RibbonRoutingFilter
	SimpleHostRoutingFilter
	SendForwardFilter
post:在route和error过滤器之后被调用
	SendResponseFilter
	
error:处理请求时发生错误时被调用

执行逻辑分析

1.ZuulServlet

所有的请求都从该类的 service() 方法中开始,该类继承自 HttpServlet

public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        try {
        // 把request与response对象 存入与线程绑定的 RequestContext 对象中(RequestContext 继承 ConcurrentHashMap),方便后面在各处调用
            this.init((HttpServletRequest)servletRequest, (HttpServletResponse)servletResponse);
            RequestContext context = RequestContext.getCurrentContext();
            context.setZuulEngineRan();
			// 执行前置过滤器
            try {
                this.preRoute();
            } catch (ZuulException var13) {
                this.error(var13);
                this.postRoute();
                return;
            }
		 // 执行路由过滤器
            try {
                this.route();
            } catch (ZuulException var12) {
                this.error(var12);
                this.postRoute();
                return;
            }
		// 执行路由后的过滤器
            try {
                this.postRoute();
            } catch (ZuulException var11) {
                this.error(var11);
            }
        } catch (Throwable var14) {
            this.error(new ZuulException(var14, 500, "UNHANDLED_EXCEPTION_" + var14.getClass().getName()));
        } finally {
            RequestContext.getCurrentContext().unset();
        }
    }

2.FilterProcessor

该类中对 过滤器进行处理

// 传入参数是拦截器的类型
public Object runFilters(String sType) throws Throwable {
        if (RequestContext.getCurrentContext().debugRouting()) {
            Debug.addRoutingDebug("Invoking {" + sType + "} type filters");
        }

        boolean bResult = false;
        // 第一次获取时会初始化过滤器,并根据 过滤器设置的 order 值进行排序,具体逻辑可以自己进去看看。
        List<ZuulFilter> list = FilterLoader.getInstance().getFiltersByType(sType);
        if (list != null) {
            for(int i = 0; i < list.size(); ++i) {
                ZuulFilter zuulFilter = (ZuulFilter)list.get(i);
                // 这里开始执行各个过滤器
                Object result = this.processZuulFilter(zuulFilter);
                if (result != null && result instanceof Boolean) {
                    bResult |= (Boolean)result;
                }
            }
        }

        return bResult;
    }

3.ZuulFilter

该类是一个抽象类,如果我们想要自定义过滤器,继承该类然后加入spring容器即可

public ZuulFilterResult runFilter() {
        ZuulFilterResult zr = new ZuulFilterResult();
        if (!this.isFilterDisabled()) {
        // 这里判断是否需要执行该过滤器,实现类中需要实现该方法
            if (this.shouldFilter()) {
                Tracer t = TracerFactory.instance().startMicroTracer("ZUUL::" + this.getClass().getSimpleName());

                try {
                // 这里执行具体实现类的run方法
                    Object res = this.run();
                    zr = new ZuulFilterResult(res, ExecutionStatus.SUCCESS);
                } catch (Throwable var7) {
                    t.setName("ZUUL::" + this.getClass().getSimpleName() + " failed");
                    zr = new ZuulFilterResult(ExecutionStatus.FAILED);
                    zr.setException(var7);
                } finally {
                    t.stopAndLog();
                }
            } else {
                zr = new ZuulFilterResult(ExecutionStatus.SKIPPED);
            }
        }

        return zr;
    }

4.具体的过滤器

我罗列我觉得重要的过滤器

4.1RibbonRoutingFilter

该过滤器类型是:route,具体的作用就是 去执行真正的请求

// 这是该类重写的 shouldFilter 方法,这里控制着是否需要执行该过滤器
public boolean shouldFilter() {
		RequestContext ctx = RequestContext.getCurrentContext();
		// 可以通过代码RequestContext.getCurrentContext().ctx.setSendZuulResponse(false);来控制不让该过滤器执行
		return (ctx.getRouteHost() == null && 				   ctx.get(SERVICE_ID_KEY) != null
				&& ctx.sendZuulResponse());
	}
// 该过滤器执行的逻辑
public Object run() {
		RequestContext context = RequestContext.getCurrentContext();
		this.helper.addIgnoredHeaders();
		try {
			RibbonCommandContext commandContext = buildCommandContext(context);
			// 这里进行真实的请求
			ClientHttpResponse response = forward(commandContext);
			// 这里执行的是这一句 RequestContext.getCurrentContext().set("zuulResponse", resp); 就是把返回对象存入与线程绑定的对象中
			setResponse(response);
			return response;
		}
		catch (ZuulException ex) {
			throw new ZuulRuntimeException(ex);
		}
		catch (Exception ex) {
			throw new ZuulRuntimeException(ex);
		}
	}

4.2SendResponseFilter

该过滤器的类型是:post 看类名也可以理解出是用来处理返回值的

@Override
	public Object run() {
		try {
			// 写入请求头数据
			addResponseHeaders();
			// 写出返回的数据到浏览器,着重看这里的逻辑
			writeResponse();
		}
		catch (Exception ex) {
			ReflectionUtils.rethrowRuntimeException(ex);
		}
		return null;
	}
private void writeResponse() throws Exception {
		RequestContext context = RequestContext.getCurrentContext();
		// there is no body to send
		if (context.getResponseBody() == null
				&& context.getResponseDataStream() == null) {
			return;
		}
		HttpServletResponse servletResponse = context.getResponse();
		if (servletResponse.getCharacterEncoding() == null) { // only set if not set
			servletResponse.setCharacterEncoding("UTF-8");
		}
		
		OutputStream outStream = servletResponse.getOutputStream();
		InputStream is = null;
		try {
			// 这里可以控制返回的数据,如果我们通过代码 RequestContext.getCurrentContext().setResponseBody(result.toString()); 设置了自定义的返回值,就不会输出 router 过滤器中得到的返回数据
			if (context.getResponseBody() != null) {
				String body = context.getResponseBody();
				is = new ByteArrayInputStream(
								body.getBytes(servletResponse.getCharacterEncoding()));
			}
			else {
				is = context.getResponseDataStream();
				if (is!=null && context.getResponseGZipped()) {
					if (isGzipRequested(context)) {				servletResponse.setHeader(ZuulHeaders.CONTENT_ENCODING, "gzip");
					}
					else {
						is = handleGzipStream(is);
					}
				}
			}
			
			if (is!=null) {
				writeResponse(is, outStream);
			}
		}
		finally {
			*
			* @author Johannes Edmeier
			*/
			if (is != null) {
				try {
					is.close();
				}
				catch (Exception ex) {
					log.warn("Error while closing upstream input stream", ex);
				}
			}

			try {
				Object zuulResponse = context.get("zuulResponse");
				if (zuulResponse instanceof Closeable) {
					((Closeable) zuulResponse).close();
				}
				outStream.flush();
				// The container will close the stream for us
			}
			catch (IOException ex) {
				log.warn("Error while sending response to client: " + ex.getMessage());
			}
		}
	}

解决需求

在zuul中执行 身份验证

自定义一个前置 过滤器 设置类型为 pre,验证不通过则执行代码 RequestContext.getCurrentContext().ctx.setSendZuulResponse(false);

@Override
    public Object run() throws ZuulException {
        RequestContext ctx = RequestContext.getCurrentContext();
        // 判断是否需要验证身份
        String requestURI = ctx.getRequest().getRequestURI();
        if (!needFilter(requestURI)){
            return null;
        }
        // 获取原请求头中的 Authorization
        String authorization = ctx.getRequest().getHeader("Authorization");

        // 放入路由后的请求头中
    ctx.getZuulRequestHeaders().put("Authorization",authorization);

        // token 验证
        SSOState result = ValidateService.jwtValidate(authorization);
        if (!(result == SSOState.Success)) {
		// ====================验证不通过===================
		// 设置不再执行 RibbonRoutingFilter 过滤器
             ctx.setSendZuulResponse(false);
       // 设置返回状态
            ctx.setResponseStatusCode(401);
       // 设置自己想要返回的内容
            ctx.setResponseBody(result.toString());
            ctx.getResponse().setContentType("text/html;charset=UTF-8");
        }

        return null;
    }

拦截请求的结果

定义一个后置的 过滤器:post

 @Override
    public Object run() throws ZuulException {
        RequestContext ctx = RequestContext.getCurrentContext();
        String responseBody = ctx.getResponseBody();
        // 这里获取的是真正返回到浏览器的数据
        System.out.println("=============="+responseBody);

        try {
            Object zuulResponse = RequestContext.getCurrentContext().get("zuulResponse");
            if (zuulResponse != null) {
                RibbonHttpResponse resp = (RibbonHttpResponse) zuulResponse;
                String body = IOUtils.toString(resp.getBody());
           // 这里获取的是 RibbonRoutingFilter 执行完后返回的数据,前提是让这个过滤器执行了的
                System.err.println("+++++++++++++++++"+body);
                resp.close();
                RequestContext.getCurrentContext().setResponseBody(body);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }
  1. 返回自己想输出的结果

在 SendResponseFilter 过滤器之前执行 RequestContext.getCurrentContext().setResponseBody(“you want to response!!!”);

猜你喜欢

转载自blog.csdn.net/zaige66/article/details/84882717