zoukankan      html  css  js  c++  java
  • zuul网关源码解析

    zuul网关源码解析

    zuul请求的生命周期

    image_1ca2nppk7gn218991jjh3aing79.png-48.2kB

    ZuulServlet

    ZuulServlet定义了对zuul整个过程的处理,如下:

    public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException {
        try {
            init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);
    
            // Marks this request as having passed through the "Zuul engine", as opposed to servlets
            // explicitly bound in web.xml, for which requests will not have the same data attached
            RequestContext context = RequestContext.getCurrentContext();
            context.setZuulEngineRan();
    
            try {
                preRoute();
            } catch (ZuulException e) {
                error(e);
                postRoute();
                return;
            }
            try {
                route();
            } catch (ZuulException e) {
                error(e);
                postRoute();
                return;
            }
            try {
                postRoute();
            } catch (ZuulException e) {
                error(e);
                return;
            }
    
        } catch (Throwable e) {
            error(new ZuulException(e, 500, "UNHANDLED_EXCEPTION_" + e.getClass().getName()));
        } finally {
            RequestContext.getCurrentContext().unset();
        }
    }
    

    PRE阶段

    PreDecorationFilter过滤器寻找路由,如下图:

    image_1ca2qif8j1psa131flf41b7n1su24j.png-106.3kB

    当得到匹配的路由后,装饰RequestContext往请求内容中添加路径等路由信息。

    ROUTE阶段

    RibbonRoutingFilter真正的对服务发起请求,并得到响应结果

    image_1ca2qk10ujk613usl4g1nbj1toq50.png-82.7kB

    run()方法

    public Object run() {
    	RequestContext context = RequestContext.getCurrentContext();
    	this.helper.addIgnoredHeaders();
    	try {
    		RibbonCommandContext commandContext = buildCommandContext(context);
    		//获取请求结果
    		ClientHttpResponse response = forward(commandContext);
    		//设置请求结果
    		setResponse(response);
    		return response;
    	}
    	catch (ZuulException ex) {
    		throw new ZuulRuntimeException(ex);
    	}
    	catch (Exception ex) {
    		throw new ZuulRuntimeException(ex);
    	}
    }
    

    forward()方法通过RibbonCommand实现对服务的调用

    protected ClientHttpResponse forward(RibbonCommandContext context) throws Exception {
    	Map<String, Object> info = this.helper.debug(context.getMethod(),
    			context.getUri(), context.getHeaders(), context.getParams(),
    			context.getRequestEntity());
    
    	RibbonCommand command = this.ribbonCommandFactory.create(context);
    	try {
    		ClientHttpResponse response = command.execute();
    		this.helper.appendDebug(info, response.getStatusCode().value(),
    				response.getHeaders());
    		return response;
    	}
    	catch (HystrixRuntimeException ex) {
    		return handleException(info, ex);
    	}
    
    }
    

    setResponse()方法将响应内容写入RequestContext

    protected void setResponse(ClientHttpResponse resp)
    		throws ClientException, IOException {
    	RequestContext.getCurrentContext().set("zuulResponse", resp);
    	this.helper.setResponse(resp.getStatusCode().value(),
    			resp.getBody() == null ? null : resp.getBody(), resp.getHeaders());
    }
    

    ROUTE还有两个过滤器SendForwardFilter(forward请求转发)SimpleHostRoutingFilter(url请求转发),根据不同的路由类型匹配相应的过滤器。

    POST阶段

    SendResponseFilter对内容进行响应

    image_1ca2oi1kv6g61q3a9p2n4cnaq2j.png-69.3kB

    run()方法

    public Object run() {
    	try {
    		addResponseHeaders();
    		//将response输出
    		writeResponse();
    	}
    	catch (Exception ex) {
    		ReflectionUtils.rethrowRuntimeException(ex);
    	}
    	return null;
    }
    

    writeResponse()方法,从RequestContext中获取response并输出

    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 {
    		if (RequestContext.getCurrentContext().getResponseBody() != null) {
    			String body = RequestContext.getCurrentContext().getResponseBody();
    			writeResponse(
    					new ByteArrayInputStream(
    							body.getBytes(servletResponse.getCharacterEncoding())),
    					outStream);
    			return;
    		}
    		boolean isGzipRequested = false;
    		final String requestEncoding = context.getRequest()
    				.getHeader(ZuulHeaders.ACCEPT_ENCODING);
    
    		if (requestEncoding != null
    				&& HTTPRequestUtils.getInstance().isGzipped(requestEncoding)) {
    			isGzipRequested = true;
    		}
    		is = context.getResponseDataStream();
    		InputStream inputStream = is;
    		if (is != null) {
    			if (context.sendZuulResponse()) {
    				if (context.getResponseGZipped() && !isGzipRequested) {
    					// If origin tell it's GZipped but the content is ZERO bytes,
    					// don't try to uncompress
    					final Long len = context.getOriginContentLength();
    					if (len == null || len > 0) {
    						try {
    							inputStream = new GZIPInputStream(is);
    						}
    						catch (java.util.zip.ZipException ex) {
    							log.debug(
    									"gzip expected but not "
    											+ "received assuming unencoded response "
    											+ RequestContext.getCurrentContext()
    											.getRequest().getRequestURL()
    											.toString());
    							inputStream = is;
    						}
    					}
    					else {
    						// Already done : inputStream = is;
    					}
    				}
    				else if (context.getResponseGZipped() && isGzipRequested) {
    					servletResponse.setHeader(ZuulHeaders.CONTENT_ENCODING, "gzip");
    				}
    				writeResponse(inputStream, outStream);
    			}
    		}
    	}
    	finally {
    		
    		if (is != null) {
    			try {
    				is.close();
    			}
    			catch (Exception ex) {
    				log.warn("Error while closing upstream input stream", ex);
    			}
    		}
    
    		try {
    			Object zuulResponse = RequestContext.getCurrentContext()
    					.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());
    		}
    	}
    }
    

    ERROR阶段

    当PRE、ROUTE、POST阶段的过滤器发生错误时,会调用ERROR过滤器。默认的error过滤器有 SendErrorFilter

  • 相关阅读:
    转:手册网(程序员开发手册相关网站)
    转:关于视频H264编解码的应用实现
    转:视频压缩的基本概念(x264解压包)
    转:MediaCoder H.264格式编码参数设置及详解
    转: 移动直播技术秒开优化经验
    关于阿里 weex 的使用与案例
    转:视频流服务架构解析(音视频格式介绍)
    转:移动端即时通讯系统实践
    转:GRADLE构建最佳实践
    转: Syslog协议介绍
  • 原文地址:https://www.cnblogs.com/liangzs/p/8695397.html
Copyright © 2011-2022 走看看