Spring Cloud实现Zuul自带的Debug功能

Zuul 中自带了一个 DebugFilter,一开始笔者也没明白这个 DebugFilter 有什么用,看名称很容易理解,它是用来调试的,可是你看它的源码几乎没什么逻辑,就 set 了两个值而已,代码如下所示。
@Override
public Object run() {
    RequestContext ctx = RequestContext.getCurrentContext();
    ctx.setDebugRouting(true);
    ctx.setDebugRequest(true);
    return null;
}
要想让这个过滤器执行就得研究一下它的 shouldFilter() 方法,代码如下所示。
@Override
public boolean shouldFilter() {
    HttpServletRequest request = RequestContext.getCurrentContext().getRequest();
    if ("true".equals(request.getParameter(DEBUG_PARAMETER.get()))) {
        return true;
    }
    return ROUTING_DEBUG.get();
}
只要满足两个条件中的任何一个就可以开启这个过滤器。

第一个条件是请求参数中带了“某个参数 =true”就可以开启,这个参数名是通过下面的代码获取的,代码如下所示。

private static final DynamicStringProperty DEBUG_PARAMETER = DynamicPropertyFactory.getInstance()
            .getStringProperty(ZuulConstants.ZUUL_DEBUG_PARAMETER, "debug");

DynamicStringProperty 是 Netflix 的配置管理框架 Archaius 提供的 API,可以从配置中心获取配置,由于 Netflix 没有开源 Archaius 的服务端,所以这边用的就是默认值 debug,如果大家想动态去获取这个值的话可以用携程开源的 Apollo 来对接 Archaius,教程后面会给大家讲解。

可以在请求地址后面追加“debug=true”来开启这个过滤器,参数名称 debug 也可以在配置文件中进行覆盖,用 zuul.debug.parameter 指定,否则就是从 Archaius 中获取,没有对接 Archaius 那就是默认值 debug。

第二个条件的代码,具体代码如下所示。

private static final DynamicBooleanProperty ROUTING_DEBUG = DynamicPropertyFactory
            .getInstance().getBooleanProperty(ZuulConstants.ZUUL_DEBUG_REQUEST, false);

它是通过配置 zuul.debug.request 来决定的,可以在配置文件中配置“zuul.debug.request=true”开启 DebugFilter 过滤器。

DebugFilter 过滤器开启后,并没什么效果,在 run 方法中只是设置了 DebugRouting 和 DebugRequest 两个值为 true,于是继续看源码,发现在很多地方都有这样一段代码,比如 com.netflix.zuul.FilterProcessor.runFilters(String) 中,代码如下所示。

if (RequestContext.getCurrentContext().debugRouting()) {
    Debug.addRoutingDebug("Invoking {" + sType + "} type filters");
}

当 debugRouting 为 true 的时候就会添加一些 Debug 信息到 RequestContext 中。现在明白了 DebugFilter 中为什么要设置 DebugRouting 和 DebugRequest 两个值为 true。

到了这步后还是有些疑惑,一般我们调试信息的话肯定是用日志输出来的,日志级别就是 Debug,但这个 Debug 信息只是累加起来存储到 RequestContext 中,没有对使用者展示。

在 org.springframework.cloud.netflix.zuul.filters.post.SendResponseFilter.addResponseHeaders() 这段代码中我们看到了希望。具体代码如下所示。
private void addResponseHeaders() {
    RequestContext context = RequestContext.getCurrentContext();
    HttpServletResponse servletResponse = context.getResponse();
    if (this.zuulProperties.isIncludeDebugHeader()) {
        @SuppressWarnings("unchecked")
        List<String> rd = (List<String>) context.get(ROUTING_DEBUG_KEY);
        if (rd != null) {
            StringBuilder debugHeader = new StringBuilder();
            for (String it : rd) {
                debugHeader.append("[[[" + it + "]]]");
            }
            servletResponse.addHeader(X_ZUUL_DEBUG_HEADER, debugHeader.toString());
        }
    }
}
核心代码在于 this.zuulProperties.isIncludeDebugHeader(),只有满足这个条件才会把 RequestContext 中的调试信息作为响应头输出,在配置文件中增加下面的配置即可:

zuul.include-debug-header=true

最后在请求的响应头中可以看到调试内容,如图 1 所示。

调试内容
图 1  调试内容

推荐文章
Java正则表达式验证电话号码

在注册会员时,经常需要输入电话号码,电话号码是指手机号码或者固定电话。如果输入的内容不合法,则会向用户输出提示。本实例模拟实现电话号码的验证功能,接收用户在控制台输入的电话号码,然后进行判断,并将结果

Python exec()和eval()的使用注意事项

