zuul网关源码解析

zuul请求的生命周期

ZuulServlet

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

  1. public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException {
  2. try {
  3. init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);
  4. // Marks this request as having passed through the "Zuul engine", as opposed to servlets
  5. // explicitly bound in web.xml, for which requests will not have the same data attached
  6. RequestContext context = RequestContext.getCurrentContext();
  7. context.setZuulEngineRan();
  8. try {
  9. preRoute();
  10. } catch (ZuulException e) {
  11. error(e);
  12. postRoute();
  13. return;
  14. }
  15. try {
  16. route();
  17. } catch (ZuulException e) {
  18. error(e);
  19. postRoute();
  20. return;
  21. }
  22. try {
  23. postRoute();
  24. } catch (ZuulException e) {
  25. error(e);
  26. return;
  27. }
  28. } catch (Throwable e) {
  29. error(new ZuulException(e, 500, "UNHANDLED_EXCEPTION_" + e.getClass().getName()));
  30. } finally {
  31. RequestContext.getCurrentContext().unset();
  32. }
  33. }

PRE阶段

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

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

ROUTE阶段

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

run()方法

  1. public Object run() {
  2. RequestContext context = RequestContext.getCurrentContext();
  3. this.helper.addIgnoredHeaders();
  4. try {
  5. RibbonCommandContext commandContext = buildCommandContext(context);
  6. //获取请求结果
  7. ClientHttpResponse response = forward(commandContext);
  8. //设置请求结果
  9. setResponse(response);
  10. return response;
  11. }
  12. catch (ZuulException ex) {
  13. throw new ZuulRuntimeException(ex);
  14. }
  15. catch (Exception ex) {
  16. throw new ZuulRuntimeException(ex);
  17. }
  18. }

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

  1. protected ClientHttpResponse forward(RibbonCommandContext context) throws Exception {
  2. Map<String, Object> info = this.helper.debug(context.getMethod(),
  3. context.getUri(), context.getHeaders(), context.getParams(),
  4. context.getRequestEntity());
  5. RibbonCommand command = this.ribbonCommandFactory.create(context);
  6. try {
  7. ClientHttpResponse response = command.execute();
  8. this.helper.appendDebug(info, response.getStatusCode().value(),
  9. response.getHeaders());
  10. return response;
  11. }
  12. catch (HystrixRuntimeException ex) {
  13. return handleException(info, ex);
  14. }
  15. }

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

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

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

POST阶段

SendResponseFilter对内容进行响应

run()方法

  1. public Object run() {
  2. try {
  3. addResponseHeaders();
  4. //将response输出
  5. writeResponse();
  6. }
  7. catch (Exception ex) {
  8. ReflectionUtils.rethrowRuntimeException(ex);
  9. }
  10. return null;
  11. }

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

  1. private void writeResponse() throws Exception {
  2. RequestContext context = RequestContext.getCurrentContext();
  3. // there is no body to send
  4. if (context.getResponseBody() == null
  5. && context.getResponseDataStream() == null) {
  6. return;
  7. }
  8. HttpServletResponse servletResponse = context.getResponse();
  9. if (servletResponse.getCharacterEncoding() == null) { // only set if not set
  10. servletResponse.setCharacterEncoding("UTF-8");
  11. }
  12. OutputStream outStream = servletResponse.getOutputStream();
  13. InputStream is = null;
  14. try {
  15. if (RequestContext.getCurrentContext().getResponseBody() != null) {
  16. String body = RequestContext.getCurrentContext().getResponseBody();
  17. writeResponse(
  18. new ByteArrayInputStream(
  19. body.getBytes(servletResponse.getCharacterEncoding())),
  20. outStream);
  21. return;
  22. }
  23. boolean isGzipRequested = false;
  24. final String requestEncoding = context.getRequest()
  25. .getHeader(ZuulHeaders.ACCEPT_ENCODING);
  26. if (requestEncoding != null
  27. && HTTPRequestUtils.getInstance().isGzipped(requestEncoding)) {
  28. isGzipRequested = true;
  29. }
  30. is = context.getResponseDataStream();
  31. InputStream inputStream = is;
  32. if (is != null) {
  33. if (context.sendZuulResponse()) {
  34. if (context.getResponseGZipped() && !isGzipRequested) {
  35. // If origin tell it's GZipped but the content is ZERO bytes,
  36. // don't try to uncompress
  37. final Long len = context.getOriginContentLength();
  38. if (len == null || len > 0) {
  39. try {
  40. inputStream = new GZIPInputStream(is);
  41. }
  42. catch (java.util.zip.ZipException ex) {
  43. log.debug(
  44. "gzip expected but not "
  45. + "received assuming unencoded response "
  46. + RequestContext.getCurrentContext()
  47. .getRequest().getRequestURL()
  48. .toString());
  49. inputStream = is;
  50. }
  51. }
  52. else {
  53. // Already done : inputStream = is;
  54. }
  55. }
  56. else if (context.getResponseGZipped() && isGzipRequested) {
  57. servletResponse.setHeader(ZuulHeaders.CONTENT_ENCODING, "gzip");
  58. }
  59. writeResponse(inputStream, outStream);
  60. }
  61. }
  62. }
  63. finally {
  64. if (is != null) {
  65. try {
  66. is.close();
  67. }
  68. catch (Exception ex) {
  69. log.warn("Error while closing upstream input stream", ex);
  70. }
  71. }
  72. try {
  73. Object zuulResponse = RequestContext.getCurrentContext()
  74. .get("zuulResponse");
  75. if (zuulResponse instanceof Closeable) {
  76. ((Closeable) zuulResponse).close();
  77. }
  78. outStream.flush();
  79. // The container will close the stream for us
  80. }
  81. catch (IOException ex) {
  82. log.warn("Error while sending response to client: " + ex.getMessage());
  83. }
  84. }
  85. }

