zuul网关源码解析
zuul网关源码解析
zuul请求的生命周期

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过滤器寻找路由,如下图:

当得到匹配的路由后,装饰RequestContext往请求内容中添加路径等路由信息。
ROUTE阶段
RibbonRoutingFilter真正的对服务发起请求,并得到响应结果

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对内容进行响应

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
zuul网关源码解析的更多相关文章
- 深入理解Zuul之源码解析
转载:http://blog.csdn.net/forezp/article/details/76211680 Zuul 架构图 在zuul中, 整个请求的过程是这样的,首先将请求给zuulservl ...
- Soul API 网关源码解析 03
目标 使用 soul 代理 dubbo 服务 dubbo 服务如何注册到网关的? dubbo 插件是如何工作的? 理清 http --> 网关--> dubbo provider 整条链路 ...
- Soul API 网关源码解析 02
如何读开源项目:对着文档跑demo,对着demo看代码,懂一点就开始试,有问题了问社区. 今日目标: 1.运行examples下面的 http服务 2.学习文档,结合divde插件,发起http请求s ...
- Soul 网关 Nacos 数据同步源码解析
学习目标: 学习Soul 网关 Nacos 数据同步源码解析 学习内容: 环境配置 Soul 网关 Nacos 数据同步基本概念 源码分析 学习时间:2020年1月28号 早7点 学习产出: 环境配置 ...
- springcloud源码解析(目录)
springcloud是一个基于springboot的一站式企业级分布式应用开发框架.springboot为其提供了创建单一项目的便利性,springcloud组合了现有的.常用的分布式项目的解决方案 ...
- Ocelot简易教程(七)之配置文件数据库存储插件源码解析
作者:依乐祝 原文地址:https://www.cnblogs.com/yilezhu/p/9852711.html 上篇文章给大家分享了如何集成我写的一个Ocelot扩展插件把Ocelot的配置存储 ...
- 死磕 java同步系列之Semaphore源码解析
问题 (1)Semaphore是什么? (2)Semaphore具有哪些特性? (3)Semaphore通常使用在什么场景中? (4)Semaphore的许可次数是否可以动态增减? (5)Semaph ...
- 深入学习 esp8266 wifimanager源码解析(打造专属自己的web配网)
QQ技术互动交流群:ESP8266&32 物联网开发 群号622368884,不喜勿喷 单片机菜鸟博哥CSDN 1.前言 废话少说,本篇博文的目的就是深入学习 WifiManager 这个gi ...
- 时序数据库 Apache-IoTDB 源码解析之系统架构(二)
上一章聊到时序数据是什么样,物联网行业中的时序数据的特点:存量数据大.新增数据多(采集频率高.设备量多).详情请见: 时序数据库 Apache-IoTDB 源码解析之前言(一) 打一波广告,欢迎大家访 ...
随机推荐
- 高并发下,php使用uniqid函数生成唯一标识符的四种方案
PHP uniqid()函数可用于生成不重复的唯一标识符,该函数基于微秒级当前时间戳.在高并发或者间隔时长极短(如循环代码)的情况下,会出现大量重复数据.即使使用了第二个参数,也会重复,最好的方案是结 ...
- Entity Framework 6 和 MVC5
网站地址: Entity Framework 6 http://msdn.microsoft.com/en-us/data/ef.aspx MVC5 http://www.asp.net ...
- cmd命令分类
1.系统功能类 AT:计划在计算机上运行的命令和程序.ATTRIB:显示或更改文件属性.BREAK:设置或清除扩展式 CTRL+C 检查.CACLS:显示或修改文件的访问控制列表(ACLs).CALL ...
- Python3基础 try-except else进行配合
Python : 3.7.0 OS : Ubuntu 18.04.1 LTS IDE : PyCharm 2018.2.4 Conda ...
- Linux写时拷贝技术【转】
本文转载自:http://www.cnblogs.com/biyeymyhjob/archive/2012/07/20/2601655.html COW技术初窥: 在Linux程序中,fork()会产 ...
- 棋盘状态压缩dp
状态压缩入门DP整理 只针对入门 一般都是用2进制的方法,压缩成一个数,所以n的范围都会特变小 一些套路 状态一般是很多的,可以搜索或者位运算筛选一下,基本都是这样的吧 当要存两个状态或者数组存不下的 ...
- [不屈的复习] - 安装Java初始化环境
点WIN键->运行(或者使用win+r) 输入cmd命令输入java -version 注: -version是小写,不能使用大写,java后面有一个空格 配置成功后,会出现版本信息 java ...
- 用 C# 计算 与 java 一致的unix时间戳 (长时间整形 如:1476956079961)
背景: 调用java提供接口,需要长时间整形作为验证. 预备知识: 1. java 的 System.currentTimeMillis() 计算的长整型,是从1970年1月1日开始,截止当前的毫秒数 ...
- HDU 6128 Inverse of sum(同余)
http://acm.hdu.edu.cn/showproblem.php?pid=6128 题意:有一个a数列,并且每个数都小于p,现在要求有多少对$(i,j)$满足$\frac{1}{a_i+a_ ...
- UVa 10723 电子人的基因(LCS)
https://vjudge.net/problem/UVA-10723 题意: 输入两个A~Z组成的字符串,找一个最短的串,使得输入的两个串均是它的子序列,另外还需要统计长度最短的串的个数. 思路: ...