解决ActiveMQ的“Invalid broker URI”异常的历程

简介:

000

最近碰到一个问题,把解决的过程记录下来。

故障原因

同事的应用上线,Tomcat无法正常启动。抛出这样的异常:

org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessExceptions (1) are:|PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property 'brokerURL' threw exception; nested exception is java.lang.IllegalArgumentException: Invalid broker URI: nio://10.0.0.0:91616 
        at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:121)
        at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:75)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1504)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1216)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:538)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
        at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:229)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)

据同事说,在线下环境,可以正常运行,在线上环境就出错了,非常的诡异。

程序有一个env.properties的配置文件:

broker.producer  = failover://(nio://10.0.0.3:91615?connectionTimeout=3000,nio://10.0.0.4:91615?connectionTimeout=3000)

线上和线下加载的是不同的env.properties。

在Spring xml配置文件里有这样的配置:

	<!-- ActiveMQ 连接工厂 -->
	<amq:connectionFactory id="jmsConnectionFactory" brokerURL="${broker.producer}" />

Spring在运行时,会自动替换${}表达式里的值。

首先检查网络的连通性:

在线上机器上,执行

telnet 10.0.0.3 91616

可以正常连接,再到ActiveMQ的Web Console上查看Connection,发现的确没有这个IP的连接。

检查配置是否正常:

线上的配置的brokerURI是这个:

failover://(nio://10.0.0.3:91616?connectionTimeout=3000,nio://10.0.0.4:91616?connectionTimeout=3000)

直接先改为最简单的,在vim下yy复制了一行,删除多余的,剩下:

nio://10.0.0.3:91616

发现还是报异常。

利用ssh做端口转发,本地测试

在本地跑了个简单程序,用XShell的端口转发,把本地请求转发到线上机器上,发现可以正常发送消息。于是让同事回去检查代码里的其它问题了。

把测试程序放到线上机器运行

但是同事没有找到错误,于是把刚才的简单的程序打成一个fat jar包,放到线上机器上去跑,发现可以正常发送ActiveMQ消息。

程序打包用的是maven的one jar 插件,参考:

http://www.mkyong.com/maven/maven-create-a-fat-jar-file-one-jar-example/

把测试程序集成到同事的代码里,运行

把测试程序放到同事的War包的代码里,放到线上机器,发现可以正常发送消息。

但是同事配置的ActiveMQ还是不能发送消息,还是报“Invalid broker URI”异常。

增加变量,反复测试,对比配置

没办法了,把同事的环境变量${broker.producer},设置到测试代码里,发现测试代码抛异常了。

于是确认是${broker.producer} 这个变量有问题。

但是env.properties文件里的配置看起来是对的。于是怀疑是配置文件格式有问题。

备份旧文件,建了个新配置文件,配置上

broker.producer =nio://10.0.0.3:91615

发现,居然正常了。

于是对比两个配置文件,发现旧的配置文件上,最后多了一个空格。。就是91615后面多了一个空格。

蛋疼无比,ActiveMQ居然不能识别处理配置值后面多出来的一个空格。而且Spring抛出来的异常里也没有这个信息。

搜索关键字"Invalid broker URI",查看ActiveMQ代码,找到原始异常

开始调试时,找不到原始的异常信息在哪里,Spring的函数调用层次太多了。于是采用代码搜索。

在 https://searchcode.com 搜索"Invalid broker URI",终于找到原始的异常信息是下面的代码抛出来的:

URI org.apache.activemq.ActiveMQConnectionFactory.createURI(String brokerURL)
    private static URI createURI(String brokerURL) {
        try {
            return new URI(brokerURL);
        } catch (URISyntaxException e) {
            throw (IllegalArgumentException)new IllegalArgumentException("Invalid broker URI: " + brokerURL).initCause(e);
        }
    }
很奇怪的是,IllegalArgumentException异常里是正确地把URISyntaxException设置到cause里了,后面的Spring却没有把这个信息给打印出来。。

Spring打印异常的工作原理

为什么Spring能把前面的异常信息都打印出来,而原始的异常信息却不能打印出来?比如某个Spring异常信息是这样的:

Caused by: org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessExceptions (1) are:

Srping会用Caused by,nested exception is,这样的字符把所有的异常都串起来。到底这里面是怎么工作的?

再次搜索"nested exception is"

查找到Spring相关的代码。

原来所有的Spring异常类都继承自NestedRuntimeException,而NestedRuntimeException重写了getMessage()函数,在getMessage()函数里,会把异常的信息全都串起来。

而IllegalArgumentException继承自Throwable类,Throwable类的getMessage()函数只是简单打印了message,并没有把cause也输出。

org.springframework.core.NestedRuntimeException

	/**
	 * Return the detail message, including the message from the nested exception
	 * if there is one.
	 */
	@Override
	public String getMessage() {
		return NestedExceptionUtils.buildMessage(super.getMessage(), getCause());
	}