ERROR阶段

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

zuul网关源码解析的更多相关文章

  1. 深入理解Zuul之源码解析

    转载:http://blog.csdn.net/forezp/article/details/76211680 Zuul 架构图 在zuul中, 整个请求的过程是这样的,首先将请求给zuulservl ...

  2. Soul API 网关源码解析 03

    目标 使用 soul 代理 dubbo 服务 dubbo 服务如何注册到网关的? dubbo 插件是如何工作的? 理清 http --> 网关--> dubbo provider 整条链路 ...

  3. Soul API 网关源码解析 02

    如何读开源项目:对着文档跑demo,对着demo看代码,懂一点就开始试,有问题了问社区. 今日目标: 1.运行examples下面的 http服务 2.学习文档,结合divde插件,发起http请求s ...

  4. Soul 网关 Nacos 数据同步源码解析

    学习目标: 学习Soul 网关 Nacos 数据同步源码解析 学习内容: 环境配置 Soul 网关 Nacos 数据同步基本概念 源码分析 学习时间:2020年1月28号 早7点 学习产出: 环境配置 ...

  5. springcloud源码解析(目录)

    springcloud是一个基于springboot的一站式企业级分布式应用开发框架.springboot为其提供了创建单一项目的便利性,springcloud组合了现有的.常用的分布式项目的解决方案 ...

  6. Ocelot简易教程(七)之配置文件数据库存储插件源码解析

    作者:依乐祝 原文地址:https://www.cnblogs.com/yilezhu/p/9852711.html 上篇文章给大家分享了如何集成我写的一个Ocelot扩展插件把Ocelot的配置存储 ...

  7. 死磕 java同步系列之Semaphore源码解析

    问题 (1)Semaphore是什么? (2)Semaphore具有哪些特性? (3)Semaphore通常使用在什么场景中? (4)Semaphore的许可次数是否可以动态增减? (5)Semaph ...

  8. 深入学习 esp8266 wifimanager源码解析(打造专属自己的web配网)

    QQ技术互动交流群:ESP8266&32 物联网开发 群号622368884,不喜勿喷 单片机菜鸟博哥CSDN 1.前言 废话少说,本篇博文的目的就是深入学习 WifiManager 这个gi ...

  9. 时序数据库 Apache-IoTDB 源码解析之系统架构(二)

    上一章聊到时序数据是什么样,物联网行业中的时序数据的特点:存量数据大.新增数据多(采集频率高.设备量多).详情请见: 时序数据库 Apache-IoTDB 源码解析之前言(一) 打一波广告,欢迎大家访 ...

随机推荐

  1. Jquery获取敲击回车时光标所在的位置

    $(document).keyup(function(event){ ){ //是否是回车 var el = event.srcElement || event.target; var input_t ...

  2. P4001 [BJOI2006]狼抓兔子(对偶图)

    P4001 [BJOI2006]狼抓兔子 最短路+对偶图 看这题最容易想到的就是网络流.Dinic可以过,据说还跑得比正解快. 如果不写网络流,那么需要知道2个前置知识:平面图和对偶图(右转baidu ...

  3. HTML5 表单元素和属性

    HTML5 表单元素和属性学习 版权声明:未经博主授权,内容严禁转载 ! 表单元素简介 无论实现提交功能还是展示页面功能,表单在HTML中的作用都十分重要. 在其他版本的HTML中,表单能够包含的元素 ...

  4. 编译错误 error C2451: “std::_Unforced”类型的条件表达式是非法的

    part 1 编译器 vs2015 VC++. 完整的错误信息粘贴如下: d:\program files (x86)\microsoft visual studio 14.0\vc\include\ ...

  5. 20145205 武钰 《网络对抗》Exp8 Web基础

    一点题外话 本次实验我三号完成的,由于一些原因吧,一直没发博客,拖到了现在,这里说就是评判一下自己的懒惰. 实验后问题回答 (1)什么是表单 表单在网页中主要负责数据采集功能. 一个表单有三个基本组成 ...

  6. windows 常用快捷键和dos命令

    快捷键 win + R 打开dos命令行窗口 win + E 打开资源管理窗口 (计算机) shift + 鼠标右击 + select 在此处打开命令窗口 可在资源管理目录下打开dos命令 windo ...

  7. 【Coursera】Security Introduction -Ninth Week(2)

    对于公钥系统,我们现在已经有了保证它 Confidentially 的一种方法:SSL.SSL利用了公钥的概念. 那么 who we are talking to? Integrity Certifi ...

  8. POJ 1509 Glass Beads---最小表示法

    题意: T组数据,每组数据给出一个字符串,求这个字符串的最小表示发(只要求输出起始位置坐标) SAM入门题(检测板子是否正确). 将字符串S加倍丢进SAM中,然后走字符串长度次,每次贪心的沿最小的边走 ...

  9. Codeforces Round #303 (Div. 2)E. Paths and Trees 最短路

    E. Paths and Trees time limit per test 3 seconds memory limit per test 256 megabytes input standard ...

  10. shell 求幂

    $ let i=** $ echo $i $ ((i=**)) $ echo $i $ echo "5^2" | bc