使用exec()和eval()函数时,一定要记住,它们的第一个参数是字符串,而字符串的内容一定要是可执行的代码。 以eval()函数为例,用代码演示常犯的错误: s="hello" print

vb窗体设计器的主要功能是什么?

vb窗体设计器的主要功能是:建立用户界面。不过这仅仅是它的其中一个功能,它就像是VB的入口,让你可以去装修门面,同时它也可以从界面连接到代码编辑器。窗体是计算机应用程序与人进行信息交互的图形界面。VB

MySQL默认值(DEFAULT)

默认值(Default)的完整称呼是“默认值约束(DefaultConstraint)”,用来指定某列的默认值。在表中插入一条新记录时,如果没有为某个字段赋值,系统就会自动为这个字段插入默认值。 例

MySQL HAVING:过滤分组

在MySQL中,可以使用HAVING 关键字对分组后的数据进行过滤。 使用HAVING关键字的语法格式如下: HAVING HAVING关键字和WHERE关键字都可以用来过滤数据,且HAVING

Java开发实战视频教程(魔乐科技)

本套视频为Java实战视频教程,包含3个实战项目,分别为山寨版超级马里奥,五子棋、Struts2开发实战。 学员下载的每个项目中,不仅有完整的开发视频,还有完整的可运行代码(可执行文件)。 超级马

二级域名和二级目录的对比

当我们搭建一个子站时,如果数据量不大,可以不启用新域名,可以将子站搭建在二级域名或者二级目录之下。 子域名与子目录同属于SEO中URL优化的范畴,在SEO的过程中,我们需要根据网站的具体类型来选择使

C语言求自然底数e,求自然对数的底e

自然底数e=2.718281828…,e的计算公式如下: e=1+1/1!+1/2!+1/3!+… 要求当最后一项的值小于10-10时结束。 算法思想 使用循环实现累加求和,并在求和后计算下一项所对

什么是sap管理系统?

SAP系统(systemsapplicationsandproductsindataprocessing)是一套企业资源管理软件系统,具有现代化、信息化、智能化的应用优势,能够为企业管理问题的解决提供

Java Swing文本编辑器的实现

最简单的文本编辑器要数Windows的记事本了。在学习了本章知识之后,本次案例将完成一个简单的文本编辑器程序。它可以打开文本文件,并输出文本文件的位置和内容。其中用到了菜单、工具栏以及选项卡等组件,是

Qt读写文件(2种方式)实现详解

文件的读写是很多应用程序具有的功能,甚至某些应用程序就是围绕着某一种格式文件的处理而开发的,所以文件读写是应用程序开发的一个基本功能。 文本文件是指以纯文本格式存储的文件,例如用QtCreator编

菜鸟也疯狂系列汇编视频教程(80集全)

一套不错的汇编语言教程,值得mark一下。学习汇编语言和你的英语、数学水平没有关系。 视频目录: 01.十进制、二进制、十六进制 02.进制数之间的转换 03.计算机工作流程 04.内存空

Python Timer定时器:控制函数在特定时间执行

Thread类有一个Timer子类,该子类可用于控制指定函数在特定时间内执行一次。例如如下程序: fromthreadingimportTimer defhello(): print("h

Hibernate cache.use_second_level_cache属性:判断是否关闭二级缓存

hibernate.cache.use_second_level_cache属性用于指定是否彻底关闭所有二级缓存。 语法: hibernate.cache.use_second_level_cac

C++对象做成员变量(无师自通)

classRectangle { private: doublelength; doublewidth; public: voidsetLength(double); voidset

C++11完美转发及实现方法详解

C++11标准为C++引入右值引用语法的同时,还解决了一个C++98/03标准长期存在的短板,即使用简单的方式即可在函数模板中实现参数的完美转发。那么,什么是完美转发?它为什么是C++98/03标准存

python是哪个国家的人开发的语言?

Python是一种解释型、面向对象、动态数据类型的高级程序设计语言。Python是一种跨平台的计算机程序设计语言。是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。最初被设计用于编写自动

JS arguments.callee属性:获取当前所在的函数

JSarguments对象有一个callee属性,它引用当前arguments对象所在的函数。使用该属性可以在函数体内调用函数自身。在匿名函数中,callee属性比较有用。例如,利用它可以设计递归调用

Go语言输出正弦函数(Sin)图像

在Go语言中,正弦函数由math包提供,函数入口为math.Sin,正弦函数的参数为float64,返回值也是float64。在使用正弦函数时,根据实际精度可以进行转换。 Go语言的标准库支持对图片

13 个关于前端的安全提示

无论你是React.js、Angular、Vue.js程序员还是前端页面仔,你的代码都可以成为引诱黑客入侵的大门。作为前端开发人员,我们最关心的是性能、SEO和UI/UX——安全性却经常被忽略。你可能