org.springframework.core.NestedExceptionUtils
	/**
	 * Build a message for the given base message and root cause.
	 * @param message the base message
	 * @param cause the root cause
	 * @return the full exception message
	 */
	public static String buildMessage(String message, Throwable cause) {
		if (cause != null) {
			StringBuilder sb = new StringBuilder();
			if (message != null) {
				sb.append(message).append("; ");
			}
			sb.append("nested exception is ").append(cause);
			return sb.toString();
		}
		else {
			return message;
		}
	}

其它的一些东东:

NestedExceptionUtils这个类是abstract,这样可以防止使用者得到实例,这样使用者不能用错。这个也是一个常见的util类的技巧了。

public abstract class NestedExceptionUtils {

总结:

这个代码搜索网站比较好用:https://searchcode.com

很多流行网站的数据都有,比github上要全。


后来在网上搜索了下“Invalid broker URI”,有10万多条结果。。估计有不少就是因为一些空格而造成的。。

ActiveMQ的开发者只需要加上一点点的trim()的判断处理代码,就可以减少很多人的痛苦了。

所以防御性编程还是有必要的,有的时候并不真的是使用者不会用,而是错误来自想像不到的地方。

URI org.apache.activemq.ActiveMQConnectionFactory.createURI(String brokerURL)
    private static URI createURI(String brokerURL) {
        try {
            if(brokerURL != null)
                brokerURL = brokerURL.trim();
            return new URI(brokerURL);
        } catch (URISyntaxException e) {
            throw (IllegalArgumentException)new IllegalArgumentException("Invalid broker URI: " + brokerURL).initCause(e);
        }
    }



目录
相关文章
|
4月前
|
芯片
天龙八部脚本,按键精灵脚本,光遇脚本【最新源码插件分享】
实现自动找怪、攻击、补血补蓝等核心挂机功能 采用颜色识别技术定位怪物位置
|
数据采集 存储 SQL
数据中台全景架构及模块解析!一文入门中台架构师!
数据中台全景架构及模块解析!包括数据采集、数据存储、数据开发处理、数据资产管理、数据质量和安全、数据服务。一文入门中台架构师!
|
JSON Java API
Java快递单号查询接口怎么接入物流API
Java怎么写物流接口,怎么接入物流接口,如何根据单号查询物流跟踪的详细信息 需求 根据用户输入的订单号,我们的后台识别订单号并根据快递鸟查询快递Api接口,实现自动查询的功能 demo实例 本人自己运行过的Demo —> 点我下载 应用场景(下图) 实现步骤 4.
10797 1
|
存储 Kubernetes 数据安全/隐私保护
k8s学习--Secret详细解释与应用
Secret 支持四种类型: - **Opaque Secrets**:存储任意类型机密数据,需自行加密。 - **Service Account Token Secrets**:自动管理 API 访问令牌。 - **Docker Registry Secrets**:存储 Docker 私有仓库认证信息。 - **TLS Secrets**:存储 TLS 证书和私钥,用于加密通信。
1159 0
|
移动开发 开发框架 JavaScript
Vue3 Vite electron 开发桌面程序
Vue3 Vite electron 开发桌面程序
606 0
一文讲明 网络调试助手的基本使用 NetAssist
文章介绍了网络调试助手NetAssist的基本使用方法,包括创建客户端和服务端,模拟网络连接和数据发送,并通过Java代码示例展示了客户端与服务端之间的通信过程。
一文讲明 网络调试助手的基本使用 NetAssist
JSTL组件的下载链接地址
这篇文章提供了JSTL(JavaServer Pages Standard Tag Library)组件的下载链接地址,并指导如何配置JSTL到web应用程序的WEB-INF/lib目录下。
|
SQL 分布式计算 资源调度
Apache Doris 整合 FLINK CDC + Iceberg 构建实时湖仓一体的联邦查询
这篇教程将展示如何使用 Flink CDC + Iceberg + Doris 构建实时湖仓一体的联邦查询分析,Doris 1.1版本提供了Iceberg的支持,本文主要展示Doris和Iceberg怎么使用,同时本教程整个环境是都基于伪分布式环境搭建,大家按照步骤可以一步步完成。完整体验整个搭建操作的过程。
3122 1
|
开发者 图形学 C#
深度解密:Unity游戏开发中的动画艺术——Mecanim状态机如何让游戏角色栩栩如生:从基础设置到高级状态切换的全面指南,助你打造流畅自然的游戏动画体验
【8月更文挑战第31天】Unity动画系统是游戏开发的关键部分,尤其适用于复杂角色动画。本文通过具体案例讲解Mecanim动画状态机的使用方法及原理。我们创建一个游戏角色并设计行走、奔跑和攻击动画,详细介绍动画状态机设置及脚本控制。首先导入动画资源并添加Animator组件,然后创建Animator Controller并设置状态间的转换条件。通过编写C#脚本(如PlayerMovement)控制动画状态切换,实现基于玩家输入的动画过渡。此方法不仅适用于游戏角色,还可用于任何需动态动画响应的对象,增强游戏的真实感与互动性。
620 0
|
前端开发 Java Spring
一文带你深入理解SpringBean生命周期之InitializingBean详解
一文带你深入理解SpringBean生命周期之InitializingBean详解
1258 0
一文带你深入理解SpringBean生命周期之InitializingBean详解

热门文章

最新文章