英文:
rabbit:connection-factory : understanding why a spring namespace element differs from the class it does wrap
问题
我对两种我预期应该足够接近以相同方式运作的用例之间的差异感到困惑。我有一个Spring xml内容,在其中定义了一个消息桥接,使用Spring Integration和spring-amqp来消耗和发布从/到rabbitMq代理。
代理必须通过SSL访问,曾经以非TLS方式访问。我们在端口5671(SSL)上向rabbit发布没有问题。但我们在消耗时遇到了问题...连接工厂未使用SSL套接字而是使用标准套接字...导致服务器拒绝接受传入的帧,因为出现了"{unsupported_record_type,65}"。
尝试了各种更改后,我们最终让它工作了,通过使用相应的“命名空间”元素替换了基本基于bean的声明...即:
“Bean”方法,不起作用:
<bean id="rabbitConnectionFactory" class="org.springframework.amqp.rabbit.connection.CachingConnectionFactory">
<constructor-arg value="clientSSLConnectionFactory" />
<property name="addresses" value="some.fully.qualified.name:5671" />
<property name="username" value="user" />
<property name="password" value="password" />
<property name="cacheMode" value="CONNECTION" />
<property name="connectionCacheSize" value="50" />
<property name="virtualHost" value="/" />
</bean>
变成
“Namespace”方法:起作用:
<rabbit:connection-factory id="rabbitConnectionFactory"
connection-factory="clientSSLConnectionFactory"
addresses="some.fully.qualified.name:5671"
username="user"
password="password"
cache-mode="CONNECTION"
connection-cache-size="50"
virtual-host="/" />
我以为两者是相同的(命名空间基本上是一个包装器),但在ConnectionFactory和CachingConnectionFactory内部设置一些断点后,我意识到它们的构造方式不同。特别是我们看到,使用命名空间方法时,连接在第一种方法中使用基本套接字,而在第二种方法中使用SSLSocket。
我们将连接流追踪到这个非常基本的流程(如下所示):它显示了哪些(主要!)方法/类参与SSL套接字和稍后连接的设置。
步骤 | 命名空间 | BEAN |
---|---|---|
使用SslProtocol | DefaultListablebeanFactory #60 <br>..=> RabbitConnectionFactoryBean #52<br>….=> ConnectionFactory #64 | DefaultListablebeanFactory #60<br>..=> RabbitConnectionFactoryBean #52<br>….=> ConnectionFactory #64 |
使用SslProtocol | DefaultListablebeanFactory #60 <br>..=> RabbitConnectionFactoryBean #108<br>….=> ConnectionFactory #113 | |
newConnection() | SimpleMessageListenerContainer #126 <br>..=> CachingConnectionFactory #78<br>….=> ConnectionFactory #113<br>…...=> socketFactory #89<br>…....=> sslContextFactory #68 | SimpleMessageListenerContainer #97<br>..=> CachingConnectionFactory #94 <br>....=> ConnectionFactory #101 <br>…....=> socketFactory null <br>…....=> sslContextFactory null |
你可以看到,在两种情况下都调用了useSslProtocol方法,但“bean”方法只使用了一次,通过一个不是后续连接所使用的连接工厂...因此不是SSl套接字。
我添加了Eclipse调试器提供的对象的#ID(在这里很有用,可以显示对象是否是同一个实例)
显然,我可能会满意于命名空间解决方案...但不幸的是它不允许设置“connectionListeners”(我们使用的内容)....我也想深入了解Spring和NamespaceHandlers的工作原理。
我可能能够进一步分析,但我也必须承认,我对Spring不够熟悉...虽然我了解大部分机制,但我在使用这些知识来弄清楚实例化的“所有神奇”过程中遇到了一些困难。
例如,我知道<object>
命名空间使用了<Object>
NamespaceHandler(https://docs.spring.io/spring-amqp/api/org/springframework/amqp/rabbit/config/RabbitNamespaceHandler.html),但我无法理解这是如何使命名空间与基本bean不同的。
那么...任何提示都可以帮助我们使用Bean方法(或包括监听器)和/或理解这种差异都是受欢迎的。
英文:
I am puzzled about a difference between 2 uses cases I expected to be close enough to work the same way.
I have a Spring xml content within which I am defining a messaging bridge, using Spring Integration and spring-amqp to consume and publish from/to a rabbitMq broker.
The broker has to be accessed through SSL, where it used to be formerly accessed the non-TLS way.
We have no problem publishing to a rabbit on port 5671 (SSL). But we encountered one consuming... with the ConnectionFactory not using a SSL socket but a standard one... resulting in the server refusing the incoming frame because of an "{unsupported_record_type,65}".
After trying various changes, we ended up having it work, replacing a basic bean-based declaration by the corresponding "namespaced" element... ie :
"Bean" method, not Working:
<bean id="rabbitConnectionFactory" class="org.springframework.amqp.rabbit.connection.CachingConnectionFactory">
<constructor-arg value="clientSSLConnectionFactory" />
<property name="addresses" value="some.fully.qualified.name:5671" />
<property name="username" value="user" />
<property name="password" value="password" />
<property name="cacheMode" value="CONNECTION" />
<property name="connectionCacheSize" value="50" />
<property name="virtualHost" value="/" />
</bean>
becomes
"Namespace" method : Working:
<rabbit:connection-factory id="rabbitConnectionFactory"
connection-factory="clientSSLConnectionFactory"
addresses="some.fully.qualified.name:5671"
username="user"
password="password"
cache-mode="CONNECTION"
connection-cache-size=50"
virtual-host="/" />
I though both were identical (and that the namespace was basicall a wrapper), but after setting up some breakpoints within ConnectionFactory and CachingConnectionFactory (mainly within constructors and new connection methods), I realized that there were not constructed the same way. Especially, we saw that, with the namespace method, the connexion was using a basic socket for the first method while a SSLSocket was used for the second one.
We tracked the connexion flow down to this very basic flow (below) : it shows which (main!) methods / classes participate in the set up of the SSL Socket, and later the connection.
STEP | NAMESPACE | BEAN |
---|---|---|
useSslProtocol | DefaultListablebeanFactory #60 <br>..=> RabbitConnectionFactoryBean #52<br>….=> ConnectionFactory #64 | DefaultListablebeanFactory #60<br>..=> RabbitConnectionFactoryBean #52<br>….=> ConnectionFactory #64 |
useSslProtocol | DefaultListablebeanFactory #60 <br>..=> RabbitConnectionFactoryBean #108<br>….=> ConnectionFactory #113 | |
newConnection() | SimpleMessageListenerContainer #126 <br>..=> CachingConnectionFactory #78<br>….=> ConnectionFactory #113<br>…...=> socketFactory #89<br>…....=> sslContextFactory #68 | SimpleMessageListenerContainer #97<br>..=> CachingConnectionFactory #94 <br>....=> ConnectionFactory #101 <br>…....=> socketFactory null <br>…....=> sslContextFactory null |
You can see that the useSslProtocol method is called in both cases, but the "bean" method does use it only once, through a ConnectionFactory that is not the one used for the later connection... hence the non-SSl socket.
I added the #ID of the objects provided by Eclipse debugger (usefull here to show whether objects are the same instance or not)
Obviously, I could be satisfied with the namespace solution... but unfortunately it does not allow to setup "connectionListeners" (that we use).... and also I would like to understand a little deeper how do Spring and NamespaceHandlers do work.
I might be able to go further in my analysis, but I must also confess I am not easy enough with Spring... although I undersand most mecanisms, I experience some difficulties using this knownledge to figure out what is really going on on the "all magical" process of instanciation.
EG, I know the <object> namespace does use a <Object>NamespaceHandler ( https://docs.spring.io/spring-amqp/api/org/springframework/amqp/rabbit/config/RabbitNamespaceHandler.html), but I wasn't able to undersand how does this make the namespace differ from the basic bean.
So... any hint to help us achieve using the Bean method (or include the listners) and/or understand the difference is welcome here.
答案1
得分: 2
NamespaceHandler
只提供了解析特定标签所需的信息。它将 connection-factory
关联到 ConnectionFactoryParser
,后者知道如何处理该特定元素。它会将 bean 或 BeanDefinition
注册到应用程序上下文中。
但是,在您的 bean
和命名空间之间存在一个相当大的区别。在您的 bean
定义中,您使用了一个带有 value
的 constructor-arg
。这将导致调用接受一个 String
(即主机名,稍后您会覆盖)的构造函数。您的本意是使用 ref
来引用上下文中的另一个 bean。
所以基本上,您的 bean
定义对 clientSSLConnectionFactory
bean 没有任何作用,而命名空间中的定义则有作用。
英文:
The NamespaceHandler
only provides the needed information on with what class to parse certain tags. It will link connection-factory
to a ConnectionFactoryParser
which knows what to do with that specific element. It will register beans or BeanDefinition
into the application context.
However there is one, quite big, difference between your bean
and the namespace. In your bean
definition you are using a constructor-arg
with a value
. This will lead to invoking the constructor that takes a String
(which is the hostname, which you later override). What you intended was to use a ref
to reference another bean in the context.
So basically your bean
definition doesn't do anything with the clientSSLConnectionFactory
bean, where as the one with the namespace does.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论