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 源码解析之前言(一) 打一波广告,欢迎大家访 ...
随机推荐
- MVC 学习
基础概念学习:http://www.cnblogs.com/meetyy/p/3451933.html 路由:http://www.cnblogs.com/meetyy/p/3453189.html ...
- linux下查找指定后缀的文件
1.linux下查找指定后缀的文件 例如查找当前目录下的所有后缀名时.c或.h的文件 find . -type f -regex ".*\.\(c\|h\)"
- 高斯消元&&luogu3389
高斯消元(Gauss) 高斯消元和我们做二元一次方程组差不多 流程: 1.把系数和右边的值就是用二维数组存下来->转化成矩阵 我们的目标是把这个矩阵装换成 上三角的形式 对角线系数全部为1,1下 ...
- 【lrzsz 】Linux安装上传下载功能
1.在线安装服务 # yum -y install lrzsz 2.上传 rz 3.下载 sz
- 论文笔记之:Human-level control through deep reinforcement learning
Human-level control through deep reinforcement learning Nature 2015 Google DeepMind Abstract RL 理论 在 ...
- java中年月日的加减法,年月的加减法使用
本文为博主原创,未经允许不得转载: java计算两个年月日之间相差的天数: public static int daysBetween(String smdate,String bdate) thro ...
- UVa 1451 平均值
https://vjudge.net/problem/UVA-1451 题意:给定长度为n的01串,选一个长度至少为L的连续子串,使得子串中数字的平均值最大. 思路:这题需要数形结合,真的是很灵活. ...
- LoadRunner测试流程
使用LoadRunner 完成测试一般分为四个步骤: 2 Vvitrual User Generator 创建脚本 创建脚本,选择协议 录制脚本 编辑脚本 检查修改脚本是否有误 3 中央控制器(Con ...
- 基于MySQl的分页显示
<%@page import="java.sql.DriverManager"%> <%@page import="java.sql.ResultSet ...
- OpenVPN Windows 平台安装部署教程
一.环境准备: 操作系统Windows 服务器IP:192.168.88.123 VPN:192.168.89.1 客户端IP:192.168.78.3 客户端服务端单网卡,路由器做好端口映射 安装 ...